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 :
9 : #include "mozilla/Attributes.h"
10 :
11 : #include <string.h>
12 :
13 : #include "jsapi.h"
14 : #include "jscntxt.h"
15 : #include "jsfun.h"
16 : #include "jsgc.h"
17 : #include "jswrapper.h"
18 :
19 : #include "gc/Marking.h"
20 : #include "proxy/DeadObjectProxy.h"
21 : #include "proxy/ScriptedProxyHandler.h"
22 : #include "vm/WrapperObject.h"
23 :
24 : #include "jsatominlines.h"
25 : #include "jsobjinlines.h"
26 :
27 : #include "vm/NativeObject-inl.h"
28 :
29 : using namespace js;
30 : using namespace js::gc;
31 :
32 : void
33 0 : js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext* cx, jsid id)
34 : {
35 0 : if (JS_IsExceptionPending(cx))
36 0 : return;
37 :
38 0 : if (JSID_IS_VOID(id)) {
39 0 : ReportAccessDenied(cx);
40 : } else {
41 0 : RootedValue idVal(cx, IdToValue(id));
42 0 : JSString* str = ValueToSource(cx, idVal);
43 0 : if (!str) {
44 0 : return;
45 : }
46 0 : AutoStableStringChars chars(cx);
47 0 : const char16_t* prop = nullptr;
48 0 : if (str->ensureFlat(cx) && chars.initTwoByte(cx, str))
49 0 : prop = chars.twoByteChars();
50 :
51 : JS_ReportErrorNumberUC(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_ACCESS_DENIED,
52 0 : prop);
53 : }
54 : }
55 :
56 : #ifdef DEBUG
57 : void
58 19339 : js::AutoEnterPolicy::recordEnter(JSContext* cx, HandleObject proxy, HandleId id, Action act)
59 : {
60 19339 : if (allowed()) {
61 19339 : context = cx;
62 19339 : enteredProxy.emplace(proxy);
63 19339 : enteredId.emplace(id);
64 19339 : enteredAction = act;
65 19339 : prev = cx->enteredPolicy;
66 19339 : cx->enteredPolicy = this;
67 : }
68 19339 : }
69 :
70 : void
71 19339 : js::AutoEnterPolicy::recordLeave()
72 : {
73 19339 : if (enteredProxy) {
74 19339 : MOZ_ASSERT(context->enteredPolicy == this);
75 19339 : context->enteredPolicy = prev;
76 : }
77 19339 : }
78 :
79 : JS_FRIEND_API(void)
80 19896 : js::assertEnteredPolicy(JSContext* cx, JSObject* proxy, jsid id,
81 : BaseProxyHandler::Action act)
82 : {
83 19896 : MOZ_ASSERT(proxy->is<ProxyObject>());
84 19896 : MOZ_ASSERT(cx->enteredPolicy);
85 19896 : MOZ_ASSERT(cx->enteredPolicy->enteredProxy->get() == proxy);
86 19896 : MOZ_ASSERT(cx->enteredPolicy->enteredId->get() == id);
87 19896 : MOZ_ASSERT(cx->enteredPolicy->enteredAction & act);
88 19896 : }
89 : #endif
90 :
91 : bool
92 0 : Proxy::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
93 : MutableHandle<PropertyDescriptor> desc)
94 : {
95 0 : if (!CheckRecursionLimit(cx))
96 0 : return false;
97 :
98 0 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
99 0 : desc.object().set(nullptr); // default result if we refuse to perform this action
100 0 : AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
101 0 : if (!policy.allowed())
102 0 : return policy.returnValue();
103 :
104 : // Special case. See the comment on BaseProxyHandler::mHasPrototype.
105 0 : if (handler->hasPrototype())
106 0 : return handler->BaseProxyHandler::getPropertyDescriptor(cx, proxy, id, desc);
107 :
108 0 : return handler->getPropertyDescriptor(cx, proxy, id, desc);
109 : }
110 :
111 : bool
112 153 : Proxy::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
113 : MutableHandle<PropertyDescriptor> desc)
114 : {
115 153 : if (!CheckRecursionLimit(cx))
116 0 : return false;
117 153 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
118 153 : desc.object().set(nullptr); // default result if we refuse to perform this action
119 306 : AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
120 153 : if (!policy.allowed())
121 0 : return policy.returnValue();
122 153 : return handler->getOwnPropertyDescriptor(cx, proxy, id, desc);
123 : }
124 :
125 : bool
126 2029 : Proxy::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
127 : Handle<PropertyDescriptor> desc, ObjectOpResult& result)
128 : {
129 2029 : if (!CheckRecursionLimit(cx))
130 0 : return false;
131 2029 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
132 4058 : AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
133 2029 : if (!policy.allowed()) {
134 0 : if (!policy.returnValue())
135 0 : return false;
136 0 : return result.succeed();
137 : }
138 2029 : return proxy->as<ProxyObject>().handler()->defineProperty(cx, proxy, id, desc, result);
139 : }
140 :
141 : bool
142 10 : Proxy::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
143 : {
144 10 : if (!CheckRecursionLimit(cx))
145 0 : return false;
146 10 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
147 20 : AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
148 10 : if (!policy.allowed())
149 0 : return policy.returnValue();
150 10 : return proxy->as<ProxyObject>().handler()->ownPropertyKeys(cx, proxy, props);
151 : }
152 :
153 : bool
154 414 : Proxy::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result)
155 : {
156 414 : if (!CheckRecursionLimit(cx))
157 0 : return false;
158 414 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
159 828 : AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
160 414 : if (!policy.allowed()) {
161 0 : bool ok = policy.returnValue();
162 0 : if (ok)
163 0 : result.succeed();
164 0 : return ok;
165 : }
166 414 : return proxy->as<ProxyObject>().handler()->delete_(cx, proxy, id, result);
167 : }
168 :
169 : JS_FRIEND_API(bool)
170 0 : js::AppendUnique(JSContext* cx, AutoIdVector& base, AutoIdVector& others)
171 : {
172 0 : AutoIdVector uniqueOthers(cx);
173 0 : if (!uniqueOthers.reserve(others.length()))
174 0 : return false;
175 0 : for (size_t i = 0; i < others.length(); ++i) {
176 0 : bool unique = true;
177 0 : for (size_t j = 0; j < base.length(); ++j) {
178 0 : if (others[i].get() == base[j]) {
179 0 : unique = false;
180 0 : break;
181 : }
182 : }
183 0 : if (unique) {
184 0 : if (!uniqueOthers.append(others[i]))
185 0 : return false;
186 : }
187 : }
188 0 : return base.appendAll(uniqueOthers);
189 : }
190 :
191 : /* static */ bool
192 440 : Proxy::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject proto)
193 : {
194 440 : MOZ_ASSERT(proxy->hasDynamicPrototype());
195 440 : if (!CheckRecursionLimit(cx))
196 0 : return false;
197 440 : return proxy->as<ProxyObject>().handler()->getPrototype(cx, proxy, proto);
198 : }
199 :
200 : /* static */ bool
201 0 : Proxy::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, ObjectOpResult& result)
202 : {
203 0 : MOZ_ASSERT(proxy->hasDynamicPrototype());
204 0 : if (!CheckRecursionLimit(cx))
205 0 : return false;
206 0 : return proxy->as<ProxyObject>().handler()->setPrototype(cx, proxy, proto, result);
207 : }
208 :
209 : /* static */ bool
210 5 : Proxy::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
211 : MutableHandleObject proto)
212 : {
213 5 : if (!CheckRecursionLimit(cx))
214 0 : return false;
215 5 : return proxy->as<ProxyObject>().handler()->getPrototypeIfOrdinary(cx, proxy, isOrdinary,
216 5 : proto);
217 : }
218 :
219 : /* static */ bool
220 0 : Proxy::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded)
221 : {
222 0 : if (!CheckRecursionLimit(cx))
223 0 : return false;
224 0 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
225 0 : return handler->setImmutablePrototype(cx, proxy, succeeded);
226 : }
227 :
228 : /* static */ bool
229 3 : Proxy::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result)
230 : {
231 3 : if (!CheckRecursionLimit(cx))
232 0 : return false;
233 3 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
234 3 : return handler->preventExtensions(cx, proxy, result);
235 : }
236 :
237 : /* static */ bool
238 0 : Proxy::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible)
239 : {
240 0 : if (!CheckRecursionLimit(cx))
241 0 : return false;
242 0 : return proxy->as<ProxyObject>().handler()->isExtensible(cx, proxy, extensible);
243 : }
244 :
245 : bool
246 854 : Proxy::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
247 : {
248 854 : if (!CheckRecursionLimit(cx))
249 0 : return false;
250 854 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
251 854 : *bp = false; // default result if we refuse to perform this action
252 1708 : AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
253 854 : if (!policy.allowed())
254 0 : return policy.returnValue();
255 :
256 854 : if (handler->hasPrototype()) {
257 496 : if (!handler->hasOwn(cx, proxy, id, bp))
258 0 : return false;
259 496 : if (*bp)
260 53 : return true;
261 :
262 886 : RootedObject proto(cx);
263 443 : if (!GetPrototype(cx, proxy, &proto))
264 0 : return false;
265 443 : if (!proto)
266 41 : return true;
267 :
268 402 : return HasProperty(cx, proto, id, bp);
269 : }
270 :
271 358 : return handler->has(cx, proxy, id, bp);
272 : }
273 :
274 : bool
275 52 : Proxy::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
276 : {
277 52 : if (!CheckRecursionLimit(cx))
278 0 : return false;
279 52 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
280 52 : *bp = false; // default result if we refuse to perform this action
281 104 : AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
282 52 : if (!policy.allowed())
283 0 : return policy.returnValue();
284 52 : return handler->hasOwn(cx, proxy, id, bp);
285 : }
286 :
287 : bool
288 0 : js::ProxyHasOwn(JSContext* cx, HandleObject proxy, HandleValue idVal, MutableHandleValue result)
289 : {
290 0 : RootedId id(cx);
291 0 : if (!ValueToId<CanGC>(cx, idVal, &id))
292 0 : return false;
293 :
294 : bool hasOwn;
295 0 : if (!Proxy::hasOwn(cx, proxy, id, &hasOwn))
296 0 : return false;
297 :
298 0 : result.setBoolean(hasOwn);
299 0 : return true;
300 : }
301 :
302 : static MOZ_ALWAYS_INLINE Value
303 10606 : ValueToWindowProxyIfWindow(const Value& v)
304 : {
305 10606 : if (v.isObject())
306 10606 : return ObjectValue(*ToWindowProxyIfWindow(&v.toObject()));
307 0 : return v;
308 : }
309 :
310 : MOZ_ALWAYS_INLINE bool
311 10202 : Proxy::get(JSContext* cx, HandleObject proxy, HandleValue receiver_, HandleId id,
312 : MutableHandleValue vp)
313 : {
314 10202 : if (!CheckRecursionLimit(cx))
315 0 : return false;
316 10202 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
317 10202 : vp.setUndefined(); // default result if we refuse to perform this action
318 20404 : AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
319 10202 : if (!policy.allowed())
320 0 : return policy.returnValue();
321 :
322 : // Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
323 : // shouldn't have to know about the Window/WindowProxy distinction.
324 20404 : RootedValue receiver(cx, ValueToWindowProxyIfWindow(receiver_));
325 :
326 10202 : if (handler->hasPrototype()) {
327 : bool own;
328 449 : if (!handler->hasOwn(cx, proxy, id, &own))
329 237 : return false;
330 449 : if (!own) {
331 474 : RootedObject proto(cx);
332 237 : if (!GetPrototype(cx, proxy, &proto))
333 0 : return false;
334 237 : if (!proto)
335 2 : return true;
336 235 : return GetProperty(cx, proto, receiver, id, vp);
337 : }
338 : }
339 :
340 9965 : return handler->get(cx, proxy, receiver, id, vp);
341 : }
342 :
343 : bool
344 1099 : js::ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
345 : {
346 2198 : RootedValue receiver(cx, ObjectValue(*proxy));
347 2198 : return Proxy::get(cx, proxy, receiver, id, vp);
348 : }
349 :
350 : bool
351 798 : js::ProxyGetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal,
352 : MutableHandleValue vp)
353 : {
354 1596 : RootedId id(cx);
355 798 : if (!ValueToId<CanGC>(cx, idVal, &id))
356 0 : return false;
357 :
358 1596 : RootedValue receiver(cx, ObjectValue(*proxy));
359 798 : return Proxy::get(cx, proxy, receiver, id, vp);
360 : }
361 :
362 : bool
363 404 : Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver_,
364 : ObjectOpResult& result)
365 : {
366 404 : if (!CheckRecursionLimit(cx))
367 0 : return false;
368 404 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
369 808 : AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
370 404 : if (!policy.allowed()) {
371 0 : if (!policy.returnValue())
372 0 : return false;
373 0 : return result.succeed();
374 : }
375 :
376 : // Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
377 : // shouldn't have to know about the Window/WindowProxy distinction.
378 808 : RootedValue receiver(cx, ValueToWindowProxyIfWindow(receiver_));
379 :
380 : // Special case. See the comment on BaseProxyHandler::mHasPrototype.
381 404 : if (handler->hasPrototype())
382 28 : return handler->BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
383 :
384 376 : return handler->set(cx, proxy, id, v, receiver, result);
385 : }
386 :
387 : bool
388 24 : js::ProxySetProperty(JSContext* cx, HandleObject proxy, HandleId id, HandleValue val, bool strict)
389 : {
390 24 : ObjectOpResult result;
391 48 : RootedValue receiver(cx, ObjectValue(*proxy));
392 24 : if (!Proxy::set(cx, proxy, id, val, receiver, result))
393 0 : return false;
394 24 : return result.checkStrictErrorOrWarning(cx, proxy, id, strict);
395 : }
396 :
397 : bool
398 97 : js::ProxySetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal, HandleValue val,
399 : bool strict)
400 : {
401 194 : RootedId id(cx);
402 97 : if (!ValueToId<CanGC>(cx, idVal, &id))
403 0 : return false;
404 :
405 97 : ObjectOpResult result;
406 194 : RootedValue receiver(cx, ObjectValue(*proxy));
407 97 : if (!Proxy::set(cx, proxy, id, val, receiver, result))
408 0 : return false;
409 97 : return result.checkStrictErrorOrWarning(cx, proxy, id, strict);
410 : }
411 :
412 : bool
413 49 : Proxy::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
414 : {
415 49 : if (!CheckRecursionLimit(cx))
416 0 : return false;
417 49 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
418 98 : AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
419 49 : if (!policy.allowed())
420 0 : return policy.returnValue();
421 49 : return handler->getOwnEnumerablePropertyKeys(cx, proxy, props);
422 : }
423 :
424 : JSObject*
425 5 : Proxy::enumerate(JSContext* cx, HandleObject proxy)
426 : {
427 5 : if (!CheckRecursionLimit(cx))
428 0 : return nullptr;
429 :
430 5 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
431 5 : if (handler->hasPrototype()) {
432 0 : AutoIdVector props(cx);
433 0 : if (!Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props))
434 0 : return nullptr;
435 :
436 0 : RootedObject proto(cx);
437 0 : if (!GetPrototype(cx, proxy, &proto))
438 0 : return nullptr;
439 0 : if (!proto)
440 0 : return EnumeratedIdVectorToIterator(cx, proxy, 0, props);
441 0 : assertSameCompartment(cx, proxy, proto);
442 :
443 0 : AutoIdVector protoProps(cx);
444 0 : if (!GetPropertyKeys(cx, proto, 0, &protoProps))
445 0 : return nullptr;
446 0 : if (!AppendUnique(cx, props, protoProps))
447 0 : return nullptr;
448 0 : return EnumeratedIdVectorToIterator(cx, proxy, 0, props);
449 : }
450 :
451 : AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
452 10 : BaseProxyHandler::ENUMERATE, true);
453 :
454 : // If the policy denies access but wants us to return true, we need
455 : // to hand a valid (empty) iterator object to the caller.
456 5 : if (!policy.allowed()) {
457 0 : if (!policy.returnValue())
458 0 : return nullptr;
459 0 : return NewEmptyPropertyIterator(cx, 0);
460 : }
461 5 : return handler->enumerate(cx, proxy);
462 : }
463 :
464 : bool
465 4925 : Proxy::call(JSContext* cx, HandleObject proxy, const CallArgs& args)
466 : {
467 4925 : if (!CheckRecursionLimit(cx))
468 0 : return false;
469 4925 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
470 :
471 : // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
472 : // can only set our default value once we're sure that we're not calling the
473 : // trap.
474 : AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
475 9850 : BaseProxyHandler::CALL, true);
476 4925 : if (!policy.allowed()) {
477 0 : args.rval().setUndefined();
478 0 : return policy.returnValue();
479 : }
480 :
481 4925 : return handler->call(cx, proxy, args);
482 : }
483 :
484 : bool
485 176 : Proxy::construct(JSContext* cx, HandleObject proxy, const CallArgs& args)
486 : {
487 176 : if (!CheckRecursionLimit(cx))
488 0 : return false;
489 176 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
490 :
491 : // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
492 : // can only set our default value once we're sure that we're not calling the
493 : // trap.
494 : AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
495 352 : BaseProxyHandler::CALL, true);
496 176 : if (!policy.allowed()) {
497 0 : args.rval().setUndefined();
498 0 : return policy.returnValue();
499 : }
500 :
501 176 : return handler->construct(cx, proxy, args);
502 : }
503 :
504 : bool
505 0 : Proxy::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl, const CallArgs& args)
506 : {
507 0 : if (!CheckRecursionLimit(cx))
508 0 : return false;
509 0 : RootedObject proxy(cx, &args.thisv().toObject());
510 : // Note - we don't enter a policy here because our security architecture
511 : // guards against nativeCall by overriding the trap itself in the right
512 : // circumstances.
513 0 : return proxy->as<ProxyObject>().handler()->nativeCall(cx, test, impl, args);
514 : }
515 :
516 : bool
517 24 : Proxy::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp)
518 : {
519 24 : if (!CheckRecursionLimit(cx))
520 0 : return false;
521 24 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
522 24 : *bp = false; // default result if we refuse to perform this action
523 48 : AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET, true);
524 24 : if (!policy.allowed())
525 0 : return policy.returnValue();
526 24 : return proxy->as<ProxyObject>().handler()->hasInstance(cx, proxy, v, bp);
527 : }
528 :
529 : bool
530 115 : Proxy::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls)
531 : {
532 115 : if (!CheckRecursionLimit(cx))
533 0 : return false;
534 115 : return proxy->as<ProxyObject>().handler()->getBuiltinClass(cx, proxy, cls);
535 : }
536 :
537 : bool
538 55 : Proxy::isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer)
539 : {
540 55 : if (!CheckRecursionLimit(cx))
541 0 : return false;
542 55 : return proxy->as<ProxyObject>().handler()->isArray(cx, proxy, answer);
543 : }
544 :
545 : const char*
546 0 : Proxy::className(JSContext* cx, HandleObject proxy)
547 : {
548 : // Check for unbounded recursion, but don't signal an error; className
549 : // needs to be infallible.
550 : int stackDummy;
551 0 : if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), &stackDummy))
552 0 : return "too much recursion";
553 :
554 0 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
555 : AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
556 0 : BaseProxyHandler::GET, /* mayThrow = */ false);
557 : // Do the safe thing if the policy rejects.
558 0 : if (!policy.allowed()) {
559 0 : return handler->BaseProxyHandler::className(cx, proxy);
560 : }
561 0 : return handler->className(cx, proxy);
562 : }
563 :
564 : JSString*
565 0 : Proxy::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent)
566 : {
567 0 : if (!CheckRecursionLimit(cx))
568 0 : return nullptr;
569 0 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
570 : AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
571 0 : BaseProxyHandler::GET, /* mayThrow = */ false);
572 : // Do the safe thing if the policy rejects.
573 0 : if (!policy.allowed())
574 0 : return handler->BaseProxyHandler::fun_toString(cx, proxy, indent);
575 0 : return handler->fun_toString(cx, proxy, indent);
576 : }
577 :
578 : RegExpShared*
579 0 : Proxy::regexp_toShared(JSContext* cx, HandleObject proxy)
580 : {
581 0 : if (!CheckRecursionLimit(cx))
582 0 : return nullptr;
583 0 : return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy);
584 : }
585 :
586 : bool
587 0 : Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp)
588 : {
589 0 : if (!CheckRecursionLimit(cx))
590 0 : return false;
591 0 : return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
592 : }
593 :
594 : JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject*>(0x1);
595 :
596 : /* static */ bool
597 0 : Proxy::watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable)
598 : {
599 0 : if (!CheckRecursionLimit(cx))
600 0 : return false;
601 0 : return proxy->as<ProxyObject>().handler()->watch(cx, proxy, id, callable);
602 : }
603 :
604 : /* static */ bool
605 0 : Proxy::unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id)
606 : {
607 0 : if (!CheckRecursionLimit(cx))
608 0 : return false;
609 0 : return proxy->as<ProxyObject>().handler()->unwatch(cx, proxy, id);
610 : }
611 :
612 : /* static */ bool
613 35 : Proxy::getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end,
614 : ElementAdder* adder)
615 : {
616 35 : if (!CheckRecursionLimit(cx))
617 0 : return false;
618 35 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
619 : AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET,
620 70 : /* mayThrow = */ true);
621 35 : if (!policy.allowed()) {
622 0 : if (policy.returnValue()) {
623 0 : MOZ_ASSERT(!cx->isExceptionPending());
624 0 : return js::GetElementsWithAdder(cx, proxy, proxy, begin, end, adder);
625 : }
626 0 : return false;
627 : }
628 35 : return handler->getElements(cx, proxy, begin, end, adder);
629 : }
630 :
631 : /* static */ void
632 2112 : Proxy::trace(JSTracer* trc, JSObject* proxy)
633 : {
634 2112 : const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
635 2112 : handler->trace(trc, proxy);
636 2112 : }
637 :
638 : static bool
639 397 : proxy_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
640 : MutableHandleObject objp, MutableHandle<JS::PropertyResult> propp)
641 : {
642 : bool found;
643 397 : if (!Proxy::has(cx, obj, id, &found))
644 0 : return false;
645 :
646 397 : if (found) {
647 51 : propp.setNonNativeProperty();
648 51 : objp.set(obj);
649 : } else {
650 346 : propp.setNotFound();
651 346 : objp.set(nullptr);
652 : }
653 397 : return true;
654 : }
655 :
656 : static bool
657 414 : proxy_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
658 : {
659 414 : if (!Proxy::delete_(cx, obj, id, result))
660 0 : return false;
661 414 : return SuppressDeletedProperty(cx, obj, id); // XXX is this necessary?
662 : }
663 :
664 : /* static */ void
665 2112 : ProxyObject::trace(JSTracer* trc, JSObject* obj)
666 : {
667 2112 : ProxyObject* proxy = &obj->as<ProxyObject>();
668 :
669 2112 : TraceEdge(trc, &proxy->shape_, "ProxyObject_shape");
670 :
671 : #ifdef DEBUG
672 2112 : if (TlsContext.get()->isStrictProxyCheckingEnabled() && proxy->is<WrapperObject>()) {
673 8 : JSObject* referent = MaybeForwarded(proxy->target());
674 8 : if (referent->compartment() != proxy->compartment()) {
675 : /*
676 : * Assert that this proxy is tracked in the wrapper map. We maintain
677 : * the invariant that the wrapped object is the key in the wrapper map.
678 : */
679 8 : Value key = ObjectValue(*referent);
680 8 : WrapperMap::Ptr p = proxy->compartment()->lookupWrapper(key);
681 8 : MOZ_ASSERT(p);
682 8 : MOZ_ASSERT(*p->value().unsafeGet() == ObjectValue(*proxy));
683 : }
684 : }
685 : #endif
686 :
687 : // Note: If you add new slots here, make sure to change
688 : // nuke() to cope.
689 2112 : TraceCrossCompartmentEdge(trc, obj, proxy->slotOfPrivate(), "private");
690 :
691 2112 : size_t nreserved = proxy->numReservedSlots();
692 6335 : for (size_t i = 0; i < nreserved; i++) {
693 : /*
694 : * The GC can use the second reserved slot to link the cross compartment
695 : * wrappers into a linked list, in which case we don't want to trace it.
696 : */
697 4223 : if (proxy->is<CrossCompartmentWrapperObject>() &&
698 : i == CrossCompartmentWrapperObject::GrayLinkReservedSlot)
699 : {
700 2110 : continue;
701 : }
702 2113 : TraceEdge(trc, proxy->reservedSlotPtr(i), "proxy_reserved");
703 : }
704 :
705 2112 : Proxy::trace(trc, obj);
706 2112 : }
707 :
708 : JSObject*
709 12 : js::proxy_WeakmapKeyDelegate(JSObject* obj)
710 : {
711 12 : MOZ_ASSERT(obj->is<ProxyObject>());
712 12 : return obj->as<ProxyObject>().handler()->weakmapKeyDelegate(obj);
713 : }
714 :
715 : static void
716 11 : proxy_Finalize(FreeOp* fop, JSObject* obj)
717 : {
718 : // Suppress a bogus warning about finalize().
719 22 : JS::AutoSuppressGCAnalysis nogc;
720 :
721 11 : MOZ_ASSERT(obj->is<ProxyObject>());
722 11 : obj->as<ProxyObject>().handler()->finalize(fop, obj);
723 :
724 11 : if (!obj->as<ProxyObject>().usingInlineValueArray())
725 0 : js_free(js::detail::GetProxyDataLayout(obj)->values());
726 11 : }
727 :
728 : static void
729 2101 : proxy_ObjectMoved(JSObject* obj, const JSObject* old)
730 : {
731 2101 : MOZ_ASSERT(obj->is<ProxyObject>());
732 2101 : obj->as<ProxyObject>().handler()->objectMoved(obj, old);
733 2101 : }
734 :
735 : bool
736 4925 : js::proxy_Call(JSContext* cx, unsigned argc, Value* vp)
737 : {
738 4925 : CallArgs args = CallArgsFromVp(argc, vp);
739 9850 : RootedObject proxy(cx, &args.callee());
740 4925 : MOZ_ASSERT(proxy->is<ProxyObject>());
741 9850 : return Proxy::call(cx, proxy, args);
742 : }
743 :
744 : bool
745 176 : js::proxy_Construct(JSContext* cx, unsigned argc, Value* vp)
746 : {
747 176 : CallArgs args = CallArgsFromVp(argc, vp);
748 352 : RootedObject proxy(cx, &args.callee());
749 176 : MOZ_ASSERT(proxy->is<ProxyObject>());
750 352 : return Proxy::construct(cx, proxy, args);
751 : }
752 :
753 : const ClassOps js::ProxyClassOps = {
754 : nullptr, /* addProperty */
755 : nullptr, /* delProperty */
756 : nullptr, /* getProperty */
757 : nullptr, /* setProperty */
758 : nullptr, /* enumerate */
759 : nullptr, /* newEnumerate */
760 : nullptr, /* resolve */
761 : nullptr, /* mayResolve */
762 : proxy_Finalize, /* finalize */
763 : nullptr, /* call */
764 : Proxy::hasInstance, /* hasInstance */
765 : nullptr, /* construct */
766 : ProxyObject::trace, /* trace */
767 : };
768 :
769 : const ClassExtension js::ProxyClassExtension = PROXY_MAKE_EXT(
770 : proxy_ObjectMoved
771 : );
772 :
773 : const ObjectOps js::ProxyObjectOps = {
774 : proxy_LookupProperty,
775 : Proxy::defineProperty,
776 : Proxy::has,
777 : Proxy::get,
778 : Proxy::set,
779 : Proxy::getOwnPropertyDescriptor,
780 : proxy_DeleteProperty,
781 : Proxy::watch, Proxy::unwatch,
782 : Proxy::getElements,
783 : Proxy::fun_toString
784 : };
785 :
786 : const Class js::ProxyObject::proxyClass =
787 : PROXY_CLASS_DEF("Proxy",
788 : JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy) |
789 : JSCLASS_HAS_RESERVED_SLOTS(2));
790 :
791 : const Class* const js::ProxyClassPtr = &js::ProxyObject::proxyClass;
792 :
793 : JS_FRIEND_API(JSObject*)
794 9333 : js::NewProxyObject(JSContext* cx, const BaseProxyHandler* handler, HandleValue priv, JSObject* proto_,
795 : const ProxyOptions& options)
796 : {
797 9333 : if (options.lazyProto()) {
798 5 : MOZ_ASSERT(!proto_);
799 5 : proto_ = TaggedProto::LazyProto;
800 : }
801 :
802 9333 : return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), options);
803 : }
804 :
805 : void
806 10 : ProxyObject::renew(const BaseProxyHandler* handler, const Value& priv)
807 : {
808 10 : MOZ_ASSERT(!IsInsideNursery(this));
809 10 : MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
810 10 : MOZ_ASSERT(getClass() == &ProxyObject::proxyClass);
811 10 : MOZ_ASSERT(!IsWindowProxy(this));
812 10 : MOZ_ASSERT(hasDynamicPrototype());
813 :
814 10 : setHandler(handler);
815 10 : setCrossCompartmentPrivate(priv);
816 30 : for (size_t i = 0; i < numReservedSlots(); i++)
817 20 : setReservedSlot(i, UndefinedValue());
818 10 : }
819 :
820 : JS_FRIEND_API(JSObject*)
821 9 : js::InitProxyClass(JSContext* cx, HandleObject obj)
822 : {
823 : static const JSFunctionSpec static_methods[] = {
824 : JS_FN("revocable", proxy_revocable, 2, 0),
825 : JS_FS_END
826 : };
827 :
828 9 : Handle<GlobalObject*> global = obj.as<GlobalObject>();
829 18 : RootedFunction ctor(cx);
830 9 : ctor = GlobalObject::createConstructor(cx, proxy, cx->names().Proxy, 2);
831 9 : if (!ctor)
832 0 : return nullptr;
833 :
834 9 : if (!JS_DefineFunctions(cx, ctor, static_methods))
835 0 : return nullptr;
836 9 : if (!JS_DefineProperty(cx, obj, "Proxy", ctor, JSPROP_RESOLVING, JS_STUBGETTER, JS_STUBSETTER))
837 0 : return nullptr;
838 :
839 9 : global->setConstructor(JSProto_Proxy, ObjectValue(*ctor));
840 9 : return ctor;
841 : }
|