Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
* vim: set ts=8 sts=2 et sw=2 tw=80:
3
* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "js/Proxy.h"
8
#include "proxy/DeadObjectProxy.h"
9
#include "vm/ProxyObject.h"
10
#include "vm/WrapperObject.h"
11
12
#include "vm/JSContext-inl.h"
13
#include "vm/JSObject-inl.h"
14
15
using namespace js;
16
17
using JS::IsArrayAnswer;
18
19
bool BaseProxyHandler::enter(JSContext* cx, HandleObject wrapper, HandleId id,
20
Action act, bool mayThrow, bool* bp) const {
21
*bp = true;
22
return true;
23
}
24
25
bool BaseProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id,
26
bool* bp) const {
27
assertEnteredPolicy(cx, proxy, id, GET);
28
29
// This method is not covered by any spec, but we follow ES 2016
30
// (February 11, 2016) 9.1.7.1 fairly closely.
31
32
// Step 2. (Step 1 is a superfluous assertion.)
33
// Non-standard: Use our faster hasOwn trap.
34
if (!hasOwn(cx, proxy, id, bp)) {
35
return false;
36
}
37
38
// Step 3.
39
if (*bp) {
40
return true;
41
}
42
43
// The spec calls this variable "parent", but that word has weird
44
// connotations in SpiderMonkey, so let's go with "proto".
45
// Step 4.
46
RootedObject proto(cx);
47
if (!GetPrototype(cx, proxy, &proto)) {
48
return false;
49
}
50
51
// Step 5.,5.a.
52
if (proto) {
53
return HasProperty(cx, proto, id, bp);
54
}
55
56
// Step 6.
57
*bp = false;
58
return true;
59
}
60
61
bool BaseProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id,
62
bool* bp) const {
63
assertEnteredPolicy(cx, proxy, id, GET);
64
Rooted<PropertyDescriptor> desc(cx);
65
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) {
66
return false;
67
}
68
*bp = !!desc.object();
69
return true;
70
}
71
72
bool BaseProxyHandler::get(JSContext* cx, HandleObject proxy,
73
HandleValue receiver, HandleId id,
74
MutableHandleValue vp) const {
75
assertEnteredPolicy(cx, proxy, id, GET);
76
77
// This method is not covered by any spec, but we follow ES 2016
78
// (January 21, 2016) 9.1.8 fairly closely.
79
80
// Step 2. (Step 1 is a superfluous assertion.)
81
Rooted<PropertyDescriptor> desc(cx);
82
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) {
83
return false;
84
}
85
desc.assertCompleteIfFound();
86
87
// Step 3.
88
if (!desc.object()) {
89
// The spec calls this variable "parent", but that word has weird
90
// connotations in SpiderMonkey, so let's go with "proto".
91
// Step 3.a.
92
RootedObject proto(cx);
93
if (!GetPrototype(cx, proxy, &proto)) {
94
return false;
95
}
96
97
// Step 3.b.
98
if (!proto) {
99
vp.setUndefined();
100
return true;
101
}
102
103
// Step 3.c.
104
return GetProperty(cx, proto, receiver, id, vp);
105
}
106
107
// Step 4.
108
if (desc.isDataDescriptor()) {
109
vp.set(desc.value());
110
return true;
111
}
112
113
// Step 5.
114
MOZ_ASSERT(desc.isAccessorDescriptor());
115
RootedObject getter(cx, desc.getterObject());
116
117
// Step 6.
118
if (!getter) {
119
vp.setUndefined();
120
return true;
121
}
122
123
// Step 7.
124
RootedValue getterFunc(cx, ObjectValue(*getter));
125
return CallGetter(cx, receiver, getterFunc, vp);
126
}
127
128
bool BaseProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id,
129
HandleValue v, HandleValue receiver,
130
ObjectOpResult& result) const {
131
assertEnteredPolicy(cx, proxy, id, SET);
132
133
// This method is not covered by any spec, but we follow ES6 draft rev 28
134
// (2014 Oct 14) 9.1.9 fairly closely, adapting it slightly for
135
// SpiderMonkey's particular foibles.
136
137
// Steps 2-3. (Step 1 is a superfluous assertion.)
138
Rooted<PropertyDescriptor> ownDesc(cx);
139
if (!getOwnPropertyDescriptor(cx, proxy, id, &ownDesc)) {
140
return false;
141
}
142
ownDesc.assertCompleteIfFound();
143
144
// The rest is factored out into a separate function with a weird name.
145
// This algorithm continues just below.
146
return SetPropertyIgnoringNamedGetter(cx, proxy, id, v, receiver, ownDesc,
147
result);
148
}
149
150
bool js::SetPropertyIgnoringNamedGetter(JSContext* cx, HandleObject obj,
151
HandleId id, HandleValue v,
152
HandleValue receiver,
153
Handle<PropertyDescriptor> ownDesc_,
154
ObjectOpResult& result) {
155
Rooted<PropertyDescriptor> ownDesc(cx, ownDesc_);
156
157
// Step 4.
158
if (!ownDesc.object()) {
159
// The spec calls this variable "parent", but that word has weird
160
// connotations in SpiderMonkey, so let's go with "proto".
161
RootedObject proto(cx);
162
if (!GetPrototype(cx, obj, &proto)) {
163
return false;
164
}
165
if (proto) {
166
return SetProperty(cx, proto, id, v, receiver, result);
167
}
168
169
// Step 4.d.
170
ownDesc.setDataDescriptor(UndefinedHandleValue, JSPROP_ENUMERATE);
171
}
172
173
// Step 5.
174
if (ownDesc.isDataDescriptor()) {
175
// Steps 5.a-b.
176
if (!ownDesc.writable()) {
177
return result.fail(JSMSG_READ_ONLY);
178
}
179
if (!receiver.isObject()) {
180
return result.fail(JSMSG_SET_NON_OBJECT_RECEIVER);
181
}
182
RootedObject receiverObj(cx, &receiver.toObject());
183
184
// Nonstandard SpiderMonkey special case: setter ops.
185
if (SetterOp setter = ownDesc.setter()) {
186
return CallJSSetterOp(cx, setter, receiverObj, id, v, result);
187
}
188
189
// Steps 5.c-d.
190
Rooted<PropertyDescriptor> existingDescriptor(cx);
191
if (!GetOwnPropertyDescriptor(cx, receiverObj, id, &existingDescriptor)) {
192
return false;
193
}
194
195
// Step 5.e.
196
if (existingDescriptor.object()) {
197
// Step 5.e.i.
198
if (existingDescriptor.isAccessorDescriptor()) {
199
return result.fail(JSMSG_OVERWRITING_ACCESSOR);
200
}
201
202
// Step 5.e.ii.
203
if (!existingDescriptor.writable()) {
204
return result.fail(JSMSG_READ_ONLY);
205
}
206
}
207
208
// Steps 5.e.iii-iv. and 5.f.i.
209
unsigned attrs = existingDescriptor.object()
210
? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY |
211
JSPROP_IGNORE_PERMANENT
212
: JSPROP_ENUMERATE;
213
214
return DefineDataProperty(cx, receiverObj, id, v, attrs, result);
215
}
216
217
// Step 6.
218
MOZ_ASSERT(ownDesc.isAccessorDescriptor());
219
RootedObject setter(cx);
220
if (ownDesc.hasSetterObject()) {
221
setter = ownDesc.setterObject();
222
}
223
if (!setter) {
224
return result.fail(JSMSG_GETTER_ONLY);
225
}
226
RootedValue setterValue(cx, ObjectValue(*setter));
227
if (!CallSetter(cx, receiver, setterValue, v)) {
228
return false;
229
}
230
return result.succeed();
231
}
232
233
bool BaseProxyHandler::getOwnEnumerablePropertyKeys(
234
JSContext* cx, HandleObject proxy, MutableHandleIdVector props) const {
235
assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
236
MOZ_ASSERT(props.length() == 0);
237
238
if (!ownPropertyKeys(cx, proxy, props)) {
239
return false;
240
}
241
242
/* Select only the enumerable properties through in-place iteration. */
243
RootedId id(cx);
244
size_t i = 0;
245
for (size_t j = 0, len = props.length(); j < len; j++) {
246
MOZ_ASSERT(i <= j);
247
id = props[j];
248
if (JSID_IS_SYMBOL(id)) {
249
continue;
250
}
251
252
AutoWaivePolicy policy(cx, proxy, id, BaseProxyHandler::GET);
253
Rooted<PropertyDescriptor> desc(cx);
254
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) {
255
return false;
256
}
257
desc.assertCompleteIfFound();
258
259
if (desc.object() && desc.enumerable()) {
260
props[i++].set(id);
261
}
262
}
263
264
MOZ_ASSERT(i <= props.length());
265
if (!props.resize(i)) {
266
return false;
267
}
268
269
return true;
270
}
271
272
bool BaseProxyHandler::enumerate(JSContext* cx, HandleObject proxy,
273
MutableHandleIdVector props) const {
274
assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
275
276
// GetPropertyKeys will invoke getOwnEnumerablePropertyKeys along the proto
277
// chain for us.
278
MOZ_ASSERT(props.empty());
279
return GetPropertyKeys(cx, proxy, 0, props);
280
}
281
282
bool BaseProxyHandler::call(JSContext* cx, HandleObject proxy,
283
const CallArgs& args) const {
284
MOZ_CRASH("callable proxies should implement call trap");
285
}
286
287
bool BaseProxyHandler::construct(JSContext* cx, HandleObject proxy,
288
const CallArgs& args) const {
289
MOZ_CRASH("callable proxies should implement construct trap");
290
}
291
292
const char* BaseProxyHandler::className(JSContext* cx,
293
HandleObject proxy) const {
294
return proxy->isCallable() ? "Function" : "Object";
295
}
296
297
JSString* BaseProxyHandler::fun_toString(JSContext* cx, HandleObject proxy,
298
bool isToSource) const {
299
if (proxy->isCallable()) {
300
return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}");
301
}
302
303
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
304
JSMSG_INCOMPATIBLE_PROTO, js_Function_str,
305
js_toString_str, "object");
306
return nullptr;
307
}
308
309
RegExpShared* BaseProxyHandler::regexp_toShared(JSContext* cx,
310
HandleObject proxy) const {
311
MOZ_CRASH("This should have been a wrapped regexp");
312
}
313
314
bool BaseProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy,
315
MutableHandleValue vp) const {
316
vp.setUndefined();
317
return true;
318
}
319
320
bool BaseProxyHandler::nativeCall(JSContext* cx, IsAcceptableThis test,
321
NativeImpl impl, const CallArgs& args) const {
322
ReportIncompatible(cx, args);
323
return false;
324
}
325
326
bool BaseProxyHandler::hasInstance(JSContext* cx, HandleObject proxy,
327
MutableHandleValue v, bool* bp) const {
328
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
329
RootedValue val(cx, ObjectValue(*proxy.get()));
330
ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, JSDVG_SEARCH_STACK, val,
331
nullptr);
332
return false;
333
}
334
335
bool BaseProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy,
336
ESClass* cls) const {
337
*cls = ESClass::Other;
338
return true;
339
}
340
341
bool BaseProxyHandler::isArray(JSContext* cx, HandleObject proxy,
342
IsArrayAnswer* answer) const {
343
*answer = IsArrayAnswer::NotArray;
344
return true;
345
}
346
347
void BaseProxyHandler::trace(JSTracer* trc, JSObject* proxy) const {}
348
349
void BaseProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const {}
350
351
size_t BaseProxyHandler::objectMoved(JSObject* proxy, JSObject* old) const {
352
return 0;
353
}
354
355
bool BaseProxyHandler::getPrototype(JSContext* cx, HandleObject proxy,
356
MutableHandleObject protop) const {
357
MOZ_CRASH("must override getPrototype with dynamic prototype");
358
}
359
360
bool BaseProxyHandler::setPrototype(JSContext* cx, HandleObject proxy,
361
HandleObject proto,
362
ObjectOpResult& result) const {
363
// Disallow sets of protos on proxies with dynamic prototypes but no hook.
364
// This keeps us away from the footgun of having the first proto set opt
365
// you out of having dynamic protos altogether.
366
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
367
JSMSG_CANT_SET_PROTO_OF, "incompatible Proxy");
368
return false;
369
}
370
371
bool BaseProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy,
372
bool* succeeded) const {
373
*succeeded = false;
374
return true;
375
}
376
377
bool BaseProxyHandler::getElements(JSContext* cx, HandleObject proxy,
378
uint32_t begin, uint32_t end,
379
ElementAdder* adder) const {
380
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
381
382
return js::GetElementsWithAdder(cx, proxy, proxy, begin, end, adder);
383
}
384
385
bool BaseProxyHandler::isCallable(JSObject* obj) const { return false; }
386
387
bool BaseProxyHandler::isConstructor(JSObject* obj) const { return false; }
388
389
JS_FRIEND_API void js::NukeNonCCWProxy(JSContext* cx, HandleObject proxy) {
390
MOZ_ASSERT(proxy->is<ProxyObject>());
391
MOZ_ASSERT(!proxy->is<CrossCompartmentWrapperObject>());
392
393
// (NotifyGCNukeWrapper() only needs to be called on CCWs.)
394
395
// The proxy is about to be replaced, so we need to do any necessary
396
// cleanup first.
397
proxy->as<ProxyObject>().handler()->finalize(cx->defaultFreeOp(), proxy);
398
399
proxy->as<ProxyObject>().nuke();
400
401
MOZ_ASSERT(IsDeadProxyObject(proxy));
402
}
403
404
JS_FRIEND_API void js::NukeRemovedCrossCompartmentWrapper(JSContext* cx,
405
JSObject* wrapper) {
406
MOZ_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
407
408
NotifyGCNukeWrapper(wrapper);
409
410
// We don't need to call finalize here because the CCW finalizer doesn't do
411
// anything. Skipping finalize means that |wrapper| doesn't need to be rooted
412
// to pass the hazard analysis, which is needed because this method is called
413
// from some tricky places inside transplanting where rooting can be
414
// difficult.
415
416
wrapper->as<ProxyObject>().nuke();
417
418
MOZ_ASSERT(IsDeadProxyObject(wrapper));
419
}