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