Bug 1681763 [wpt PR 26839] - WPT test: referrer on navigation from opaque origin...
[gecko.git] / js / src / proxy / ScriptedProxyHandler.cpp
blob9d2d17f8a6a202959d4147ab78278701109baece
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/. */
7 #include "proxy/ScriptedProxyHandler.h"
9 #include "jsapi.h"
11 #include "js/CharacterEncoding.h"
12 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
13 #include "js/PropertyDescriptor.h" // JS::FromPropertyDescriptor
14 #include "vm/EqualityOperations.h" // js::SameValue
15 #include "vm/JSFunction.h"
16 #include "vm/JSObject.h"
17 #include "vm/PlainObject.h" // js::PlainObject
19 #include "vm/JSObject-inl.h"
20 #include "vm/NativeObject-inl.h"
22 using namespace js;
24 using JS::IsArrayAnswer;
26 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
27 // 9.1.6.2 IsCompatiblePropertyDescriptor. BUT that method just calls
28 // 9.1.6.3 ValidateAndApplyPropertyDescriptor with two additional constant
29 // arguments. Therefore step numbering is from the latter method, and
30 // resulting dead code has been removed.
32 // If an exception should be thrown, we will set errorDetails.
33 static bool IsCompatiblePropertyDescriptor(JSContext* cx, bool extensible,
34 Handle<PropertyDescriptor> desc,
35 Handle<PropertyDescriptor> current,
36 const char** errorDetails) {
37 // precondition: we won't set details if checks pass, so it must be null
38 // here.
39 MOZ_ASSERT(*errorDetails == nullptr);
41 // Step 2.
42 if (!current.object()) {
43 // Step 2a-b,e. As |O| is always undefined, steps 2c-d fall away.
44 if (!extensible) {
45 static const char DETAILS_NOT_EXTENSIBLE[] =
46 "proxy can't report an extensible object as non-extensible";
47 *errorDetails = DETAILS_NOT_EXTENSIBLE;
49 return true;
52 // Step 3.
53 if (!desc.hasValue() && !desc.hasWritable() && !desc.hasGetterObject() &&
54 !desc.hasSetterObject() && !desc.hasEnumerable() &&
55 !desc.hasConfigurable()) {
56 return true;
59 // Step 4.
60 if ((!desc.hasWritable() ||
61 (current.hasWritable() && desc.writable() == current.writable())) &&
62 (!desc.hasGetterObject() || desc.getter() == current.getter()) &&
63 (!desc.hasSetterObject() || desc.setter() == current.setter()) &&
64 (!desc.hasEnumerable() || desc.enumerable() == current.enumerable()) &&
65 (!desc.hasConfigurable() ||
66 desc.configurable() == current.configurable())) {
67 if (!desc.hasValue()) {
68 return true;
71 bool same = false;
72 if (!SameValue(cx, desc.value(), current.value(), &same)) {
73 return false;
76 if (same) {
77 return true;
81 // Step 5.
82 if (!current.configurable()) {
83 // Step 5a.
84 if (desc.hasConfigurable() && desc.configurable()) {
85 static const char DETAILS_CANT_REPORT_NC_AS_C[] =
86 "proxy can't report an existing non-configurable property as "
87 "configurable";
88 *errorDetails = DETAILS_CANT_REPORT_NC_AS_C;
89 return true;
92 // Step 5b.
93 if (desc.hasEnumerable() && desc.enumerable() != current.enumerable()) {
94 static const char DETAILS_ENUM_DIFFERENT[] =
95 "proxy can't report a different 'enumerable' from target when target "
96 "is not configurable";
97 *errorDetails = DETAILS_ENUM_DIFFERENT;
98 return true;
102 // Step 6.
103 if (desc.isGenericDescriptor()) {
104 return true;
107 // Step 7.
108 if (current.isDataDescriptor() != desc.isDataDescriptor()) {
109 // Steps 7a, 11. As |O| is always undefined, steps 2b-c fall away.
110 if (!current.configurable()) {
111 static const char DETAILS_CURRENT_NC_DIFF_TYPE[] =
112 "proxy can't report a different descriptor type when target is not "
113 "configurable";
114 *errorDetails = DETAILS_CURRENT_NC_DIFF_TYPE;
116 return true;
119 // Step 8.
120 if (current.isDataDescriptor()) {
121 MOZ_ASSERT(desc.isDataDescriptor()); // by step 7
122 if (!current.configurable() && !current.writable()) {
123 if (desc.hasWritable() && desc.writable()) {
124 static const char DETAILS_CANT_REPORT_NW_AS_W[] =
125 "proxy can't report a non-configurable, non-writable property as "
126 "writable";
127 *errorDetails = DETAILS_CANT_REPORT_NW_AS_W;
128 return true;
131 if (desc.hasValue()) {
132 bool same;
133 if (!SameValue(cx, desc.value(), current.value(), &same)) {
134 return false;
136 if (!same) {
137 static const char DETAILS_DIFFERENT_VALUE[] =
138 "proxy must report the same value for the non-writable, "
139 "non-configurable property";
140 *errorDetails = DETAILS_DIFFERENT_VALUE;
141 return true;
146 return true;
149 // Step 9.
150 MOZ_ASSERT(current.isAccessorDescriptor()); // by step 8
151 MOZ_ASSERT(desc.isAccessorDescriptor()); // by step 7
153 if (current.configurable()) {
154 return true;
156 if (desc.hasSetterObject() && (desc.setter() != current.setter())) {
157 static const char DETAILS_SETTERS_DIFFERENT[] =
158 "proxy can't report different setters for a currently non-configurable "
159 "property";
160 *errorDetails = DETAILS_SETTERS_DIFFERENT;
161 } else if (desc.hasGetterObject() && (desc.getter() != current.getter())) {
162 static const char DETAILS_GETTERS_DIFFERENT[] =
163 "proxy can't report different getters for a currently non-configurable "
164 "property";
165 *errorDetails = DETAILS_GETTERS_DIFFERENT;
167 return true;
170 // Get the [[ProxyHandler]] of a scripted proxy.
171 /* static */
172 JSObject* ScriptedProxyHandler::handlerObject(const JSObject* proxy) {
173 MOZ_ASSERT(proxy->as<ProxyObject>().handler() ==
174 &ScriptedProxyHandler::singleton);
175 return proxy->as<ProxyObject>()
176 .reservedSlot(ScriptedProxyHandler::HANDLER_EXTRA)
177 .toObjectOrNull();
180 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
181 // 7.3.9 GetMethod, reimplemented for proxy handler trap-getting to produce
182 // better error messages.
183 static bool GetProxyTrap(JSContext* cx, HandleObject handler,
184 HandlePropertyName name, MutableHandleValue func) {
185 // Steps 2, 5.
186 if (!GetProperty(cx, handler, handler, name, func)) {
187 return false;
190 // Step 3.
191 if (func.isUndefined()) {
192 return true;
195 if (func.isNull()) {
196 func.setUndefined();
197 return true;
200 // Step 4.
201 if (!IsCallable(func)) {
202 UniqueChars bytes = EncodeAscii(cx, name);
203 if (!bytes) {
204 return false;
207 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_TRAP,
208 bytes.get());
209 return false;
212 return true;
215 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
216 // 9.5.1 Proxy.[[GetPrototypeOf]].
217 bool ScriptedProxyHandler::getPrototype(JSContext* cx, HandleObject proxy,
218 MutableHandleObject protop) const {
219 // Steps 1-3.
220 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
221 if (!handler) {
222 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
223 JSMSG_PROXY_REVOKED);
224 return false;
227 // Step 4.
228 RootedObject target(cx, proxy->as<ProxyObject>().target());
229 MOZ_ASSERT(target);
231 // Step 5.
232 RootedValue trap(cx);
233 if (!GetProxyTrap(cx, handler, cx->names().getPrototypeOf, &trap)) {
234 return false;
237 // Step 6.
238 if (trap.isUndefined()) {
239 return GetPrototype(cx, target, protop);
242 // Step 7.
243 RootedValue handlerProto(cx);
245 FixedInvokeArgs<1> args(cx);
247 args[0].setObject(*target);
249 handlerProto.setObject(*handler);
251 if (!js::Call(cx, trap, handlerProto, args, &handlerProto)) {
252 return false;
256 // Step 8.
257 if (!handlerProto.isObjectOrNull()) {
258 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
259 JSMSG_BAD_GETPROTOTYPEOF_TRAP_RETURN);
260 return false;
263 // Step 9.
264 bool extensibleTarget;
265 if (!IsExtensible(cx, target, &extensibleTarget)) {
266 return false;
269 // Step 10.
270 if (extensibleTarget) {
271 protop.set(handlerProto.toObjectOrNull());
272 return true;
275 // Step 11.
276 RootedObject targetProto(cx);
277 if (!GetPrototype(cx, target, &targetProto)) {
278 return false;
281 // Step 12.
282 if (handlerProto.toObjectOrNull() != targetProto) {
283 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
284 JSMSG_INCONSISTENT_GETPROTOTYPEOF_TRAP);
285 return false;
288 // Step 13.
289 protop.set(handlerProto.toObjectOrNull());
290 return true;
293 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
294 // 9.5.2 Proxy.[[SetPrototypeOf]].
295 bool ScriptedProxyHandler::setPrototype(JSContext* cx, HandleObject proxy,
296 HandleObject proto,
297 ObjectOpResult& result) const {
298 // Steps 1-4.
299 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
300 if (!handler) {
301 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
302 JSMSG_PROXY_REVOKED);
303 return false;
306 // Step 5.
307 RootedObject target(cx, proxy->as<ProxyObject>().target());
308 MOZ_ASSERT(target);
310 // Step 6.
311 RootedValue trap(cx);
312 if (!GetProxyTrap(cx, handler, cx->names().setPrototypeOf, &trap)) {
313 return false;
316 // Step 7.
317 if (trap.isUndefined()) {
318 return SetPrototype(cx, target, proto, result);
321 // Step 8.
322 bool booleanTrapResult;
324 FixedInvokeArgs<2> args(cx);
326 args[0].setObject(*target);
327 args[1].setObjectOrNull(proto);
329 RootedValue hval(cx, ObjectValue(*handler));
330 if (!js::Call(cx, trap, hval, args, &hval)) {
331 return false;
334 booleanTrapResult = ToBoolean(hval);
337 // Step 9.
338 if (!booleanTrapResult) {
339 return result.fail(JSMSG_PROXY_SETPROTOTYPEOF_RETURNED_FALSE);
342 // Step 10.
343 bool extensibleTarget;
344 if (!IsExtensible(cx, target, &extensibleTarget)) {
345 return false;
348 // Step 11.
349 if (extensibleTarget) {
350 return result.succeed();
353 // Step 12.
354 RootedObject targetProto(cx);
355 if (!GetPrototype(cx, target, &targetProto)) {
356 return false;
359 // Step 13.
360 if (proto != targetProto) {
361 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
362 JSMSG_INCONSISTENT_SETPROTOTYPEOF_TRAP);
363 return false;
366 // Step 14.
367 return result.succeed();
370 bool ScriptedProxyHandler::getPrototypeIfOrdinary(
371 JSContext* cx, HandleObject proxy, bool* isOrdinary,
372 MutableHandleObject protop) const {
373 *isOrdinary = false;
374 return true;
377 // Not yet part of ES6, but hopefully to be standards-tracked -- and needed to
378 // handle revoked proxies in any event.
379 bool ScriptedProxyHandler::setImmutablePrototype(JSContext* cx,
380 HandleObject proxy,
381 bool* succeeded) const {
382 RootedObject target(cx, proxy->as<ProxyObject>().target());
383 if (!target) {
384 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
385 JSMSG_PROXY_REVOKED);
386 return false;
389 return SetImmutablePrototype(cx, target, succeeded);
392 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
393 // 9.5.4 Proxy.[[PreventExtensions]]()
394 bool ScriptedProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy,
395 ObjectOpResult& result) const {
396 // Steps 1-3.
397 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
398 if (!handler) {
399 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
400 JSMSG_PROXY_REVOKED);
401 return false;
404 // Step 4.
405 RootedObject target(cx, proxy->as<ProxyObject>().target());
406 MOZ_ASSERT(target);
408 // Step 5.
409 RootedValue trap(cx);
410 if (!GetProxyTrap(cx, handler, cx->names().preventExtensions, &trap)) {
411 return false;
414 // Step 6.
415 if (trap.isUndefined()) {
416 return PreventExtensions(cx, target, result);
419 // Step 7.
420 bool booleanTrapResult;
422 RootedValue arg(cx, ObjectValue(*target));
423 RootedValue trapResult(cx);
424 if (!Call(cx, trap, handler, arg, &trapResult)) {
425 return false;
428 booleanTrapResult = ToBoolean(trapResult);
431 // Step 8.
432 if (booleanTrapResult) {
433 // Step 8a.
434 bool targetIsExtensible;
435 if (!IsExtensible(cx, target, &targetIsExtensible)) {
436 return false;
439 if (targetIsExtensible) {
440 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
441 JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE);
442 return false;
445 // Step 9.
446 return result.succeed();
449 // Also step 9.
450 return result.fail(JSMSG_PROXY_PREVENTEXTENSIONS_RETURNED_FALSE);
453 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
454 // 9.5.3 Proxy.[[IsExtensible]]()
455 bool ScriptedProxyHandler::isExtensible(JSContext* cx, HandleObject proxy,
456 bool* extensible) const {
457 // Steps 1-3.
458 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
459 if (!handler) {
460 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
461 JSMSG_PROXY_REVOKED);
462 return false;
465 // Step 4.
466 RootedObject target(cx, proxy->as<ProxyObject>().target());
467 MOZ_ASSERT(target);
469 // Step 5.
470 RootedValue trap(cx);
471 if (!GetProxyTrap(cx, handler, cx->names().isExtensible, &trap)) {
472 return false;
475 // Step 6.
476 if (trap.isUndefined()) {
477 return IsExtensible(cx, target, extensible);
480 // Step 7.
481 bool booleanTrapResult;
483 RootedValue arg(cx, ObjectValue(*target));
484 RootedValue trapResult(cx);
485 if (!Call(cx, trap, handler, arg, &trapResult)) {
486 return false;
489 booleanTrapResult = ToBoolean(trapResult);
492 // Steps 8.
493 bool targetResult;
494 if (!IsExtensible(cx, target, &targetResult)) {
495 return false;
498 // Step 9.
499 if (targetResult != booleanTrapResult) {
500 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
501 JSMSG_PROXY_EXTENSIBILITY);
502 return false;
505 // Step 10.
506 *extensible = booleanTrapResult;
507 return true;
510 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
511 // 9.5.5 Proxy.[[GetOwnProperty]](P)
512 bool ScriptedProxyHandler::getOwnPropertyDescriptor(
513 JSContext* cx, HandleObject proxy, HandleId id,
514 MutableHandle<PropertyDescriptor> desc) const {
515 // Steps 2-4.
516 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
517 if (!handler) {
518 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
519 JSMSG_PROXY_REVOKED);
520 return false;
523 // Step 5.
524 RootedObject target(cx, proxy->as<ProxyObject>().target());
525 MOZ_ASSERT(target);
527 // Step 6.
528 RootedValue trap(cx);
529 if (!GetProxyTrap(cx, handler, cx->names().getOwnPropertyDescriptor, &trap)) {
530 return false;
533 // Step 7.
534 if (trap.isUndefined()) {
535 return GetOwnPropertyDescriptor(cx, target, id, desc);
538 // Step 8.
539 RootedValue propKey(cx);
540 if (!IdToStringOrSymbol(cx, id, &propKey)) {
541 return false;
544 RootedValue trapResult(cx);
545 RootedValue targetVal(cx, ObjectValue(*target));
546 if (!Call(cx, trap, handler, targetVal, propKey, &trapResult)) {
547 return false;
550 // Step 9.
551 if (!trapResult.isUndefined() && !trapResult.isObject()) {
552 return js::Throw(cx, id, JSMSG_PROXY_GETOWN_OBJORUNDEF);
555 // Step 10.
556 Rooted<PropertyDescriptor> targetDesc(cx);
557 if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) {
558 return false;
561 // Step 11.
562 if (trapResult.isUndefined()) {
563 // Step 11a.
564 if (!targetDesc.object()) {
565 desc.object().set(nullptr);
566 return true;
569 // Step 11b.
570 if (!targetDesc.configurable()) {
571 return js::Throw(cx, id, JSMSG_CANT_REPORT_NC_AS_NE);
574 // Steps 11c-d.
575 bool extensibleTarget;
576 if (!IsExtensible(cx, target, &extensibleTarget)) {
577 return false;
580 // Step 11e.
581 if (!extensibleTarget) {
582 return js::Throw(cx, id, JSMSG_CANT_REPORT_E_AS_NE);
585 // Step 11f.
586 desc.object().set(nullptr);
587 return true;
590 // Step 12.
591 bool extensibleTarget;
592 if (!IsExtensible(cx, target, &extensibleTarget)) {
593 return false;
596 // Step 13.
597 Rooted<PropertyDescriptor> resultDesc(cx);
598 if (!ToPropertyDescriptor(cx, trapResult, true, &resultDesc)) {
599 return false;
602 // Step 14.
603 CompletePropertyDescriptor(&resultDesc);
605 // Step 15.
606 const char* errorDetails = nullptr;
607 if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, resultDesc,
608 targetDesc, &errorDetails))
609 return false;
611 // Step 16.
612 if (errorDetails) {
613 return js::Throw(cx, id, JSMSG_CANT_REPORT_INVALID, errorDetails);
616 // Step 17.
617 if (!resultDesc.configurable()) {
618 if (!targetDesc.object()) {
619 return js::Throw(cx, id, JSMSG_CANT_REPORT_NE_AS_NC);
622 if (targetDesc.configurable()) {
623 return js::Throw(cx, id, JSMSG_CANT_REPORT_C_AS_NC);
626 if (resultDesc.hasWritable() && !resultDesc.writable()) {
627 if (targetDesc.writable()) {
628 return js::Throw(cx, id, JSMSG_CANT_REPORT_W_AS_NW);
633 // Step 18.
634 desc.set(resultDesc);
635 desc.object().set(proxy);
636 return true;
639 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
640 // 9.5.6 Proxy.[[DefineOwnProperty]](P, Desc)
641 bool ScriptedProxyHandler::defineProperty(JSContext* cx, HandleObject proxy,
642 HandleId id,
643 Handle<PropertyDescriptor> desc,
644 ObjectOpResult& result) const {
645 // Steps 2-4.
646 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
647 if (!handler) {
648 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
649 JSMSG_PROXY_REVOKED);
650 return false;
653 // Step 5.
654 RootedObject target(cx, proxy->as<ProxyObject>().target());
655 MOZ_ASSERT(target);
657 // Step 6.
658 RootedValue trap(cx);
659 if (!GetProxyTrap(cx, handler, cx->names().defineProperty, &trap)) {
660 return false;
663 // Step 7.
664 if (trap.isUndefined()) {
665 return DefineProperty(cx, target, id, desc, result);
668 // Step 8.
669 RootedValue descObj(cx);
670 if (!FromPropertyDescriptorToObject(cx, desc, &descObj)) {
671 return false;
674 // Step 9.
675 RootedValue propKey(cx);
676 if (!IdToStringOrSymbol(cx, id, &propKey)) {
677 return false;
680 RootedValue trapResult(cx);
682 FixedInvokeArgs<3> args(cx);
684 args[0].setObject(*target);
685 args[1].set(propKey);
686 args[2].set(descObj);
688 RootedValue thisv(cx, ObjectValue(*handler));
689 if (!Call(cx, trap, thisv, args, &trapResult)) {
690 return false;
694 // Step 10.
695 if (!ToBoolean(trapResult)) {
696 return result.fail(JSMSG_PROXY_DEFINE_RETURNED_FALSE);
699 // Step 11.
700 Rooted<PropertyDescriptor> targetDesc(cx);
701 if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) {
702 return false;
705 // Step 12.
706 bool extensibleTarget;
707 if (!IsExtensible(cx, target, &extensibleTarget)) {
708 return false;
711 // Steps 13-14.
712 bool settingConfigFalse = desc.hasConfigurable() && !desc.configurable();
714 // Steps 15-16.
715 if (!targetDesc.object()) {
716 // Step 15a.
717 if (!extensibleTarget) {
718 return js::Throw(cx, id, JSMSG_CANT_DEFINE_NEW);
721 // Step 15b.
722 if (settingConfigFalse) {
723 return js::Throw(cx, id, JSMSG_CANT_DEFINE_NE_AS_NC);
725 } else {
726 // Step 16a.
727 const char* errorDetails = nullptr;
728 if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, desc, targetDesc,
729 &errorDetails))
730 return false;
732 if (errorDetails) {
733 return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID, errorDetails);
736 // Step 16b.
737 if (settingConfigFalse && targetDesc.configurable()) {
738 static const char DETAILS_CANT_REPORT_C_AS_NC[] =
739 "proxy can't define an existing configurable property as "
740 "non-configurable";
741 return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID,
742 DETAILS_CANT_REPORT_C_AS_NC);
745 if (targetDesc.isDataDescriptor() && !targetDesc.configurable() &&
746 targetDesc.writable()) {
747 if (desc.hasWritable() && !desc.writable()) {
748 static const char DETAILS_CANT_DEFINE_NW[] =
749 "proxy can't define an existing non-configurable writable property "
750 "as non-writable";
751 return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID,
752 DETAILS_CANT_DEFINE_NW);
757 // Step 17.
758 return result.succeed();
761 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
762 // 7.3.17 CreateListFromArrayLike with elementTypes fixed to symbol/string.
763 static bool CreateFilteredListFromArrayLike(JSContext* cx, HandleValue v,
764 MutableHandleIdVector props) {
765 // Step 2.
766 RootedObject obj(cx, RequireObject(cx, JSMSG_OBJECT_REQUIRED_RET_OWNKEYS,
767 JSDVG_IGNORE_STACK, v));
768 if (!obj) {
769 return false;
772 // Step 3.
773 uint32_t len;
774 if (!GetLengthProperty(cx, obj, &len)) {
775 return false;
778 // Steps 4-6.
779 RootedValue next(cx);
780 RootedId id(cx);
781 uint32_t index = 0;
782 while (index < len) {
783 // Steps 6a-b.
784 if (!GetElement(cx, obj, obj, index, &next)) {
785 return false;
788 // Step 6c.
789 if (!next.isString() && !next.isSymbol()) {
790 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
791 JSMSG_OWNKEYS_STR_SYM);
792 return false;
795 if (!PrimitiveValueToId<CanGC>(cx, next, &id)) {
796 return false;
799 // Step 6d.
800 if (!props.append(id)) {
801 return false;
804 // Step 6e.
805 index++;
808 // Step 7.
809 return true;
812 // ES2018 draft rev aab1ea3bd4d03c85d6f4a91503b4169346ab7271
813 // 9.5.11 Proxy.[[OwnPropertyKeys]]()
814 bool ScriptedProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy,
815 MutableHandleIdVector props) const {
816 // Steps 1-3.
817 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
818 if (!handler) {
819 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
820 JSMSG_PROXY_REVOKED);
821 return false;
824 // Step 4.
825 RootedObject target(cx, proxy->as<ProxyObject>().target());
826 MOZ_ASSERT(target);
828 // Step 5.
829 RootedValue trap(cx);
830 if (!GetProxyTrap(cx, handler, cx->names().ownKeys, &trap)) {
831 return false;
834 // Step 6.
835 if (trap.isUndefined()) {
836 return GetPropertyKeys(
837 cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
840 // Step 7.
841 RootedValue trapResultArray(cx);
842 RootedValue targetVal(cx, ObjectValue(*target));
843 if (!Call(cx, trap, handler, targetVal, &trapResultArray)) {
844 return false;
847 // Step 8.
848 RootedIdVector trapResult(cx);
849 if (!CreateFilteredListFromArrayLike(cx, trapResultArray, &trapResult)) {
850 return false;
853 // Steps 9, 18.
854 Rooted<GCHashSet<jsid>> uncheckedResultKeys(
855 cx, GCHashSet<jsid>(cx, trapResult.length()));
857 for (size_t i = 0, len = trapResult.length(); i < len; i++) {
858 MOZ_ASSERT(!JSID_IS_VOID(trapResult[i]));
860 auto ptr = uncheckedResultKeys.lookupForAdd(trapResult[i]);
861 if (ptr) {
862 return js::Throw(cx, trapResult[i], JSMSG_OWNKEYS_DUPLICATE);
865 if (!uncheckedResultKeys.add(ptr, trapResult[i])) {
866 return false;
870 // Step 10.
871 bool extensibleTarget;
872 if (!IsExtensible(cx, target, &extensibleTarget)) {
873 return false;
876 // Steps 11-13.
877 RootedIdVector targetKeys(cx);
878 if (!GetPropertyKeys(cx, target,
879 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
880 &targetKeys)) {
881 return false;
884 // Steps 14-15.
885 RootedIdVector targetConfigurableKeys(cx);
886 RootedIdVector targetNonconfigurableKeys(cx);
888 // Step 16.
889 Rooted<PropertyDescriptor> desc(cx);
890 for (size_t i = 0; i < targetKeys.length(); ++i) {
891 // Step 16.a.
892 if (!GetOwnPropertyDescriptor(cx, target, targetKeys[i], &desc)) {
893 return false;
896 // Steps 16.b-c.
897 if (desc.object() && !desc.configurable()) {
898 if (!targetNonconfigurableKeys.append(targetKeys[i])) {
899 return false;
901 } else {
902 if (!targetConfigurableKeys.append(targetKeys[i])) {
903 return false;
908 // Step 17.
909 if (extensibleTarget && targetNonconfigurableKeys.empty()) {
910 return props.appendAll(std::move(trapResult));
913 // Step 19.
914 for (size_t i = 0; i < targetNonconfigurableKeys.length(); ++i) {
915 MOZ_ASSERT(!JSID_IS_VOID(targetNonconfigurableKeys[i]));
917 auto ptr = uncheckedResultKeys.lookup(targetNonconfigurableKeys[i]);
919 // Step 19.a.
920 if (!ptr) {
921 return js::Throw(cx, targetNonconfigurableKeys[i], JSMSG_CANT_SKIP_NC);
924 // Step 19.b.
925 uncheckedResultKeys.remove(ptr);
928 // Step 20.
929 if (extensibleTarget) {
930 return props.appendAll(std::move(trapResult));
933 // Step 21.
934 for (size_t i = 0; i < targetConfigurableKeys.length(); ++i) {
935 MOZ_ASSERT(!JSID_IS_VOID(targetConfigurableKeys[i]));
937 auto ptr = uncheckedResultKeys.lookup(targetConfigurableKeys[i]);
939 // Step 21.a.
940 if (!ptr) {
941 return js::Throw(cx, targetConfigurableKeys[i],
942 JSMSG_CANT_REPORT_E_AS_NE);
945 // Step 21.b.
946 uncheckedResultKeys.remove(ptr);
949 // Step 22.
950 if (!uncheckedResultKeys.empty()) {
951 RootedId id(cx, uncheckedResultKeys.all().front());
952 return js::Throw(cx, id, JSMSG_CANT_REPORT_NEW);
955 // Step 23.
956 return props.appendAll(std::move(trapResult));
959 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
960 // 9.5.10 Proxy.[[Delete]](P)
961 bool ScriptedProxyHandler::delete_(JSContext* cx, HandleObject proxy,
962 HandleId id, ObjectOpResult& result) const {
963 // Steps 2-4.
964 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
965 if (!handler) {
966 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
967 JSMSG_PROXY_REVOKED);
968 return false;
971 // Step 5.
972 RootedObject target(cx, proxy->as<ProxyObject>().target());
973 MOZ_ASSERT(target);
975 // Step 6.
976 RootedValue trap(cx);
977 if (!GetProxyTrap(cx, handler, cx->names().deleteProperty, &trap)) {
978 return false;
981 // Step 7.
982 if (trap.isUndefined()) {
983 return DeleteProperty(cx, target, id, result);
986 // Step 8.
987 bool booleanTrapResult;
989 RootedValue value(cx);
990 if (!IdToStringOrSymbol(cx, id, &value)) {
991 return false;
994 RootedValue targetVal(cx, ObjectValue(*target));
995 RootedValue trapResult(cx);
996 if (!Call(cx, trap, handler, targetVal, value, &trapResult)) {
997 return false;
1000 booleanTrapResult = ToBoolean(trapResult);
1003 // Step 9.
1004 if (!booleanTrapResult) {
1005 return result.fail(JSMSG_PROXY_DELETE_RETURNED_FALSE);
1008 // Step 10.
1009 Rooted<PropertyDescriptor> desc(cx);
1010 if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) {
1011 return false;
1014 // Step 11.
1015 if (!desc.object()) {
1016 return result.succeed();
1019 // Step 12.
1020 if (!desc.configurable()) {
1021 return Throw(cx, id, JSMSG_CANT_DELETE);
1024 bool extensible;
1025 if (!IsExtensible(cx, target, &extensible)) {
1026 return false;
1029 if (!extensible) {
1030 return Throw(cx, id, JSMSG_CANT_DELETE_NON_EXTENSIBLE);
1033 // Step 13.
1034 return result.succeed();
1037 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
1038 // 9.5.7 Proxy.[[HasProperty]](P)
1039 bool ScriptedProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id,
1040 bool* bp) const {
1041 // Steps 2-4.
1042 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1043 if (!handler) {
1044 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1045 JSMSG_PROXY_REVOKED);
1046 return false;
1049 // Step 5.
1050 RootedObject target(cx, proxy->as<ProxyObject>().target());
1051 MOZ_ASSERT(target);
1053 // Step 6.
1054 RootedValue trap(cx);
1055 if (!GetProxyTrap(cx, handler, cx->names().has, &trap)) {
1056 return false;
1059 // Step 7.
1060 if (trap.isUndefined()) {
1061 return HasProperty(cx, target, id, bp);
1064 // Step 8.
1065 RootedValue value(cx);
1066 if (!IdToStringOrSymbol(cx, id, &value)) {
1067 return false;
1070 RootedValue trapResult(cx);
1071 RootedValue targetVal(cx, ObjectValue(*target));
1072 if (!Call(cx, trap, handler, targetVal, value, &trapResult)) {
1073 return false;
1076 bool booleanTrapResult = ToBoolean(trapResult);
1078 // Step 9.
1079 if (!booleanTrapResult) {
1080 // Step 9a.
1081 Rooted<PropertyDescriptor> desc(cx);
1082 if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) {
1083 return false;
1086 // Step 9b.
1087 if (desc.object()) {
1088 // Step 9b(i).
1089 if (!desc.configurable()) {
1090 return js::Throw(cx, id, JSMSG_CANT_REPORT_NC_AS_NE);
1093 // Step 9b(ii).
1094 bool extensible;
1095 if (!IsExtensible(cx, target, &extensible)) {
1096 return false;
1099 // Step 9b(iii).
1100 if (!extensible) {
1101 return js::Throw(cx, id, JSMSG_CANT_REPORT_E_AS_NE);
1106 // Step 10.
1107 *bp = booleanTrapResult;
1108 return true;
1111 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
1112 // 9.5.8 Proxy.[[GetP]](P, Receiver)
1113 bool ScriptedProxyHandler::get(JSContext* cx, HandleObject proxy,
1114 HandleValue receiver, HandleId id,
1115 MutableHandleValue vp) const {
1116 // Steps 2-4.
1117 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1118 if (!handler) {
1119 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1120 JSMSG_PROXY_REVOKED);
1121 return false;
1124 // Step 5.
1125 RootedObject target(cx, proxy->as<ProxyObject>().target());
1126 MOZ_ASSERT(target);
1128 // Steps 6.
1129 RootedValue trap(cx);
1130 if (!GetProxyTrap(cx, handler, cx->names().get, &trap)) {
1131 return false;
1134 // Step 7.
1135 if (trap.isUndefined()) {
1136 return GetProperty(cx, target, receiver, id, vp);
1139 // Step 8.
1140 RootedValue value(cx);
1141 if (!IdToStringOrSymbol(cx, id, &value)) {
1142 return false;
1145 RootedValue trapResult(cx);
1147 FixedInvokeArgs<3> args(cx);
1149 args[0].setObject(*target);
1150 args[1].set(value);
1151 args[2].set(receiver);
1153 RootedValue thisv(cx, ObjectValue(*handler));
1154 if (!Call(cx, trap, thisv, args, &trapResult)) {
1155 return false;
1159 // Step 9.
1160 Rooted<PropertyDescriptor> desc(cx);
1161 if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) {
1162 return false;
1165 // Step 10.
1166 if (desc.object()) {
1167 // Step 10a.
1168 if (desc.isDataDescriptor() && !desc.configurable() && !desc.writable()) {
1169 bool same;
1170 if (!SameValue(cx, trapResult, desc.value(), &same)) {
1171 return false;
1173 if (!same) {
1174 return js::Throw(cx, id, JSMSG_MUST_REPORT_SAME_VALUE);
1178 // Step 10b.
1179 if (desc.isAccessorDescriptor() && !desc.configurable() &&
1180 (desc.getterObject() == nullptr) && !trapResult.isUndefined()) {
1181 return js::Throw(cx, id, JSMSG_MUST_REPORT_UNDEFINED);
1185 // Step 11.
1186 vp.set(trapResult);
1187 return true;
1190 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
1191 // 9.5.9 Proxy.[[Set]](P, V, Receiver)
1192 bool ScriptedProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id,
1193 HandleValue v, HandleValue receiver,
1194 ObjectOpResult& result) const {
1195 // Steps 2-4.
1196 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1197 if (!handler) {
1198 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1199 JSMSG_PROXY_REVOKED);
1200 return false;
1203 // Step 5.
1204 RootedObject target(cx, proxy->as<ProxyObject>().target());
1205 MOZ_ASSERT(target);
1207 // Step 6.
1208 RootedValue trap(cx);
1209 if (!GetProxyTrap(cx, handler, cx->names().set, &trap)) {
1210 return false;
1213 // Step 7.
1214 if (trap.isUndefined()) {
1215 return SetProperty(cx, target, id, v, receiver, result);
1218 // Step 8.
1219 RootedValue value(cx);
1220 if (!IdToStringOrSymbol(cx, id, &value)) {
1221 return false;
1224 RootedValue trapResult(cx);
1226 FixedInvokeArgs<4> args(cx);
1228 args[0].setObject(*target);
1229 args[1].set(value);
1230 args[2].set(v);
1231 args[3].set(receiver);
1233 RootedValue thisv(cx, ObjectValue(*handler));
1234 if (!Call(cx, trap, thisv, args, &trapResult)) {
1235 return false;
1239 // Step 9.
1240 if (!ToBoolean(trapResult)) {
1241 return result.fail(JSMSG_PROXY_SET_RETURNED_FALSE);
1244 // Step 10.
1245 Rooted<PropertyDescriptor> desc(cx);
1246 if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) {
1247 return false;
1250 // Step 11.
1251 if (desc.object()) {
1252 // Step 11a.
1253 if (desc.isDataDescriptor() && !desc.configurable() && !desc.writable()) {
1254 bool same;
1255 if (!SameValue(cx, v, desc.value(), &same)) {
1256 return false;
1258 if (!same) {
1259 return js::Throw(cx, id, JSMSG_CANT_SET_NW_NC);
1263 // Step 11b.
1264 if (desc.isAccessorDescriptor() && !desc.configurable() &&
1265 desc.setterObject() == nullptr) {
1266 return js::Throw(cx, id, JSMSG_CANT_SET_WO_SETTER);
1270 // Step 12.
1271 return result.succeed();
1274 // ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.13 Proxy.[[Call]]
1275 bool ScriptedProxyHandler::call(JSContext* cx, HandleObject proxy,
1276 const CallArgs& args) const {
1277 // Steps 1-3.
1278 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1279 if (!handler) {
1280 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1281 JSMSG_PROXY_REVOKED);
1282 return false;
1285 // Step 4.
1286 RootedObject target(cx, proxy->as<ProxyObject>().target());
1287 MOZ_ASSERT(target);
1288 MOZ_ASSERT(target->isCallable());
1290 // Step 5.
1291 RootedValue trap(cx);
1292 if (!GetProxyTrap(cx, handler, cx->names().apply, &trap)) {
1293 return false;
1296 // Step 6.
1297 if (trap.isUndefined()) {
1298 InvokeArgs iargs(cx);
1299 if (!FillArgumentsFromArraylike(cx, iargs, args)) {
1300 return false;
1303 RootedValue fval(cx, ObjectValue(*target));
1304 return js::Call(cx, fval, args.thisv(), iargs, args.rval());
1307 // Step 7.
1308 RootedObject argArray(cx,
1309 NewDenseCopiedArray(cx, args.length(), args.array()));
1310 if (!argArray) {
1311 return false;
1314 // Step 8.
1315 FixedInvokeArgs<3> iargs(cx);
1317 iargs[0].setObject(*target);
1318 iargs[1].set(args.thisv());
1319 iargs[2].setObject(*argArray);
1321 RootedValue thisv(cx, ObjectValue(*handler));
1322 return js::Call(cx, trap, thisv, iargs, args.rval());
1325 // ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.14 Proxy.[[Construct]]
1326 bool ScriptedProxyHandler::construct(JSContext* cx, HandleObject proxy,
1327 const CallArgs& args) const {
1328 // Steps 1-3.
1329 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1330 if (!handler) {
1331 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1332 JSMSG_PROXY_REVOKED);
1333 return false;
1336 // Step 4.
1337 RootedObject target(cx, proxy->as<ProxyObject>().target());
1338 MOZ_ASSERT(target);
1339 MOZ_ASSERT(target->isConstructor());
1341 // Step 5.
1342 RootedValue trap(cx);
1343 if (!GetProxyTrap(cx, handler, cx->names().construct, &trap)) {
1344 return false;
1347 // Step 6.
1348 if (trap.isUndefined()) {
1349 ConstructArgs cargs(cx);
1350 if (!FillArgumentsFromArraylike(cx, cargs, args)) {
1351 return false;
1354 RootedValue targetv(cx, ObjectValue(*target));
1355 RootedObject obj(cx);
1356 if (!Construct(cx, targetv, cargs, args.newTarget(), &obj)) {
1357 return false;
1360 args.rval().setObject(*obj);
1361 return true;
1364 // Step 7.
1365 RootedObject argArray(cx,
1366 NewDenseCopiedArray(cx, args.length(), args.array()));
1367 if (!argArray) {
1368 return false;
1371 // Steps 8, 10.
1373 FixedInvokeArgs<3> iargs(cx);
1375 iargs[0].setObject(*target);
1376 iargs[1].setObject(*argArray);
1377 iargs[2].set(args.newTarget());
1379 RootedValue thisv(cx, ObjectValue(*handler));
1380 if (!Call(cx, trap, thisv, iargs, args.rval())) {
1381 return false;
1385 // Step 9.
1386 if (!args.rval().isObject()) {
1387 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1388 JSMSG_PROXY_CONSTRUCT_OBJECT);
1389 return false;
1392 return true;
1395 bool ScriptedProxyHandler::nativeCall(JSContext* cx, IsAcceptableThis test,
1396 NativeImpl impl,
1397 const CallArgs& args) const {
1398 ReportIncompatible(cx, args);
1399 return false;
1402 bool ScriptedProxyHandler::hasInstance(JSContext* cx, HandleObject proxy,
1403 MutableHandleValue v, bool* bp) const {
1404 return InstanceofOperator(cx, proxy, v, bp);
1407 bool ScriptedProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy,
1408 ESClass* cls) const {
1409 *cls = ESClass::Other;
1410 return true;
1413 bool ScriptedProxyHandler::isArray(JSContext* cx, HandleObject proxy,
1414 IsArrayAnswer* answer) const {
1415 RootedObject target(cx, proxy->as<ProxyObject>().target());
1416 if (target) {
1417 return JS::IsArray(cx, target, answer);
1420 *answer = IsArrayAnswer::RevokedProxy;
1421 return true;
1424 const char* ScriptedProxyHandler::className(JSContext* cx,
1425 HandleObject proxy) const {
1426 // Right now the caller is not prepared to handle failures.
1427 return BaseProxyHandler::className(cx, proxy);
1430 JSString* ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy,
1431 bool isToSource) const {
1432 // The BaseProxyHandler has the desired behavior: Throw for non-callable,
1433 // otherwise return [native code].
1434 return BaseProxyHandler::fun_toString(cx, proxy, isToSource);
1437 RegExpShared* ScriptedProxyHandler::regexp_toShared(JSContext* cx,
1438 HandleObject proxy) const {
1439 MOZ_CRASH("Should not end up in ScriptedProxyHandler::regexp_toShared");
1442 bool ScriptedProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy,
1443 MutableHandleValue vp) const {
1444 MOZ_CRASH("Should not end up in ScriptedProxyHandler::boxedValue_unbox");
1445 return false;
1448 bool ScriptedProxyHandler::isCallable(JSObject* obj) const {
1449 MOZ_ASSERT(obj->as<ProxyObject>().handler() ==
1450 &ScriptedProxyHandler::singleton);
1451 uint32_t callConstruct = obj->as<ProxyObject>()
1452 .reservedSlot(IS_CALLCONSTRUCT_EXTRA)
1453 .toPrivateUint32();
1454 return !!(callConstruct & IS_CALLABLE);
1457 bool ScriptedProxyHandler::isConstructor(JSObject* obj) const {
1458 MOZ_ASSERT(obj->as<ProxyObject>().handler() ==
1459 &ScriptedProxyHandler::singleton);
1460 uint32_t callConstruct = obj->as<ProxyObject>()
1461 .reservedSlot(IS_CALLCONSTRUCT_EXTRA)
1462 .toPrivateUint32();
1463 return !!(callConstruct & IS_CONSTRUCTOR);
1466 const char ScriptedProxyHandler::family = 0;
1467 const ScriptedProxyHandler ScriptedProxyHandler::singleton;
1469 // ES2021 rev c21b280a2c46e92decf3efeca9e9da35d5b9f622
1470 // Including the changes from: https://github.com/tc39/ecma262/pull/1814
1471 // 9.5.14 ProxyCreate.
1472 static bool ProxyCreate(JSContext* cx, CallArgs& args, const char* callerName) {
1473 if (!args.requireAtLeast(cx, callerName, 2)) {
1474 return false;
1477 // Step 1.
1478 RootedObject target(cx,
1479 RequireObjectArg(cx, "`target`", callerName, args[0]));
1480 if (!target) {
1481 return false;
1484 // Step 2.
1485 RootedObject handler(cx,
1486 RequireObjectArg(cx, "`handler`", callerName, args[1]));
1487 if (!handler) {
1488 return false;
1491 // Steps 3-4, 6.
1492 RootedValue priv(cx, ObjectValue(*target));
1493 JSObject* proxy_ = NewProxyObject(cx, &ScriptedProxyHandler::singleton, priv,
1494 TaggedProto::LazyProto);
1495 if (!proxy_) {
1496 return false;
1499 // Step 7 (reordered).
1500 Rooted<ProxyObject*> proxy(cx, &proxy_->as<ProxyObject>());
1501 proxy->setReservedSlot(ScriptedProxyHandler::HANDLER_EXTRA,
1502 ObjectValue(*handler));
1504 // Step 5.
1505 uint32_t callable =
1506 target->isCallable() ? ScriptedProxyHandler::IS_CALLABLE : 0;
1507 uint32_t constructor =
1508 target->isConstructor() ? ScriptedProxyHandler::IS_CONSTRUCTOR : 0;
1509 proxy->setReservedSlot(ScriptedProxyHandler::IS_CALLCONSTRUCT_EXTRA,
1510 PrivateUint32Value(callable | constructor));
1512 // Step 8.
1513 args.rval().setObject(*proxy);
1514 return true;
1517 bool js::proxy(JSContext* cx, unsigned argc, Value* vp) {
1518 CallArgs args = CallArgsFromVp(argc, vp);
1520 if (!ThrowIfNotConstructing(cx, args, "Proxy")) {
1521 return false;
1524 return ProxyCreate(cx, args, "Proxy");
1527 static bool RevokeProxy(JSContext* cx, unsigned argc, Value* vp) {
1528 CallArgs args = CallArgsFromVp(argc, vp);
1530 RootedFunction func(cx, &args.callee().as<JSFunction>());
1531 RootedObject p(cx, func->getExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT)
1532 .toObjectOrNull());
1534 if (p) {
1535 func->setExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT, NullValue());
1537 MOZ_ASSERT(p->is<ProxyObject>());
1539 p->as<ProxyObject>().setSameCompartmentPrivate(NullValue());
1540 p->as<ProxyObject>().setReservedSlot(ScriptedProxyHandler::HANDLER_EXTRA,
1541 NullValue());
1544 args.rval().setUndefined();
1545 return true;
1548 bool js::proxy_revocable(JSContext* cx, unsigned argc, Value* vp) {
1549 CallArgs args = CallArgsFromVp(argc, vp);
1551 if (!ProxyCreate(cx, args, "Proxy.revocable")) {
1552 return false;
1555 RootedValue proxyVal(cx, args.rval());
1556 MOZ_ASSERT(proxyVal.toObject().is<ProxyObject>());
1558 RootedFunction revoker(
1559 cx, NewNativeFunction(cx, RevokeProxy, 0, nullptr,
1560 gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
1561 if (!revoker) {
1562 return false;
1565 revoker->initExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT, proxyVal);
1567 RootedPlainObject result(cx, NewBuiltinClassInstance<PlainObject>(cx));
1568 if (!result) {
1569 return false;
1572 RootedValue revokeVal(cx, ObjectValue(*revoker));
1573 if (!DefineDataProperty(cx, result, cx->names().proxy, proxyVal) ||
1574 !DefineDataProperty(cx, result, cx->names().revoke, revokeVal)) {
1575 return false;
1578 args.rval().setObject(*result);
1579 return true;