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