tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }