ScriptedProxyHandler.cpp (43420B)
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 "proxy/ScriptedProxyHandler.h" 8 9 #include "mozilla/Maybe.h" 10 11 #include "jsapi.h" 12 13 #include "builtin/Object.h" 14 #include "js/CallAndConstruct.h" // JS::Construct, JS::IsCallable 15 #include "js/CharacterEncoding.h" 16 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 17 #include "js/PropertyDescriptor.h" // JS::FromPropertyDescriptor 18 #include "vm/EqualityOperations.h" // js::SameValue 19 #include "vm/Interpreter.h" // js::Call 20 #include "vm/JSFunction.h" 21 #include "vm/JSObject.h" 22 #include "vm/PlainObject.h" // js::PlainObject 23 24 #include "vm/JSObject-inl.h" 25 #include "vm/NativeObject-inl.h" 26 27 using namespace js; 28 29 using JS::IsArrayAnswer; 30 31 using mozilla::Maybe; 32 33 // ES2022 rev 33fe30f9a6b0dc81826f2f217167a89c025779a0 34 // IsCompatiblePropertyDescriptor. BUT that method just calls 35 // ValidateAndApplyPropertyDescriptor with two additional constant arguments. 36 // Therefore step numbering is from the latter method, and resulting dead code 37 // has been removed. 38 39 // If an exception should be thrown, we will set errorDetails. 40 static bool IsCompatiblePropertyDescriptor( 41 JSContext* cx, bool extensible, Handle<PropertyDescriptor> desc, 42 Handle<Maybe<PropertyDescriptor>> current, const char** errorDetails) { 43 // precondition: we won't set details if checks pass, so it must be null 44 // here. 45 MOZ_ASSERT(*errorDetails == nullptr); 46 47 // Step 2. 48 if (current.isNothing()) { 49 // Step 2.a-b,e. As |O| is always undefined, steps 2.c-d fall away. 50 if (!extensible) { 51 static const char DETAILS_NOT_EXTENSIBLE[] = 52 "proxy can't report an extensible object as non-extensible"; 53 *errorDetails = DETAILS_NOT_EXTENSIBLE; 54 } 55 return true; 56 } 57 58 current->assertComplete(); 59 60 // Step 3. 61 if (!desc.hasValue() && !desc.hasWritable() && !desc.hasGetter() && 62 !desc.hasSetter() && !desc.hasEnumerable() && !desc.hasConfigurable()) { 63 return true; 64 } 65 66 // Step 4. 67 if (!current->configurable()) { 68 // Step 4.a. 69 if (desc.hasConfigurable() && desc.configurable()) { 70 static const char DETAILS_CANT_REPORT_NC_AS_C[] = 71 "proxy can't report an existing non-configurable property as " 72 "configurable"; 73 *errorDetails = DETAILS_CANT_REPORT_NC_AS_C; 74 return true; 75 } 76 77 // Step 4.b. 78 if (desc.hasEnumerable() && desc.enumerable() != current->enumerable()) { 79 static const char DETAILS_ENUM_DIFFERENT[] = 80 "proxy can't report a different 'enumerable' from target when target " 81 "is not configurable"; 82 *errorDetails = DETAILS_ENUM_DIFFERENT; 83 return true; 84 } 85 } 86 87 // Step 5. 88 if (desc.isGenericDescriptor()) { 89 return true; 90 } 91 92 // Step 6. 93 if (current->isDataDescriptor() != desc.isDataDescriptor()) { 94 // Steps 6.a., 10. As |O| is always undefined, steps 6.b-c fall away. 95 if (!current->configurable()) { 96 static const char DETAILS_CURRENT_NC_DIFF_TYPE[] = 97 "proxy can't report a different descriptor type when target is not " 98 "configurable"; 99 *errorDetails = DETAILS_CURRENT_NC_DIFF_TYPE; 100 } 101 return true; 102 } 103 104 // Step 7. 105 if (current->isDataDescriptor()) { 106 MOZ_ASSERT(desc.isDataDescriptor()); // by step 6 107 // Step 7.a. 108 if (!current->configurable() && !current->writable()) { 109 // Step 7.a.i. 110 if (desc.hasWritable() && desc.writable()) { 111 static const char DETAILS_CANT_REPORT_NW_AS_W[] = 112 "proxy can't report a non-configurable, non-writable property as " 113 "writable"; 114 *errorDetails = DETAILS_CANT_REPORT_NW_AS_W; 115 return true; 116 } 117 118 // Step 7.a.ii. 119 if (desc.hasValue()) { 120 bool same; 121 if (!SameValue(cx, desc.value(), current->value(), &same)) { 122 return false; 123 } 124 if (!same) { 125 static const char DETAILS_DIFFERENT_VALUE[] = 126 "proxy must report the same value for the non-writable, " 127 "non-configurable property"; 128 *errorDetails = DETAILS_DIFFERENT_VALUE; 129 return true; 130 } 131 } 132 } 133 134 // Step 7.a.ii, 10. 135 return true; 136 } 137 138 // Step 8. 139 140 // Step 8.a. 141 MOZ_ASSERT(current->isAccessorDescriptor()); // by step 7 142 MOZ_ASSERT(desc.isAccessorDescriptor()); // by step 6 143 144 // Step 8.b. 145 if (current->configurable()) { 146 return true; 147 } 148 // Steps 8.b.i-ii. 149 if (desc.hasSetter() && desc.setter() != current->setter()) { 150 static const char DETAILS_SETTERS_DIFFERENT[] = 151 "proxy can't report different setters for a currently non-configurable " 152 "property"; 153 *errorDetails = DETAILS_SETTERS_DIFFERENT; 154 } else if (desc.hasGetter() && desc.getter() != current->getter()) { 155 static const char DETAILS_GETTERS_DIFFERENT[] = 156 "proxy can't report different getters for a currently non-configurable " 157 "property"; 158 *errorDetails = DETAILS_GETTERS_DIFFERENT; 159 } 160 161 // Step 9. 162 // |O| is always undefined. 163 164 // Step 10. 165 return true; 166 } 167 168 // Get the [[ProxyHandler]] of a scripted proxy. 169 /* static */ 170 JSObject* ScriptedProxyHandler::handlerObject(const JSObject* proxy) { 171 MOZ_ASSERT(proxy->as<ProxyObject>().handler() == 172 &ScriptedProxyHandler::singleton); 173 return proxy->as<ProxyObject>() 174 .reservedSlot(ScriptedProxyHandler::HANDLER_EXTRA) 175 .toObjectOrNull(); 176 } 177 178 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 179 // 7.3.9 GetMethod, reimplemented for proxy handler trap-getting to produce 180 // better error messages. 181 static bool GetProxyTrap(JSContext* cx, HandleObject handler, 182 Handle<PropertyName*> name, MutableHandleValue func) { 183 // Steps 2, 5. 184 if (!GetProperty(cx, handler, handler, name, func)) { 185 return false; 186 } 187 188 // Step 3. 189 if (func.isUndefined()) { 190 return true; 191 } 192 193 if (func.isNull()) { 194 func.setUndefined(); 195 return true; 196 } 197 198 // Step 4. 199 if (!IsCallable(func)) { 200 UniqueChars bytes = EncodeAscii(cx, name); 201 if (!bytes) { 202 return false; 203 } 204 205 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_TRAP, 206 bytes.get()); 207 return false; 208 } 209 210 return true; 211 } 212 213 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 214 // 9.5.1 Proxy.[[GetPrototypeOf]]. 215 bool ScriptedProxyHandler::getPrototype(JSContext* cx, HandleObject proxy, 216 MutableHandleObject protop) const { 217 // Steps 1-3. 218 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 219 if (!handler) { 220 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 221 JSMSG_PROXY_REVOKED); 222 return false; 223 } 224 225 // Step 4. 226 RootedObject target(cx, proxy->as<ProxyObject>().target()); 227 MOZ_ASSERT(target); 228 229 // Step 5. 230 RootedValue trap(cx); 231 if (!GetProxyTrap(cx, handler, cx->names().getPrototypeOf, &trap)) { 232 return false; 233 } 234 235 // Step 6. 236 if (trap.isUndefined()) { 237 return GetPrototype(cx, target, protop); 238 } 239 240 // Step 7. 241 RootedValue handlerProto(cx); 242 { 243 FixedInvokeArgs<1> args(cx); 244 245 args[0].setObject(*target); 246 247 handlerProto.setObject(*handler); 248 249 if (!js::Call(cx, trap, handlerProto, args, &handlerProto)) { 250 return false; 251 } 252 } 253 254 // Step 8. 255 if (!handlerProto.isObjectOrNull()) { 256 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 257 JSMSG_BAD_GETPROTOTYPEOF_TRAP_RETURN); 258 return false; 259 } 260 261 // Step 9. 262 bool extensibleTarget; 263 if (!IsExtensible(cx, target, &extensibleTarget)) { 264 return false; 265 } 266 267 // Step 10. 268 if (extensibleTarget) { 269 protop.set(handlerProto.toObjectOrNull()); 270 return true; 271 } 272 273 // Step 11. 274 RootedObject targetProto(cx); 275 if (!GetPrototype(cx, target, &targetProto)) { 276 return false; 277 } 278 279 // Step 12. 280 if (handlerProto.toObjectOrNull() != targetProto) { 281 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 282 JSMSG_INCONSISTENT_GETPROTOTYPEOF_TRAP); 283 return false; 284 } 285 286 // Step 13. 287 protop.set(handlerProto.toObjectOrNull()); 288 return true; 289 } 290 291 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 292 // 9.5.2 Proxy.[[SetPrototypeOf]]. 293 bool ScriptedProxyHandler::setPrototype(JSContext* cx, HandleObject proxy, 294 HandleObject proto, 295 ObjectOpResult& result) const { 296 // Steps 1-4. 297 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 298 if (!handler) { 299 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 300 JSMSG_PROXY_REVOKED); 301 return false; 302 } 303 304 // Step 5. 305 RootedObject target(cx, proxy->as<ProxyObject>().target()); 306 MOZ_ASSERT(target); 307 308 // Step 6. 309 RootedValue trap(cx); 310 if (!GetProxyTrap(cx, handler, cx->names().setPrototypeOf, &trap)) { 311 return false; 312 } 313 314 // Step 7. 315 if (trap.isUndefined()) { 316 return SetPrototype(cx, target, proto, result); 317 } 318 319 // Step 8. 320 bool booleanTrapResult; 321 { 322 FixedInvokeArgs<2> args(cx); 323 324 args[0].setObject(*target); 325 args[1].setObjectOrNull(proto); 326 327 RootedValue hval(cx, ObjectValue(*handler)); 328 if (!js::Call(cx, trap, hval, args, &hval)) { 329 return false; 330 } 331 332 booleanTrapResult = ToBoolean(hval); 333 } 334 335 // Step 9. 336 if (!booleanTrapResult) { 337 return result.fail(JSMSG_PROXY_SETPROTOTYPEOF_RETURNED_FALSE); 338 } 339 340 // Step 10. 341 bool extensibleTarget; 342 if (!IsExtensible(cx, target, &extensibleTarget)) { 343 return false; 344 } 345 346 // Step 11. 347 if (extensibleTarget) { 348 return result.succeed(); 349 } 350 351 // Step 12. 352 RootedObject targetProto(cx); 353 if (!GetPrototype(cx, target, &targetProto)) { 354 return false; 355 } 356 357 // Step 13. 358 if (proto != targetProto) { 359 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 360 JSMSG_INCONSISTENT_SETPROTOTYPEOF_TRAP); 361 return false; 362 } 363 364 // Step 14. 365 return result.succeed(); 366 } 367 368 bool ScriptedProxyHandler::getPrototypeIfOrdinary( 369 JSContext* cx, HandleObject proxy, bool* isOrdinary, 370 MutableHandleObject protop) const { 371 *isOrdinary = false; 372 return true; 373 } 374 375 // Not yet part of ES6, but hopefully to be standards-tracked -- and needed to 376 // handle revoked proxies in any event. 377 bool ScriptedProxyHandler::setImmutablePrototype(JSContext* cx, 378 HandleObject proxy, 379 bool* succeeded) const { 380 RootedObject target(cx, proxy->as<ProxyObject>().target()); 381 if (!target) { 382 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 383 JSMSG_PROXY_REVOKED); 384 return false; 385 } 386 387 return SetImmutablePrototype(cx, target, succeeded); 388 } 389 390 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 391 // 9.5.4 Proxy.[[PreventExtensions]]() 392 bool ScriptedProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy, 393 ObjectOpResult& result) const { 394 // Steps 1-3. 395 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 396 if (!handler) { 397 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 398 JSMSG_PROXY_REVOKED); 399 return false; 400 } 401 402 // Step 4. 403 RootedObject target(cx, proxy->as<ProxyObject>().target()); 404 MOZ_ASSERT(target); 405 406 // Step 5. 407 RootedValue trap(cx); 408 if (!GetProxyTrap(cx, handler, cx->names().preventExtensions, &trap)) { 409 return false; 410 } 411 412 // Step 6. 413 if (trap.isUndefined()) { 414 return PreventExtensions(cx, target, result); 415 } 416 417 // Step 7. 418 bool booleanTrapResult; 419 { 420 RootedValue arg(cx, ObjectValue(*target)); 421 RootedValue trapResult(cx); 422 if (!Call(cx, trap, handler, arg, &trapResult)) { 423 return false; 424 } 425 426 booleanTrapResult = ToBoolean(trapResult); 427 } 428 429 // Step 8. 430 if (booleanTrapResult) { 431 // Step 8a. 432 bool targetIsExtensible; 433 if (!IsExtensible(cx, target, &targetIsExtensible)) { 434 return false; 435 } 436 437 if (targetIsExtensible) { 438 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 439 JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE); 440 return false; 441 } 442 443 // Step 9. 444 return result.succeed(); 445 } 446 447 // Also step 9. 448 return result.fail(JSMSG_PROXY_PREVENTEXTENSIONS_RETURNED_FALSE); 449 } 450 451 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 452 // 9.5.3 Proxy.[[IsExtensible]]() 453 bool ScriptedProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, 454 bool* extensible) const { 455 // Steps 1-3. 456 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 457 if (!handler) { 458 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 459 JSMSG_PROXY_REVOKED); 460 return false; 461 } 462 463 // Step 4. 464 RootedObject target(cx, proxy->as<ProxyObject>().target()); 465 MOZ_ASSERT(target); 466 467 // Step 5. 468 RootedValue trap(cx); 469 if (!GetProxyTrap(cx, handler, cx->names().isExtensible, &trap)) { 470 return false; 471 } 472 473 // Step 6. 474 if (trap.isUndefined()) { 475 return IsExtensible(cx, target, extensible); 476 } 477 478 // Step 7. 479 bool booleanTrapResult; 480 { 481 RootedValue arg(cx, ObjectValue(*target)); 482 RootedValue trapResult(cx); 483 if (!Call(cx, trap, handler, arg, &trapResult)) { 484 return false; 485 } 486 487 booleanTrapResult = ToBoolean(trapResult); 488 } 489 490 // Steps 8. 491 bool targetResult; 492 if (!IsExtensible(cx, target, &targetResult)) { 493 return false; 494 } 495 496 // Step 9. 497 if (targetResult != booleanTrapResult) { 498 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 499 JSMSG_PROXY_EXTENSIBILITY); 500 return false; 501 } 502 503 // Step 10. 504 *extensible = booleanTrapResult; 505 return true; 506 } 507 508 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 509 // 9.5.5 Proxy.[[GetOwnProperty]](P) 510 bool ScriptedProxyHandler::getOwnPropertyDescriptor( 511 JSContext* cx, HandleObject proxy, HandleId id, 512 MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const { 513 // Steps 2-4. 514 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 515 if (!handler) { 516 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 517 JSMSG_PROXY_REVOKED); 518 return false; 519 } 520 521 // Step 5. 522 RootedObject target(cx, proxy->as<ProxyObject>().target()); 523 MOZ_ASSERT(target); 524 525 // Step 6. 526 RootedValue trap(cx); 527 if (!GetProxyTrap(cx, handler, cx->names().getOwnPropertyDescriptor, &trap)) { 528 return false; 529 } 530 531 // Step 7. 532 if (trap.isUndefined()) { 533 return GetOwnPropertyDescriptor(cx, target, id, desc); 534 } 535 536 // Step 8. 537 RootedValue propKey(cx); 538 if (!IdToStringOrSymbol(cx, id, &propKey)) { 539 return false; 540 } 541 542 RootedValue trapResult(cx); 543 RootedValue targetVal(cx, ObjectValue(*target)); 544 if (!Call(cx, trap, handler, targetVal, propKey, &trapResult)) { 545 return false; 546 } 547 548 // Step 9. 549 if (!trapResult.isUndefined() && !trapResult.isObject()) { 550 return js::Throw(cx, id, JSMSG_PROXY_GETOWN_OBJORUNDEF); 551 } 552 553 // Step 10. 554 Rooted<Maybe<PropertyDescriptor>> targetDesc(cx); 555 if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) { 556 return false; 557 } 558 559 // Step 11. 560 if (trapResult.isUndefined()) { 561 // Step 11a. 562 if (targetDesc.isNothing()) { 563 desc.reset(); 564 return true; 565 } 566 567 // Step 11b. 568 if (!targetDesc->configurable()) { 569 return js::Throw(cx, id, JSMSG_CANT_REPORT_NC_AS_NE); 570 } 571 572 // Steps 11c-d. 573 bool extensibleTarget; 574 if (!IsExtensible(cx, target, &extensibleTarget)) { 575 return false; 576 } 577 578 // Step 11e. 579 if (!extensibleTarget) { 580 return js::Throw(cx, id, JSMSG_CANT_REPORT_E_AS_NE); 581 } 582 583 // Step 11f. 584 desc.reset(); 585 return true; 586 } 587 588 // Step 12. 589 bool extensibleTarget; 590 if (!IsExtensible(cx, target, &extensibleTarget)) { 591 return false; 592 } 593 594 // Step 13. 595 Rooted<PropertyDescriptor> resultDesc(cx); 596 if (!ToPropertyDescriptor(cx, trapResult, true, &resultDesc)) { 597 return false; 598 } 599 600 // Step 14. 601 CompletePropertyDescriptor(&resultDesc); 602 603 // Step 15. 604 const char* errorDetails = nullptr; 605 if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, resultDesc, 606 targetDesc, &errorDetails)) 607 return false; 608 609 // Step 16. 610 if (errorDetails) { 611 return js::Throw(cx, id, JSMSG_CANT_REPORT_INVALID, errorDetails); 612 } 613 614 // Step 17. 615 if (!resultDesc.configurable()) { 616 if (targetDesc.isNothing()) { 617 return js::Throw(cx, id, JSMSG_CANT_REPORT_NE_AS_NC); 618 } 619 620 if (targetDesc->configurable()) { 621 return js::Throw(cx, id, JSMSG_CANT_REPORT_C_AS_NC); 622 } 623 624 if (resultDesc.hasWritable() && !resultDesc.writable()) { 625 if (targetDesc->writable()) { 626 return js::Throw(cx, id, JSMSG_CANT_REPORT_W_AS_NW); 627 } 628 } 629 } 630 631 // Step 18. 632 desc.set(mozilla::Some(resultDesc.get())); 633 return true; 634 } 635 636 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 637 // 9.5.6 Proxy.[[DefineOwnProperty]](P, Desc) 638 bool ScriptedProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, 639 HandleId id, 640 Handle<PropertyDescriptor> desc, 641 ObjectOpResult& result) const { 642 // Steps 2-4. 643 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 644 if (!handler) { 645 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 646 JSMSG_PROXY_REVOKED); 647 return false; 648 } 649 650 // Step 5. 651 RootedObject target(cx, proxy->as<ProxyObject>().target()); 652 MOZ_ASSERT(target); 653 654 // Step 6. 655 RootedValue trap(cx); 656 if (!GetProxyTrap(cx, handler, cx->names().defineProperty, &trap)) { 657 return false; 658 } 659 660 // Step 7. 661 if (trap.isUndefined()) { 662 return DefineProperty(cx, target, id, desc, result); 663 } 664 665 // Step 8. 666 RootedValue descObj(cx); 667 if (!FromPropertyDescriptorToObject(cx, desc, &descObj)) { 668 return false; 669 } 670 671 // Step 9. 672 RootedValue propKey(cx); 673 if (!IdToStringOrSymbol(cx, id, &propKey)) { 674 return false; 675 } 676 677 RootedValue trapResult(cx); 678 { 679 FixedInvokeArgs<3> args(cx); 680 681 args[0].setObject(*target); 682 args[1].set(propKey); 683 args[2].set(descObj); 684 685 RootedValue thisv(cx, ObjectValue(*handler)); 686 if (!Call(cx, trap, thisv, args, &trapResult)) { 687 return false; 688 } 689 } 690 691 // Step 10. 692 if (!ToBoolean(trapResult)) { 693 return result.fail(JSMSG_PROXY_DEFINE_RETURNED_FALSE); 694 } 695 696 // Step 11. 697 Rooted<Maybe<PropertyDescriptor>> targetDesc(cx); 698 if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) { 699 return false; 700 } 701 702 // Step 12. 703 bool extensibleTarget; 704 if (!IsExtensible(cx, target, &extensibleTarget)) { 705 return false; 706 } 707 708 // Steps 13-14. 709 bool settingConfigFalse = desc.hasConfigurable() && !desc.configurable(); 710 711 // Steps 15-16. 712 if (targetDesc.isNothing()) { 713 // Step 15a. 714 if (!extensibleTarget) { 715 return js::Throw(cx, id, JSMSG_CANT_DEFINE_NEW); 716 } 717 718 // Step 15b. 719 if (settingConfigFalse) { 720 return js::Throw(cx, id, JSMSG_CANT_DEFINE_NE_AS_NC); 721 } 722 } else { 723 // Step 16a. 724 const char* errorDetails = nullptr; 725 if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, desc, targetDesc, 726 &errorDetails)) 727 return false; 728 729 if (errorDetails) { 730 return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID, errorDetails); 731 } 732 733 // Step 16b. 734 if (settingConfigFalse && targetDesc->configurable()) { 735 static const char DETAILS_CANT_REPORT_C_AS_NC[] = 736 "proxy can't define an existing configurable property as " 737 "non-configurable"; 738 return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID, 739 DETAILS_CANT_REPORT_C_AS_NC); 740 } 741 742 if (targetDesc->isDataDescriptor() && !targetDesc->configurable() && 743 targetDesc->writable()) { 744 if (desc.hasWritable() && !desc.writable()) { 745 static const char DETAILS_CANT_DEFINE_NW[] = 746 "proxy can't define an existing non-configurable writable property " 747 "as non-writable"; 748 return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID, 749 DETAILS_CANT_DEFINE_NW); 750 } 751 } 752 } 753 754 // Step 17. 755 return result.succeed(); 756 } 757 758 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 759 // 7.3.17 CreateListFromArrayLike with elementTypes fixed to symbol/string. 760 static bool CreateFilteredListFromArrayLike(JSContext* cx, HandleValue v, 761 MutableHandleIdVector props) { 762 // Step 2. 763 RootedObject obj(cx, RequireObject(cx, JSMSG_OBJECT_REQUIRED_RET_OWNKEYS, 764 JSDVG_IGNORE_STACK, v)); 765 if (!obj) { 766 return false; 767 } 768 769 // Step 3. 770 uint64_t len; 771 if (!GetLengthProperty(cx, obj, &len)) { 772 return false; 773 } 774 775 // Steps 4-6. 776 RootedValue next(cx); 777 RootedId id(cx); 778 uint64_t index = 0; 779 while (index < len) { 780 // Steps 6a-b. 781 if (!GetElementLargeIndex(cx, obj, obj, index, &next)) { 782 return false; 783 } 784 785 // Step 6c. 786 if (!next.isString() && !next.isSymbol()) { 787 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 788 JSMSG_OWNKEYS_STR_SYM); 789 return false; 790 } 791 792 if (!PrimitiveValueToId<CanGC>(cx, next, &id)) { 793 return false; 794 } 795 796 // Step 6d. 797 if (!props.append(id)) { 798 return false; 799 } 800 801 // Step 6e. 802 index++; 803 } 804 805 // Step 7. 806 return true; 807 } 808 809 // ES2018 draft rev aab1ea3bd4d03c85d6f4a91503b4169346ab7271 810 // 9.5.11 Proxy.[[OwnPropertyKeys]]() 811 bool ScriptedProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, 812 MutableHandleIdVector props) const { 813 // Steps 1-3. 814 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 815 if (!handler) { 816 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 817 JSMSG_PROXY_REVOKED); 818 return false; 819 } 820 821 // Step 4. 822 RootedObject target(cx, proxy->as<ProxyObject>().target()); 823 MOZ_ASSERT(target); 824 825 // Step 5. 826 RootedValue trap(cx); 827 if (!GetProxyTrap(cx, handler, cx->names().ownKeys, &trap)) { 828 return false; 829 } 830 831 // Step 6. 832 if (trap.isUndefined()) { 833 return GetPropertyKeys( 834 cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props); 835 } 836 837 // Step 7. 838 RootedValue trapResultArray(cx); 839 RootedValue targetVal(cx, ObjectValue(*target)); 840 if (!Call(cx, trap, handler, targetVal, &trapResultArray)) { 841 return false; 842 } 843 844 // Step 8. 845 RootedIdVector trapResult(cx); 846 if (!CreateFilteredListFromArrayLike(cx, trapResultArray, &trapResult)) { 847 return false; 848 } 849 850 // Steps 9, 18. 851 Rooted<GCHashSet<jsid>> uncheckedResultKeys( 852 cx, GCHashSet<jsid>(cx, trapResult.length())); 853 854 for (size_t i = 0, len = trapResult.length(); i < len; i++) { 855 MOZ_ASSERT(!trapResult[i].isVoid()); 856 857 auto ptr = uncheckedResultKeys.lookupForAdd(trapResult[i]); 858 if (ptr) { 859 return js::Throw(cx, trapResult[i], JSMSG_OWNKEYS_DUPLICATE); 860 } 861 862 if (!uncheckedResultKeys.add(ptr, trapResult[i])) { 863 return false; 864 } 865 } 866 867 // Step 10. 868 bool extensibleTarget; 869 if (!IsExtensible(cx, target, &extensibleTarget)) { 870 return false; 871 } 872 873 // Steps 11-13. 874 RootedIdVector targetKeys(cx); 875 if (!GetPropertyKeys(cx, target, 876 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, 877 &targetKeys)) { 878 return false; 879 } 880 881 // Steps 14-15. 882 RootedIdVector targetConfigurableKeys(cx); 883 RootedIdVector targetNonconfigurableKeys(cx); 884 885 // Step 16. 886 Rooted<Maybe<PropertyDescriptor>> desc(cx); 887 for (size_t i = 0; i < targetKeys.length(); ++i) { 888 // Step 16.a. 889 if (!GetOwnPropertyDescriptor(cx, target, targetKeys[i], &desc)) { 890 return false; 891 } 892 893 // Steps 16.b-c. 894 if (desc.isSome() && !desc->configurable()) { 895 if (!targetNonconfigurableKeys.append(targetKeys[i])) { 896 return false; 897 } 898 } else { 899 if (!targetConfigurableKeys.append(targetKeys[i])) { 900 return false; 901 } 902 } 903 } 904 905 // Step 17. 906 if (extensibleTarget && targetNonconfigurableKeys.empty()) { 907 return props.appendAll(std::move(trapResult)); 908 } 909 910 // Step 19. 911 for (size_t i = 0; i < targetNonconfigurableKeys.length(); ++i) { 912 MOZ_ASSERT(!targetNonconfigurableKeys[i].isVoid()); 913 914 auto ptr = uncheckedResultKeys.lookup(targetNonconfigurableKeys[i]); 915 916 // Step 19.a. 917 if (!ptr) { 918 return js::Throw(cx, targetNonconfigurableKeys[i], JSMSG_CANT_SKIP_NC); 919 } 920 921 // Step 19.b. 922 uncheckedResultKeys.remove(ptr); 923 } 924 925 // Step 20. 926 if (extensibleTarget) { 927 return props.appendAll(std::move(trapResult)); 928 } 929 930 // Step 21. 931 for (size_t i = 0; i < targetConfigurableKeys.length(); ++i) { 932 MOZ_ASSERT(!targetConfigurableKeys[i].isVoid()); 933 934 auto ptr = uncheckedResultKeys.lookup(targetConfigurableKeys[i]); 935 936 // Step 21.a. 937 if (!ptr) { 938 return js::Throw(cx, targetConfigurableKeys[i], 939 JSMSG_CANT_REPORT_E_AS_NE); 940 } 941 942 // Step 21.b. 943 uncheckedResultKeys.remove(ptr); 944 } 945 946 // Step 22. 947 if (!uncheckedResultKeys.empty()) { 948 RootedId id(cx, uncheckedResultKeys.all().front()); 949 return js::Throw(cx, id, JSMSG_CANT_REPORT_NEW); 950 } 951 952 // Step 23. 953 return props.appendAll(std::move(trapResult)); 954 } 955 956 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 957 // 9.5.10 Proxy.[[Delete]](P) 958 bool ScriptedProxyHandler::delete_(JSContext* cx, HandleObject proxy, 959 HandleId id, ObjectOpResult& result) const { 960 // Steps 2-4. 961 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 962 if (!handler) { 963 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 964 JSMSG_PROXY_REVOKED); 965 return false; 966 } 967 968 // Step 5. 969 RootedObject target(cx, proxy->as<ProxyObject>().target()); 970 MOZ_ASSERT(target); 971 972 // Step 6. 973 RootedValue trap(cx); 974 if (!GetProxyTrap(cx, handler, cx->names().deleteProperty, &trap)) { 975 return false; 976 } 977 978 // Step 7. 979 if (trap.isUndefined()) { 980 return DeleteProperty(cx, target, id, result); 981 } 982 983 // Step 8. 984 bool booleanTrapResult; 985 { 986 RootedValue value(cx); 987 if (!IdToStringOrSymbol(cx, id, &value)) { 988 return false; 989 } 990 991 RootedValue targetVal(cx, ObjectValue(*target)); 992 RootedValue trapResult(cx); 993 if (!Call(cx, trap, handler, targetVal, value, &trapResult)) { 994 return false; 995 } 996 997 booleanTrapResult = ToBoolean(trapResult); 998 } 999 1000 // Step 9. 1001 if (!booleanTrapResult) { 1002 return result.fail(JSMSG_PROXY_DELETE_RETURNED_FALSE); 1003 } 1004 1005 // Step 10. 1006 Rooted<Maybe<PropertyDescriptor>> desc(cx); 1007 if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) { 1008 return false; 1009 } 1010 1011 // Step 11. 1012 if (desc.isNothing()) { 1013 return result.succeed(); 1014 } 1015 1016 // Step 12. 1017 if (!desc->configurable()) { 1018 return Throw(cx, id, JSMSG_CANT_DELETE); 1019 } 1020 1021 bool extensible; 1022 if (!IsExtensible(cx, target, &extensible)) { 1023 return false; 1024 } 1025 1026 if (!extensible) { 1027 return Throw(cx, id, JSMSG_CANT_DELETE_NON_EXTENSIBLE); 1028 } 1029 1030 // Step 13. 1031 return result.succeed(); 1032 } 1033 1034 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 1035 // 9.5.7 Proxy.[[HasProperty]](P) 1036 bool ScriptedProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, 1037 bool* bp) const { 1038 // Steps 2-4. 1039 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 1040 if (!handler) { 1041 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1042 JSMSG_PROXY_REVOKED); 1043 return false; 1044 } 1045 1046 // Step 5. 1047 RootedObject target(cx, proxy->as<ProxyObject>().target()); 1048 MOZ_ASSERT(target); 1049 1050 // Step 6. 1051 RootedValue trap(cx); 1052 if (!GetProxyTrap(cx, handler, cx->names().has, &trap)) { 1053 return false; 1054 } 1055 1056 // Step 7. 1057 if (trap.isUndefined()) { 1058 return HasProperty(cx, target, id, bp); 1059 } 1060 1061 // Step 8. 1062 RootedValue value(cx); 1063 if (!IdToStringOrSymbol(cx, id, &value)) { 1064 return false; 1065 } 1066 1067 RootedValue trapResult(cx); 1068 RootedValue targetVal(cx, ObjectValue(*target)); 1069 if (!Call(cx, trap, handler, targetVal, value, &trapResult)) { 1070 return false; 1071 } 1072 1073 bool booleanTrapResult = ToBoolean(trapResult); 1074 1075 // Step 9. 1076 if (!booleanTrapResult) { 1077 // Step 9a. 1078 Rooted<Maybe<PropertyDescriptor>> desc(cx); 1079 if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) { 1080 return false; 1081 } 1082 1083 // Step 9b. 1084 if (desc.isSome()) { 1085 // Step 9b(i). 1086 if (!desc->configurable()) { 1087 return js::Throw(cx, id, JSMSG_CANT_REPORT_NC_AS_NE); 1088 } 1089 1090 // Step 9b(ii). 1091 bool extensible; 1092 if (!IsExtensible(cx, target, &extensible)) { 1093 return false; 1094 } 1095 1096 // Step 9b(iii). 1097 if (!extensible) { 1098 return js::Throw(cx, id, JSMSG_CANT_REPORT_E_AS_NE); 1099 } 1100 } 1101 } 1102 1103 // Step 10. 1104 *bp = booleanTrapResult; 1105 return true; 1106 } 1107 1108 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 1109 // 9.5.8 Proxy.[[GetP]](P, Receiver) 1110 bool ScriptedProxyHandler::get(JSContext* cx, HandleObject proxy, 1111 HandleValue receiver, HandleId id, 1112 MutableHandleValue vp) const { 1113 // Steps 2-4. 1114 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 1115 if (!handler) { 1116 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1117 JSMSG_PROXY_REVOKED); 1118 return false; 1119 } 1120 1121 // Step 5. 1122 RootedObject target(cx, proxy->as<ProxyObject>().target()); 1123 MOZ_ASSERT(target); 1124 1125 // Steps 6. 1126 RootedValue trap(cx); 1127 if (!GetProxyTrap(cx, handler, cx->names().get, &trap)) { 1128 return false; 1129 } 1130 1131 // Step 7. 1132 if (trap.isUndefined()) { 1133 return GetProperty(cx, target, receiver, id, vp); 1134 } 1135 1136 // Step 8. 1137 RootedValue value(cx); 1138 if (!IdToStringOrSymbol(cx, id, &value)) { 1139 return false; 1140 } 1141 1142 RootedValue trapResult(cx); 1143 { 1144 FixedInvokeArgs<3> args(cx); 1145 1146 args[0].setObject(*target); 1147 args[1].set(value); 1148 args[2].set(receiver); 1149 1150 RootedValue thisv(cx, ObjectValue(*handler)); 1151 if (!Call(cx, trap, thisv, args, &trapResult)) { 1152 return false; 1153 } 1154 } 1155 1156 // Steps 9 and 10. 1157 GetTrapValidationResult validation = 1158 checkGetTrapResult(cx, target, id, trapResult); 1159 if (validation != GetTrapValidationResult::OK) { 1160 reportGetTrapValidationError(cx, id, validation); 1161 return false; 1162 } 1163 1164 // Step 11. 1165 vp.set(trapResult); 1166 return true; 1167 } 1168 1169 void ScriptedProxyHandler::reportGetTrapValidationError( 1170 JSContext* cx, HandleId id, GetTrapValidationResult validation) { 1171 switch (validation) { 1172 case GetTrapValidationResult::MustReportSameValue: 1173 js::Throw(cx, id, JSMSG_MUST_REPORT_SAME_VALUE); 1174 return; 1175 case GetTrapValidationResult::MustReportUndefined: 1176 js::Throw(cx, id, JSMSG_MUST_REPORT_SAME_VALUE); 1177 return; 1178 case GetTrapValidationResult::Exception: 1179 return; 1180 case GetTrapValidationResult::OK: 1181 MOZ_CRASH("unreachable"); 1182 } 1183 } 1184 1185 ScriptedProxyHandler::GetTrapValidationResult 1186 ScriptedProxyHandler::checkGetTrapResult(JSContext* cx, HandleObject target, 1187 HandleId id, HandleValue trapResult) { 1188 // Step 9. 1189 Rooted<Maybe<PropertyDescriptor>> desc(cx); 1190 if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) { 1191 return GetTrapValidationResult::Exception; 1192 } 1193 1194 // Step 10. 1195 if (desc.isSome()) { 1196 // Step 10a. 1197 if (desc->isDataDescriptor() && !desc->configurable() && 1198 !desc->writable()) { 1199 bool same; 1200 if (!SameValue(cx, trapResult, desc->value(), &same)) { 1201 return GetTrapValidationResult::Exception; 1202 } 1203 1204 if (!same) { 1205 return GetTrapValidationResult::MustReportSameValue; 1206 } 1207 } 1208 1209 // Step 10b. 1210 if (desc->isAccessorDescriptor() && !desc->configurable() && 1211 (desc->getter() == nullptr) && !trapResult.isUndefined()) { 1212 return GetTrapValidationResult::MustReportUndefined; 1213 } 1214 } 1215 1216 return GetTrapValidationResult::OK; 1217 } 1218 1219 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 1220 // 9.5.9 Proxy.[[Set]](P, V, Receiver) 1221 bool ScriptedProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id, 1222 HandleValue v, HandleValue receiver, 1223 ObjectOpResult& result) const { 1224 // Steps 2-4. 1225 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 1226 if (!handler) { 1227 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1228 JSMSG_PROXY_REVOKED); 1229 return false; 1230 } 1231 1232 // Step 5. 1233 RootedObject target(cx, proxy->as<ProxyObject>().target()); 1234 MOZ_ASSERT(target); 1235 1236 // Step 6. 1237 RootedValue trap(cx); 1238 if (!GetProxyTrap(cx, handler, cx->names().set, &trap)) { 1239 return false; 1240 } 1241 1242 // Step 7. 1243 if (trap.isUndefined()) { 1244 return SetProperty(cx, target, id, v, receiver, result); 1245 } 1246 1247 // Step 8. 1248 RootedValue value(cx); 1249 if (!IdToStringOrSymbol(cx, id, &value)) { 1250 return false; 1251 } 1252 1253 RootedValue trapResult(cx); 1254 { 1255 FixedInvokeArgs<4> args(cx); 1256 1257 args[0].setObject(*target); 1258 args[1].set(value); 1259 args[2].set(v); 1260 args[3].set(receiver); 1261 1262 RootedValue thisv(cx, ObjectValue(*handler)); 1263 if (!Call(cx, trap, thisv, args, &trapResult)) { 1264 return false; 1265 } 1266 } 1267 1268 // Step 9. 1269 if (!ToBoolean(trapResult)) { 1270 return result.fail(JSMSG_PROXY_SET_RETURNED_FALSE); 1271 } 1272 1273 // Step 10. 1274 Rooted<Maybe<PropertyDescriptor>> desc(cx); 1275 if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) { 1276 return false; 1277 } 1278 1279 // Step 11. 1280 if (desc.isSome()) { 1281 // Step 11a. 1282 if (desc->isDataDescriptor() && !desc->configurable() && 1283 !desc->writable()) { 1284 bool same; 1285 if (!SameValue(cx, v, desc->value(), &same)) { 1286 return false; 1287 } 1288 if (!same) { 1289 return js::Throw(cx, id, JSMSG_CANT_SET_NW_NC); 1290 } 1291 } 1292 1293 // Step 11b. 1294 if (desc->isAccessorDescriptor() && !desc->configurable() && 1295 desc->setter() == nullptr) { 1296 return js::Throw(cx, id, JSMSG_CANT_SET_WO_SETTER); 1297 } 1298 } 1299 1300 // Step 12. 1301 return result.succeed(); 1302 } 1303 1304 // ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.13 Proxy.[[Call]] 1305 bool ScriptedProxyHandler::call(JSContext* cx, HandleObject proxy, 1306 const CallArgs& args) const { 1307 // Steps 1-3. 1308 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 1309 if (!handler) { 1310 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1311 JSMSG_PROXY_REVOKED); 1312 return false; 1313 } 1314 1315 // Step 4. 1316 RootedObject target(cx, proxy->as<ProxyObject>().target()); 1317 MOZ_ASSERT(target); 1318 MOZ_ASSERT(target->isCallable()); 1319 1320 // Step 5. 1321 RootedValue trap(cx); 1322 if (!GetProxyTrap(cx, handler, cx->names().apply, &trap)) { 1323 return false; 1324 } 1325 1326 // Step 6. 1327 if (trap.isUndefined()) { 1328 InvokeArgs iargs(cx); 1329 if (!FillArgumentsFromArraylike(cx, iargs, args)) { 1330 return false; 1331 } 1332 1333 RootedValue fval(cx, ObjectValue(*target)); 1334 return js::Call(cx, fval, args.thisv(), iargs, args.rval()); 1335 } 1336 1337 // Step 7. 1338 RootedObject argArray(cx, 1339 NewDenseCopiedArray(cx, args.length(), args.array())); 1340 if (!argArray) { 1341 return false; 1342 } 1343 1344 // Step 8. 1345 FixedInvokeArgs<3> iargs(cx); 1346 1347 iargs[0].setObject(*target); 1348 iargs[1].set(args.thisv()); 1349 iargs[2].setObject(*argArray); 1350 1351 RootedValue thisv(cx, ObjectValue(*handler)); 1352 return js::Call(cx, trap, thisv, iargs, args.rval()); 1353 } 1354 1355 // ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.14 Proxy.[[Construct]] 1356 bool ScriptedProxyHandler::construct(JSContext* cx, HandleObject proxy, 1357 const CallArgs& args) const { 1358 // Steps 1-3. 1359 RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); 1360 if (!handler) { 1361 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1362 JSMSG_PROXY_REVOKED); 1363 return false; 1364 } 1365 1366 // Step 4. 1367 RootedObject target(cx, proxy->as<ProxyObject>().target()); 1368 MOZ_ASSERT(target); 1369 MOZ_ASSERT(target->isConstructor()); 1370 1371 // Step 5. 1372 RootedValue trap(cx); 1373 if (!GetProxyTrap(cx, handler, cx->names().construct, &trap)) { 1374 return false; 1375 } 1376 1377 // Step 6. 1378 if (trap.isUndefined()) { 1379 ConstructArgs cargs(cx); 1380 if (!FillArgumentsFromArraylike(cx, cargs, args)) { 1381 return false; 1382 } 1383 1384 RootedValue targetv(cx, ObjectValue(*target)); 1385 RootedObject obj(cx); 1386 if (!Construct(cx, targetv, cargs, args.newTarget(), &obj)) { 1387 return false; 1388 } 1389 1390 args.rval().setObject(*obj); 1391 return true; 1392 } 1393 1394 // Step 7. 1395 RootedObject argArray(cx, 1396 NewDenseCopiedArray(cx, args.length(), args.array())); 1397 if (!argArray) { 1398 return false; 1399 } 1400 1401 // Steps 8, 10. 1402 { 1403 FixedInvokeArgs<3> iargs(cx); 1404 1405 iargs[0].setObject(*target); 1406 iargs[1].setObject(*argArray); 1407 iargs[2].set(args.newTarget()); 1408 1409 RootedValue thisv(cx, ObjectValue(*handler)); 1410 if (!Call(cx, trap, thisv, iargs, args.rval())) { 1411 return false; 1412 } 1413 } 1414 1415 // Step 9. 1416 if (!args.rval().isObject()) { 1417 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1418 JSMSG_PROXY_CONSTRUCT_OBJECT); 1419 return false; 1420 } 1421 1422 return true; 1423 } 1424 1425 bool ScriptedProxyHandler::nativeCall(JSContext* cx, IsAcceptableThis test, 1426 NativeImpl impl, 1427 const CallArgs& args) const { 1428 ReportIncompatible(cx, args); 1429 return false; 1430 } 1431 1432 bool ScriptedProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy, 1433 ESClass* cls) const { 1434 *cls = ESClass::Other; 1435 return true; 1436 } 1437 1438 bool ScriptedProxyHandler::isArray(JSContext* cx, HandleObject proxy, 1439 IsArrayAnswer* answer) const { 1440 RootedObject target(cx, proxy->as<ProxyObject>().target()); 1441 if (target) { 1442 return JS::IsArray(cx, target, answer); 1443 } 1444 1445 *answer = IsArrayAnswer::RevokedProxy; 1446 return true; 1447 } 1448 1449 const char* ScriptedProxyHandler::className(JSContext* cx, 1450 HandleObject proxy) const { 1451 // Right now the caller is not prepared to handle failures. 1452 return BaseProxyHandler::className(cx, proxy); 1453 } 1454 1455 JSString* ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, 1456 bool isToSource) const { 1457 // The BaseProxyHandler has the desired behavior: Throw for non-callable, 1458 // otherwise return [native code]. 1459 return BaseProxyHandler::fun_toString(cx, proxy, isToSource); 1460 } 1461 1462 RegExpShared* ScriptedProxyHandler::regexp_toShared(JSContext* cx, 1463 HandleObject proxy) const { 1464 MOZ_CRASH("Should not end up in ScriptedProxyHandler::regexp_toShared"); 1465 } 1466 1467 bool ScriptedProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy, 1468 MutableHandleValue vp) const { 1469 MOZ_CRASH("Should not end up in ScriptedProxyHandler::boxedValue_unbox"); 1470 return false; 1471 } 1472 1473 bool ScriptedProxyHandler::isCallable(JSObject* obj) const { 1474 MOZ_ASSERT(obj->as<ProxyObject>().handler() == 1475 &ScriptedProxyHandler::singleton); 1476 uint32_t callConstruct = obj->as<ProxyObject>() 1477 .reservedSlot(IS_CALLCONSTRUCT_EXTRA) 1478 .toPrivateUint32(); 1479 return !!(callConstruct & IS_CALLABLE); 1480 } 1481 1482 bool ScriptedProxyHandler::isConstructor(JSObject* obj) const { 1483 MOZ_ASSERT(obj->as<ProxyObject>().handler() == 1484 &ScriptedProxyHandler::singleton); 1485 uint32_t callConstruct = obj->as<ProxyObject>() 1486 .reservedSlot(IS_CALLCONSTRUCT_EXTRA) 1487 .toPrivateUint32(); 1488 return !!(callConstruct & IS_CONSTRUCTOR); 1489 } 1490 1491 const char ScriptedProxyHandler::family = 0; 1492 const ScriptedProxyHandler ScriptedProxyHandler::singleton; 1493 1494 // ES2021 rev c21b280a2c46e92decf3efeca9e9da35d5b9f622 1495 // Including the changes from: https://github.com/tc39/ecma262/pull/1814 1496 // 9.5.14 ProxyCreate. 1497 static bool ProxyCreate(JSContext* cx, CallArgs& args, const char* callerName) { 1498 if (!args.requireAtLeast(cx, callerName, 2)) { 1499 return false; 1500 } 1501 1502 // Step 1. 1503 RootedObject target(cx, 1504 RequireObjectArg(cx, "`target`", callerName, args[0])); 1505 if (!target) { 1506 return false; 1507 } 1508 1509 // Step 2. 1510 RootedObject handler(cx, 1511 RequireObjectArg(cx, "`handler`", callerName, args[1])); 1512 if (!handler) { 1513 return false; 1514 } 1515 1516 // Steps 3-4, 6. 1517 RootedValue priv(cx, ObjectValue(*target)); 1518 JSObject* proxy_ = NewProxyObject(cx, &ScriptedProxyHandler::singleton, priv, 1519 TaggedProto::LazyProto); 1520 if (!proxy_) { 1521 return false; 1522 } 1523 1524 // Step 7 (reordered). 1525 ProxyObject* proxy = &proxy_->as<ProxyObject>(); 1526 proxy->setReservedSlot(ScriptedProxyHandler::HANDLER_EXTRA, 1527 ObjectValue(*handler)); 1528 1529 // Step 5. 1530 uint32_t callable = 1531 target->isCallable() ? ScriptedProxyHandler::IS_CALLABLE : 0; 1532 uint32_t constructor = 1533 target->isConstructor() ? ScriptedProxyHandler::IS_CONSTRUCTOR : 0; 1534 proxy->setReservedSlot(ScriptedProxyHandler::IS_CALLCONSTRUCT_EXTRA, 1535 PrivateUint32Value(callable | constructor)); 1536 1537 // Step 8. 1538 args.rval().setObject(*proxy); 1539 return true; 1540 } 1541 1542 bool js::proxy(JSContext* cx, unsigned argc, Value* vp) { 1543 CallArgs args = CallArgsFromVp(argc, vp); 1544 1545 if (!ThrowIfNotConstructing(cx, args, "Proxy")) { 1546 return false; 1547 } 1548 1549 return ProxyCreate(cx, args, "Proxy"); 1550 } 1551 1552 static bool RevokeProxy(JSContext* cx, unsigned argc, Value* vp) { 1553 CallArgs args = CallArgsFromVp(argc, vp); 1554 1555 RootedFunction func(cx, &args.callee().as<JSFunction>()); 1556 RootedObject p(cx, func->getExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT) 1557 .toObjectOrNull()); 1558 1559 if (p) { 1560 func->setExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT, NullValue()); 1561 1562 MOZ_ASSERT(p->is<ProxyObject>()); 1563 1564 p->as<ProxyObject>().setSameCompartmentPrivate(NullValue()); 1565 p->as<ProxyObject>().setReservedSlot(ScriptedProxyHandler::HANDLER_EXTRA, 1566 NullValue()); 1567 } 1568 1569 args.rval().setUndefined(); 1570 return true; 1571 } 1572 1573 bool js::proxy_revocable(JSContext* cx, unsigned argc, Value* vp) { 1574 CallArgs args = CallArgsFromVp(argc, vp); 1575 1576 if (!ProxyCreate(cx, args, "Proxy.revocable")) { 1577 return false; 1578 } 1579 1580 RootedValue proxyVal(cx, args.rval()); 1581 MOZ_ASSERT(proxyVal.toObject().is<ProxyObject>()); 1582 1583 RootedFunction revoker( 1584 cx, NewNativeFunction(cx, RevokeProxy, 0, nullptr, 1585 gc::AllocKind::FUNCTION_EXTENDED, GenericObject)); 1586 if (!revoker) { 1587 return false; 1588 } 1589 1590 revoker->initExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT, proxyVal); 1591 1592 Rooted<PlainObject*> result(cx, NewPlainObject(cx)); 1593 if (!result) { 1594 return false; 1595 } 1596 1597 RootedValue revokeVal(cx, ObjectValue(*revoker)); 1598 if (!DefineDataProperty(cx, result, cx->names().proxy, proxyVal) || 1599 !DefineDataProperty(cx, result, cx->names().revoke, revokeVal)) { 1600 return false; 1601 } 1602 1603 args.rval().setObject(*result); 1604 return true; 1605 }