tor-browser

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

Proxy.cpp (35859B)


      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 "js/Proxy.h"
      8 
      9 #include "mozilla/Attributes.h"
     10 #include "mozilla/Maybe.h"
     11 
     12 #include <string.h>
     13 
     14 #include "js/friend/ErrorMessages.h"  // JSMSG_*
     15 #include "js/friend/StackLimits.h"  // js::AutoCheckRecursionLimit, js::GetNativeStackLimit
     16 #include "js/friend/WindowProxy.h"  // js::IsWindow, js::IsWindowProxy, js::ToWindowProxyIfWindow
     17 #include "js/PropertySpec.h"
     18 #include "js/Value.h"  // JS::ObjectValue
     19 #include "js/Wrapper.h"
     20 #include "proxy/DeadObjectProxy.h"
     21 #include "proxy/ScriptedProxyHandler.h"
     22 #include "vm/Compartment.h"
     23 #include "vm/Interpreter.h"  // js::CallGetter
     24 #include "vm/JSContext.h"
     25 #include "vm/JSFunction.h"
     26 #include "vm/JSObject.h"
     27 #include "vm/WrapperObject.h"
     28 
     29 #include "gc/Marking-inl.h"
     30 #include "vm/JSObject-inl.h"
     31 #include "vm/NativeObject-inl.h"
     32 
     33 using namespace js;
     34 
     35 // Used by private fields to manipulate the ProxyExpando:
     36 // All the following methods are called iff the handler for the proxy
     37 // returns true for useProxyExpandoObjectForPrivateFields.
     38 static bool ProxySetOnExpando(JSContext* cx, HandleObject proxy, HandleId id,
     39                              HandleValue v, HandleValue receiver,
     40                              ObjectOpResult& result) {
     41  MOZ_ASSERT(id.isPrivateName());
     42 
     43  // For BaseProxyHandler, private names are stored in the expando object.
     44  RootedObject expando(cx, proxy->as<ProxyObject>().expando().toObjectOrNull());
     45 
     46  // SetPrivateElementOperation checks for hasOwn first, which ensures the
     47  // expando exsists.
     48  //
     49  // If we don't have an expando, then we're probably misusing debugger apis and
     50  // should just throw.
     51  if (!expando) {
     52    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     53                              JSMSG_SET_MISSING_PRIVATE);
     54    return false;
     55  }
     56 
     57  Rooted<mozilla::Maybe<PropertyDescriptor>> ownDesc(cx);
     58  if (!GetOwnPropertyDescriptor(cx, expando, id, &ownDesc)) {
     59    return false;
     60  }
     61  if (ownDesc.isNothing()) {
     62    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     63                              JSMSG_SET_MISSING_PRIVATE);
     64    return false;
     65  }
     66 
     67  RootedValue expandoValue(cx, proxy->as<ProxyObject>().expando());
     68  return SetPropertyIgnoringNamedGetter(cx, expando, id, v, expandoValue,
     69                                        ownDesc, result);
     70 }
     71 
     72 static bool ProxyGetOwnPropertyDescriptorFromExpando(
     73    JSContext* cx, HandleObject proxy, HandleId id,
     74    MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) {
     75  RootedObject expando(cx, proxy->as<ProxyObject>().expando().toObjectOrNull());
     76 
     77  if (!expando) {
     78    return true;
     79  }
     80 
     81  return GetOwnPropertyDescriptor(cx, expando, id, desc);
     82 }
     83 
     84 static bool ProxyGetOnExpando(JSContext* cx, HandleObject proxy,
     85                              HandleValue receiver, HandleId id,
     86                              MutableHandleValue vp) {
     87  // For BaseProxyHandler, private names are stored in the expando object.
     88  RootedObject expando(cx, proxy->as<ProxyObject>().expando().toObjectOrNull());
     89 
     90  // We must have the expando, or GetPrivateElemOperation didn't call
     91  // hasPrivate first.
     92  if (!expando) {
     93    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     94                              JSMSG_GET_MISSING_PRIVATE);
     95    return false;
     96  }
     97 
     98  // Because we controlled the creation of the expando, we know it's not a
     99  // proxy, and so can safely call internal methods on it without worrying about
    100  // exposing information about private names.
    101  Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
    102  if (!GetOwnPropertyDescriptor(cx, expando, id, &desc)) {
    103    return false;
    104  }
    105  // We must have the object, same reasoning as the expando.
    106  if (desc.isNothing()) {
    107    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    108                              JSMSG_SET_MISSING_PRIVATE);
    109    return false;
    110  }
    111 
    112  // If the private name has a getter, delegate to that.
    113  if (desc->hasGetter()) {
    114    RootedValue getter(cx, JS::ObjectValue(*desc->getter()));
    115    return js::CallGetter(cx, receiver, getter, vp);
    116  }
    117 
    118  MOZ_ASSERT(desc->hasValue());
    119  MOZ_ASSERT(desc->isDataDescriptor());
    120 
    121  vp.set(desc->value());
    122  return true;
    123 }
    124 
    125 static bool ProxyHasOnExpando(JSContext* cx, HandleObject proxy, HandleId id,
    126                              bool* bp) {
    127  // For BaseProxyHandler, private names are stored in the expando object.
    128  RootedObject expando(cx, proxy->as<ProxyObject>().expando().toObjectOrNull());
    129 
    130  // If there is no expando object, then there is no private field.
    131  if (!expando) {
    132    *bp = false;
    133    return true;
    134  }
    135 
    136  return HasOwnProperty(cx, expando, id, bp);
    137 }
    138 
    139 static bool ProxyDefineOnExpando(JSContext* cx, HandleObject proxy, HandleId id,
    140                                 Handle<PropertyDescriptor> desc,
    141                                 ObjectOpResult& result) {
    142  MOZ_ASSERT(id.isPrivateName());
    143 
    144  // For BaseProxyHandler, private names are stored in the expando object.
    145  RootedObject expando(cx, proxy->as<ProxyObject>().expando().toObjectOrNull());
    146 
    147  if (!expando) {
    148    expando = NewPlainObjectWithProto(cx, nullptr);
    149    if (!expando) {
    150      return false;
    151    }
    152 
    153    proxy->as<ProxyObject>().setExpando(expando);
    154  }
    155 
    156  return DefineProperty(cx, expando, id, desc, result);
    157 }
    158 
    159 void js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext* cx,
    160                                                             HandleId id) {
    161  if (JS_IsExceptionPending(cx)) {
    162    return;
    163  }
    164 
    165  if (id.isVoid()) {
    166    ReportAccessDenied(cx);
    167  } else {
    168    Throw(cx, id, JSMSG_PROPERTY_ACCESS_DENIED);
    169  }
    170 }
    171 
    172 #ifdef DEBUG
    173 void js::AutoEnterPolicy::recordEnter(JSContext* cx, HandleObject proxy,
    174                                      HandleId id, Action act) {
    175  if (allowed()) {
    176    context = cx;
    177    enteredProxy.emplace(proxy);
    178    enteredId.emplace(id);
    179    enteredAction = act;
    180    prev = cx->enteredPolicy;
    181    cx->enteredPolicy = this;
    182  }
    183 }
    184 
    185 void js::AutoEnterPolicy::recordLeave() {
    186  if (enteredProxy) {
    187    MOZ_ASSERT(context->enteredPolicy == this);
    188    context->enteredPolicy = prev;
    189  }
    190 }
    191 
    192 JS_PUBLIC_API void js::assertEnteredPolicy(JSContext* cx, JSObject* proxy,
    193                                           jsid id,
    194                                           BaseProxyHandler::Action act) {
    195  MOZ_ASSERT(proxy->is<ProxyObject>());
    196  MOZ_ASSERT(cx->enteredPolicy);
    197  MOZ_ASSERT(cx->enteredPolicy->enteredProxy->get() == proxy);
    198  MOZ_ASSERT(cx->enteredPolicy->enteredId->get() == id);
    199  MOZ_ASSERT(cx->enteredPolicy->enteredAction & act);
    200 }
    201 #endif
    202 
    203 bool Proxy::getOwnPropertyDescriptor(
    204    JSContext* cx, HandleObject proxy, HandleId id,
    205    MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) {
    206  AutoCheckRecursionLimit recursion(cx);
    207  if (!recursion.check(cx)) {
    208    return false;
    209  }
    210  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    211  desc.reset();  // default result if we refuse to perform this action
    212  AutoEnterPolicy policy(cx, handler, proxy, id,
    213                         BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
    214  if (!policy.allowed()) {
    215    return policy.returnValue();
    216  }
    217 
    218  if (handler->useProxyExpandoObjectForPrivateFields() && id.isPrivateName()) {
    219    return ProxyGetOwnPropertyDescriptorFromExpando(cx, proxy, id, desc);
    220  }
    221  return handler->getOwnPropertyDescriptor(cx, proxy, id, desc);
    222 }
    223 
    224 bool Proxy::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
    225                           Handle<PropertyDescriptor> desc,
    226                           ObjectOpResult& result) {
    227  AutoCheckRecursionLimit recursion(cx);
    228  if (!recursion.check(cx)) {
    229    return false;
    230  }
    231  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    232 
    233  // We shouldn't be definining a private field if we are supposed to throw;
    234  // this ought to have been caught by CheckPrivateField.
    235  MOZ_ASSERT_IF(id.isPrivateName(), !handler->throwOnPrivateField());
    236 
    237  AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
    238  if (!policy.allowed()) {
    239    if (!policy.returnValue()) {
    240      return false;
    241    }
    242    return result.succeed();
    243  }
    244 
    245  // Private field accesses have different semantics depending on the kind
    246  // of proxy involved, and so take a different path compared to regular
    247  // [[Get]] operations. For example, scripted handlers don't fire traps
    248  // when accessing private fields (because of the WeakMap semantics)
    249  if (id.isPrivateName() && handler->useProxyExpandoObjectForPrivateFields()) {
    250    return ProxyDefineOnExpando(cx, proxy, id, desc, result);
    251  }
    252 
    253  return proxy->as<ProxyObject>().handler()->defineProperty(cx, proxy, id, desc,
    254                                                            result);
    255 }
    256 
    257 bool Proxy::ownPropertyKeys(JSContext* cx, HandleObject proxy,
    258                            MutableHandleIdVector props) {
    259  AutoCheckRecursionLimit recursion(cx);
    260  if (!recursion.check(cx)) {
    261    return false;
    262  }
    263  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    264  AutoEnterPolicy policy(cx, handler, proxy, JS::VoidHandlePropertyKey,
    265                         BaseProxyHandler::ENUMERATE, true);
    266  if (!policy.allowed()) {
    267    return policy.returnValue();
    268  }
    269  return proxy->as<ProxyObject>().handler()->ownPropertyKeys(cx, proxy, props);
    270 }
    271 
    272 bool Proxy::delete_(JSContext* cx, HandleObject proxy, HandleId id,
    273                    ObjectOpResult& result) {
    274  AutoCheckRecursionLimit recursion(cx);
    275  if (!recursion.check(cx)) {
    276    return false;
    277  }
    278  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    279  AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
    280  if (!policy.allowed()) {
    281    bool ok = policy.returnValue();
    282    if (ok) {
    283      result.succeed();
    284    }
    285    return ok;
    286  }
    287 
    288  // Private names shouldn't take this path, as deleting a private name
    289  // should be a syntax error.
    290  MOZ_ASSERT(!id.isPrivateName());
    291 
    292  return proxy->as<ProxyObject>().handler()->delete_(cx, proxy, id, result);
    293 }
    294 
    295 JS_PUBLIC_API bool js::AppendUnique(JSContext* cx, MutableHandleIdVector base,
    296                                    HandleIdVector others) {
    297  RootedIdVector uniqueOthers(cx);
    298  if (!uniqueOthers.reserve(others.length())) {
    299    return false;
    300  }
    301  for (size_t i = 0; i < others.length(); ++i) {
    302    bool unique = true;
    303    for (size_t j = 0; j < base.length(); ++j) {
    304      if (others[i].get() == base[j]) {
    305        unique = false;
    306        break;
    307      }
    308    }
    309    if (unique) {
    310      if (!uniqueOthers.append(others[i])) {
    311        return false;
    312      }
    313    }
    314  }
    315  return base.appendAll(std::move(uniqueOthers));
    316 }
    317 
    318 /* static */
    319 bool Proxy::getPrototype(JSContext* cx, HandleObject proxy,
    320                         MutableHandleObject proto) {
    321  MOZ_ASSERT(proxy->hasDynamicPrototype());
    322  AutoCheckRecursionLimit recursion(cx);
    323  if (!recursion.check(cx)) {
    324    return false;
    325  }
    326  return proxy->as<ProxyObject>().handler()->getPrototype(cx, proxy, proto);
    327 }
    328 
    329 /* static */
    330 bool Proxy::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
    331                         ObjectOpResult& result) {
    332  MOZ_ASSERT(proxy->hasDynamicPrototype());
    333  AutoCheckRecursionLimit recursion(cx);
    334  if (!recursion.check(cx)) {
    335    return false;
    336  }
    337  return proxy->as<ProxyObject>().handler()->setPrototype(cx, proxy, proto,
    338                                                          result);
    339 }
    340 
    341 /* static */
    342 bool Proxy::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy,
    343                                   bool* isOrdinary,
    344                                   MutableHandleObject proto) {
    345  AutoCheckRecursionLimit recursion(cx);
    346  if (!recursion.check(cx)) {
    347    return false;
    348  }
    349  return proxy->as<ProxyObject>().handler()->getPrototypeIfOrdinary(
    350      cx, proxy, isOrdinary, proto);
    351 }
    352 
    353 /* static */
    354 bool Proxy::setImmutablePrototype(JSContext* cx, HandleObject proxy,
    355                                  bool* succeeded) {
    356  AutoCheckRecursionLimit recursion(cx);
    357  if (!recursion.check(cx)) {
    358    return false;
    359  }
    360  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    361  return handler->setImmutablePrototype(cx, proxy, succeeded);
    362 }
    363 
    364 /* static */
    365 bool Proxy::preventExtensions(JSContext* cx, HandleObject proxy,
    366                              ObjectOpResult& result) {
    367  AutoCheckRecursionLimit recursion(cx);
    368  if (!recursion.check(cx)) {
    369    return false;
    370  }
    371  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    372  return handler->preventExtensions(cx, proxy, result);
    373 }
    374 
    375 /* static */
    376 bool Proxy::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) {
    377  AutoCheckRecursionLimit recursion(cx);
    378  if (!recursion.check(cx)) {
    379    return false;
    380  }
    381  return proxy->as<ProxyObject>().handler()->isExtensible(cx, proxy,
    382                                                          extensible);
    383 }
    384 
    385 bool Proxy::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) {
    386  AutoCheckRecursionLimit recursion(cx);
    387  if (!recursion.check(cx)) {
    388    return false;
    389  }
    390  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    391  *bp = false;  // default result if we refuse to perform this action
    392  AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
    393  if (!policy.allowed()) {
    394    return policy.returnValue();
    395  }
    396 
    397  // Private names shouldn't take this path, but only hasOwn;
    398  MOZ_ASSERT(!id.isPrivateName());
    399 
    400  if (handler->hasPrototype()) {
    401    if (!handler->hasOwn(cx, proxy, id, bp)) {
    402      return false;
    403    }
    404    if (*bp) {
    405      return true;
    406    }
    407 
    408    RootedObject proto(cx);
    409    if (!GetPrototype(cx, proxy, &proto)) {
    410      return false;
    411    }
    412    if (!proto) {
    413      return true;
    414    }
    415 
    416    return HasProperty(cx, proto, id, bp);
    417  }
    418 
    419  return handler->has(cx, proxy, id, bp);
    420 }
    421 
    422 bool js::ProxyHas(JSContext* cx, HandleObject proxy, HandleValue idVal,
    423                  bool* result) {
    424  RootedId id(cx);
    425  if (!ToPropertyKey(cx, idVal, &id)) {
    426    return false;
    427  }
    428 
    429  return Proxy::has(cx, proxy, id, result);
    430 }
    431 
    432 bool Proxy::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) {
    433  AutoCheckRecursionLimit recursion(cx);
    434  if (!recursion.check(cx)) {
    435    return false;
    436  }
    437  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    438  *bp = false;  // default result if we refuse to perform this action
    439 
    440  // If the handler is supposed to throw, we'll never have a private field so
    441  // simply return, as we shouldn't throw an invalid security error when
    442  // checking for the presence of a private field (WeakMap model).
    443  if (id.isPrivateName() && handler->throwOnPrivateField()) {
    444    return true;
    445  }
    446 
    447  AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
    448  if (!policy.allowed()) {
    449    return policy.returnValue();
    450  }
    451 
    452  // Private field accesses have different semantics depending on the kind
    453  // of proxy involved, and so take a different path compared to regular
    454  // [[Get]] operations. For example, scripted handlers don't fire traps
    455  // when accessing private fields (because of the WeakMap semantics)
    456  if (id.isPrivateName() && handler->useProxyExpandoObjectForPrivateFields()) {
    457    return ProxyHasOnExpando(cx, proxy, id, bp);
    458  }
    459 
    460  return handler->hasOwn(cx, proxy, id, bp);
    461 }
    462 
    463 bool js::ProxyHasOwn(JSContext* cx, HandleObject proxy, HandleValue idVal,
    464                     bool* result) {
    465  RootedId id(cx);
    466  if (!ToPropertyKey(cx, idVal, &id)) {
    467    return false;
    468  }
    469 
    470  return Proxy::hasOwn(cx, proxy, id, result);
    471 }
    472 
    473 static MOZ_ALWAYS_INLINE Value ValueToWindowProxyIfWindow(const Value& v,
    474                                                          JSObject* proxy) {
    475  if (v.isObject() && v != ObjectValue(*proxy)) {
    476    return ObjectValue(*ToWindowProxyIfWindow(&v.toObject()));
    477  }
    478  return v;
    479 }
    480 
    481 MOZ_ALWAYS_INLINE bool Proxy::getInternal(JSContext* cx, HandleObject proxy,
    482                                          HandleValue receiver, HandleId id,
    483                                          MutableHandleValue vp) {
    484  MOZ_ASSERT_IF(receiver.isObject(), !IsWindow(&receiver.toObject()));
    485 
    486  AutoCheckRecursionLimit recursion(cx);
    487  if (!recursion.check(cx)) {
    488    return false;
    489  }
    490  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    491 
    492  // Shouldn't have gotten here, as this should have been caught earlier.
    493  MOZ_ASSERT_IF(id.isPrivateName(), !handler->throwOnPrivateField());
    494 
    495  vp.setUndefined();  // default result if we refuse to perform this action
    496  AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
    497  if (!policy.allowed()) {
    498    return policy.returnValue();
    499  }
    500 
    501  // Private field accesses have different semantics depending on the kind
    502  // of proxy involved, and so take a different path compared to regular
    503  // [[Get]] operations. For example, scripted handlers don't fire traps
    504  // when accessing private fields (because of the WeakMap semantics)
    505  if (id.isPrivateName() && handler->useProxyExpandoObjectForPrivateFields()) {
    506    return ProxyGetOnExpando(cx, proxy, receiver, id, vp);
    507  }
    508 
    509  if (handler->hasPrototype()) {
    510    bool own;
    511    if (!handler->hasOwn(cx, proxy, id, &own)) {
    512      return false;
    513    }
    514    if (!own) {
    515      RootedObject proto(cx);
    516      if (!GetPrototype(cx, proxy, &proto)) {
    517        return false;
    518      }
    519      if (!proto) {
    520        return true;
    521      }
    522      return GetProperty(cx, proto, receiver, id, vp);
    523    }
    524  }
    525 
    526  return handler->get(cx, proxy, receiver, id, vp);
    527 }
    528 
    529 bool Proxy::get(JSContext* cx, HandleObject proxy, HandleValue receiver_,
    530                HandleId id, MutableHandleValue vp) {
    531  // Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
    532  // shouldn't have to know about the Window/WindowProxy distinction.
    533  RootedValue receiver(cx, ValueToWindowProxyIfWindow(receiver_, proxy));
    534  return getInternal(cx, proxy, receiver, id, vp);
    535 }
    536 
    537 bool js::ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id,
    538                          MutableHandleValue vp) {
    539  RootedValue receiver(cx, ObjectValue(*proxy));
    540  return Proxy::getInternal(cx, proxy, receiver, id, vp);
    541 }
    542 
    543 bool js::ProxyGetPropertyByValue(JSContext* cx, HandleObject proxy,
    544                                 HandleValue idVal, MutableHandleValue vp) {
    545  RootedId id(cx);
    546  if (!ToPropertyKey(cx, idVal, &id)) {
    547    return false;
    548  }
    549 
    550  RootedValue receiver(cx, ObjectValue(*proxy));
    551  return Proxy::getInternal(cx, proxy, receiver, id, vp);
    552 }
    553 
    554 MOZ_ALWAYS_INLINE bool Proxy::setInternal(JSContext* cx, HandleObject proxy,
    555                                          HandleId id, HandleValue v,
    556                                          HandleValue receiver,
    557                                          ObjectOpResult& result) {
    558  MOZ_ASSERT_IF(receiver.isObject(), !IsWindow(&receiver.toObject()));
    559 
    560  AutoCheckRecursionLimit recursion(cx);
    561  if (!recursion.check(cx)) {
    562    return false;
    563  }
    564 
    565  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    566 
    567  // Should have been handled already.
    568  MOZ_ASSERT_IF(id.isPrivateName(), !handler->throwOnPrivateField());
    569 
    570  AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
    571  if (!policy.allowed()) {
    572    if (!policy.returnValue()) {
    573      return false;
    574    }
    575    return result.succeed();
    576  }
    577 
    578  // Private field accesses have different semantics depending on the kind
    579  // of proxy involved, and so take a different path compared to regular
    580  // [[Set]] operations.
    581  //
    582  // This doesn't interact with hasPrototype, as PrivateFields are always
    583  // own propertiers, and so we never deal with prototype traversals.
    584  if (id.isPrivateName() && handler->useProxyExpandoObjectForPrivateFields()) {
    585    return ProxySetOnExpando(cx, proxy, id, v, receiver, result);
    586  }
    587 
    588  // Special case. See the comment on BaseProxyHandler::mHasPrototype.
    589  if (handler->hasPrototype()) {
    590    return handler->BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
    591  }
    592 
    593  return handler->set(cx, proxy, id, v, receiver, result);
    594 }
    595 
    596 bool Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
    597                HandleValue receiver_, ObjectOpResult& result) {
    598  // Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
    599  // shouldn't have to know about the Window/WindowProxy distinction.
    600  RootedValue receiver(cx, ValueToWindowProxyIfWindow(receiver_, proxy));
    601  return setInternal(cx, proxy, id, v, receiver, result);
    602 }
    603 
    604 bool js::ProxySetProperty(JSContext* cx, HandleObject proxy, HandleId id,
    605                          HandleValue val, bool strict) {
    606  ObjectOpResult result;
    607  RootedValue receiver(cx, ObjectValue(*proxy));
    608  if (!Proxy::setInternal(cx, proxy, id, val, receiver, result)) {
    609    return false;
    610  }
    611  return result.checkStrictModeError(cx, proxy, id, strict);
    612 }
    613 
    614 bool js::ProxySetPropertyByValue(JSContext* cx, HandleObject proxy,
    615                                 HandleValue idVal, HandleValue val,
    616                                 bool strict) {
    617  RootedId id(cx);
    618  if (!ToPropertyKey(cx, idVal, &id)) {
    619    return false;
    620  }
    621 
    622  ObjectOpResult result;
    623  RootedValue receiver(cx, ObjectValue(*proxy));
    624  if (!Proxy::setInternal(cx, proxy, id, val, receiver, result)) {
    625    return false;
    626  }
    627  return result.checkStrictModeError(cx, proxy, id, strict);
    628 }
    629 
    630 bool Proxy::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
    631                                         MutableHandleIdVector props) {
    632  AutoCheckRecursionLimit recursion(cx);
    633  if (!recursion.check(cx)) {
    634    return false;
    635  }
    636  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    637  AutoEnterPolicy policy(cx, handler, proxy, JS::VoidHandlePropertyKey,
    638                         BaseProxyHandler::ENUMERATE, true);
    639  if (!policy.allowed()) {
    640    return policy.returnValue();
    641  }
    642  return handler->getOwnEnumerablePropertyKeys(cx, proxy, props);
    643 }
    644 
    645 bool Proxy::enumerate(JSContext* cx, HandleObject proxy,
    646                      MutableHandleIdVector props) {
    647  AutoCheckRecursionLimit recursion(cx);
    648  if (!recursion.check(cx)) {
    649    return false;
    650  }
    651 
    652  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    653  if (handler->hasPrototype()) {
    654    if (!Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props)) {
    655      return false;
    656    }
    657 
    658    RootedObject proto(cx);
    659    if (!GetPrototype(cx, proxy, &proto)) {
    660      return false;
    661    }
    662    if (!proto) {
    663      return true;
    664    }
    665 
    666    cx->check(proxy, proto);
    667 
    668    RootedIdVector protoProps(cx);
    669    if (!GetPropertyKeys(cx, proto, 0, &protoProps)) {
    670      return false;
    671    }
    672    return AppendUnique(cx, props, protoProps);
    673  }
    674 
    675  AutoEnterPolicy policy(cx, handler, proxy, JS::VoidHandlePropertyKey,
    676                         BaseProxyHandler::ENUMERATE, true);
    677 
    678  // If the policy denies access but wants us to return true, we need
    679  // to return an empty |props| list.
    680  if (!policy.allowed()) {
    681    MOZ_ASSERT(props.empty());
    682    return policy.returnValue();
    683  }
    684 
    685  return handler->enumerate(cx, proxy, props);
    686 }
    687 
    688 bool Proxy::call(JSContext* cx, HandleObject proxy, const CallArgs& args) {
    689  AutoCheckRecursionLimit recursion(cx);
    690  if (!recursion.check(cx)) {
    691    return false;
    692  }
    693  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    694 
    695  // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
    696  // can only set our default value once we're sure that we're not calling the
    697  // trap.
    698  AutoEnterPolicy policy(cx, handler, proxy, JS::VoidHandlePropertyKey,
    699                         BaseProxyHandler::CALL, true);
    700  if (!policy.allowed()) {
    701    args.rval().setUndefined();
    702    return policy.returnValue();
    703  }
    704 
    705  return handler->call(cx, proxy, args);
    706 }
    707 
    708 bool Proxy::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) {
    709  AutoCheckRecursionLimit recursion(cx);
    710  if (!recursion.check(cx)) {
    711    return false;
    712  }
    713  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    714 
    715  // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
    716  // can only set our default value once we're sure that we're not calling the
    717  // trap.
    718  AutoEnterPolicy policy(cx, handler, proxy, JS::VoidHandlePropertyKey,
    719                         BaseProxyHandler::CALL, true);
    720  if (!policy.allowed()) {
    721    args.rval().setUndefined();
    722    return policy.returnValue();
    723  }
    724 
    725  return handler->construct(cx, proxy, args);
    726 }
    727 
    728 bool Proxy::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
    729                       const CallArgs& args) {
    730  AutoCheckRecursionLimit recursion(cx);
    731  if (!recursion.check(cx)) {
    732    return false;
    733  }
    734  RootedObject proxy(cx, &args.thisv().toObject());
    735  // Note - we don't enter a policy here because our security architecture
    736  // guards against nativeCall by overriding the trap itself in the right
    737  // circumstances.
    738  return proxy->as<ProxyObject>().handler()->nativeCall(cx, test, impl, args);
    739 }
    740 
    741 bool Proxy::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) {
    742  AutoCheckRecursionLimit recursion(cx);
    743  if (!recursion.check(cx)) {
    744    return false;
    745  }
    746  return proxy->as<ProxyObject>().handler()->getBuiltinClass(cx, proxy, cls);
    747 }
    748 
    749 bool Proxy::isArray(JSContext* cx, HandleObject proxy,
    750                    JS::IsArrayAnswer* answer) {
    751  AutoCheckRecursionLimit recursion(cx);
    752  if (!recursion.check(cx)) {
    753    return false;
    754  }
    755  return proxy->as<ProxyObject>().handler()->isArray(cx, proxy, answer);
    756 }
    757 
    758 const char* Proxy::className(JSContext* cx, HandleObject proxy) {
    759  // Check for unbounded recursion, but don't signal an error; className
    760  // needs to be infallible.
    761  AutoCheckRecursionLimit recursion(cx);
    762  if (!recursion.checkDontReport(cx)) {
    763    return "too much recursion";
    764  }
    765 
    766  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    767  AutoEnterPolicy policy(cx, handler, proxy, JS::VoidHandlePropertyKey,
    768                         BaseProxyHandler::GET, /* mayThrow = */ false);
    769  // Do the safe thing if the policy rejects.
    770  if (!policy.allowed()) {
    771    return handler->BaseProxyHandler::className(cx, proxy);
    772  }
    773  return handler->className(cx, proxy);
    774 }
    775 
    776 JSString* Proxy::fun_toString(JSContext* cx, HandleObject proxy,
    777                              bool isToSource) {
    778  AutoCheckRecursionLimit recursion(cx);
    779  if (!recursion.check(cx)) {
    780    return nullptr;
    781  }
    782  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    783  AutoEnterPolicy policy(cx, handler, proxy, JS::VoidHandlePropertyKey,
    784                         BaseProxyHandler::GET, /* mayThrow = */ false);
    785  // Do the safe thing if the policy rejects.
    786  if (!policy.allowed()) {
    787    return handler->BaseProxyHandler::fun_toString(cx, proxy, isToSource);
    788  }
    789  return handler->fun_toString(cx, proxy, isToSource);
    790 }
    791 
    792 RegExpShared* Proxy::regexp_toShared(JSContext* cx, HandleObject proxy) {
    793  AutoCheckRecursionLimit recursion(cx);
    794  if (!recursion.check(cx)) {
    795    return nullptr;
    796  }
    797  return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy);
    798 }
    799 
    800 bool Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy,
    801                             MutableHandleValue vp) {
    802  AutoCheckRecursionLimit recursion(cx);
    803  if (!recursion.check(cx)) {
    804    return false;
    805  }
    806  return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
    807 }
    808 
    809 JSObject* const TaggedProto::LazyProto = reinterpret_cast<JSObject*>(0x1);
    810 
    811 /* static */
    812 bool Proxy::getElements(JSContext* cx, HandleObject proxy, uint32_t begin,
    813                        uint32_t end, ElementAdder* adder) {
    814  AutoCheckRecursionLimit recursion(cx);
    815  if (!recursion.check(cx)) {
    816    return false;
    817  }
    818  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    819  AutoEnterPolicy policy(cx, handler, proxy, JS::VoidHandlePropertyKey,
    820                         BaseProxyHandler::GET,
    821                         /* mayThrow = */ true);
    822  if (!policy.allowed()) {
    823    if (policy.returnValue()) {
    824      MOZ_ASSERT(!cx->isExceptionPending());
    825      return js::GetElementsWithAdder(cx, proxy, proxy, begin, end, adder);
    826    }
    827    return false;
    828  }
    829  return handler->getElements(cx, proxy, begin, end, adder);
    830 }
    831 
    832 /* static */
    833 void Proxy::trace(JSTracer* trc, JSObject* proxy) {
    834  const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
    835  handler->trace(trc, proxy);
    836 }
    837 
    838 static bool proxy_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
    839                                 MutableHandleObject objp,
    840                                 PropertyResult* propp) {
    841  bool found;
    842  if (!Proxy::has(cx, obj, id, &found)) {
    843    return false;
    844  }
    845 
    846  if (found) {
    847    propp->setProxyProperty();
    848    objp.set(obj);
    849  } else {
    850    propp->setNotFound();
    851    objp.set(nullptr);
    852  }
    853  return true;
    854 }
    855 
    856 static bool proxy_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id,
    857                                 ObjectOpResult& result) {
    858  if (!Proxy::delete_(cx, obj, id, result)) {
    859    return false;
    860  }
    861  return SuppressDeletedProperty(cx, obj, id);  // XXX is this necessary?
    862 }
    863 
    864 /* static */
    865 void ProxyObject::traceEdgeToTarget(JSTracer* trc, ProxyObject* obj) {
    866  TraceCrossCompartmentEdge(trc, obj, obj->slotOfPrivate(), "proxy target");
    867 }
    868 
    869 #ifdef DEBUG
    870 static inline void CheckProxyIsInCCWMap(ProxyObject* proxy) {
    871  if (proxy->zone()->isGCCompacting()) {
    872    // Skip this check during compacting GC since objects' shapes and base
    873    // shapes may be forwarded. It's not impossible to make this work, but
    874    // requires adding a parallel lookupWrapper() path for this one case.
    875    return;
    876  }
    877 
    878  JSObject* referent = MaybeForwarded(proxy->target());
    879  if (referent->compartment() != proxy->compartment()) {
    880    // Assert that this proxy is tracked in the wrapper map. We maintain the
    881    // invariant that the wrapped object is the key in the wrapper map.
    882    ObjectWrapperMap::Ptr p = proxy->compartment()->lookupWrapper(referent);
    883    MOZ_ASSERT(p);
    884    MOZ_ASSERT(*p->value().unsafeGet() == proxy);
    885  }
    886 }
    887 #endif
    888 
    889 /* static */
    890 void ProxyObject::trace(JSTracer* trc, JSObject* obj) {
    891  ProxyObject* proxy = &obj->as<ProxyObject>();
    892 
    893  TraceNullableEdge(trc, proxy->slotOfExpando(), "expando");
    894 
    895 #ifdef DEBUG
    896  JSContext* cx = TlsContext.get();
    897  if (cx && cx->isStrictProxyCheckingEnabled() && proxy->is<WrapperObject>()) {
    898    CheckProxyIsInCCWMap(proxy);
    899  }
    900 #endif
    901 
    902  // Note: If you add new slots here, make sure to change
    903  // nuke() to cope.
    904 
    905  traceEdgeToTarget(trc, proxy);
    906 
    907  size_t nreserved = proxy->numReservedSlots();
    908  for (size_t i = 0; i < nreserved; i++) {
    909    /*
    910     * The GC can use the second reserved slot to link the cross compartment
    911     * wrappers into a linked list, in which case we don't want to trace it.
    912     */
    913    if (proxy->is<CrossCompartmentWrapperObject>() &&
    914        i == CrossCompartmentWrapperObject::GrayLinkReservedSlot) {
    915      continue;
    916    }
    917    TraceEdge(trc, proxy->reservedSlotPtr(i), "proxy_reserved");
    918  }
    919 
    920  Proxy::trace(trc, obj);
    921 }
    922 
    923 static void proxy_Finalize(JS::GCContext* gcx, JSObject* obj) {
    924  // Suppress a bogus warning about finalize().
    925  JS::AutoSuppressGCAnalysis nogc;
    926 
    927  MOZ_ASSERT(obj->is<ProxyObject>());
    928  ProxyObject* proxy = &obj->as<ProxyObject>();
    929  proxy->handler()->finalize(gcx, obj);
    930 
    931  if (!proxy->usingInlineValueArray() && proxy->isTenured()) {
    932    auto* valArray = js::detail::GetProxyDataLayout(obj)->values();
    933    size_t size =
    934        js::detail::ProxyValueArray::sizeOf(proxy->numReservedSlots());
    935    gcx->free_(obj, valArray, size, MemoryUse::ProxyExternalValueArray);
    936  }
    937 }
    938 
    939 size_t js::proxy_ObjectMoved(JSObject* obj, JSObject* old) {
    940  ProxyObject& proxy = obj->as<ProxyObject>();
    941 
    942  if (IsInsideNursery(old)) {
    943    proxy.nurseryProxyTenured(&old->as<ProxyObject>());
    944  }
    945 
    946  return proxy.handler()->objectMoved(obj, old);
    947 }
    948 
    949 void ProxyObject::nurseryProxyTenured(ProxyObject* old) {
    950  if (old->usingInlineValueArray()) {
    951    setInlineValueArray();
    952    return;
    953  }
    954 
    955  Nursery& nursery = runtimeFromMainThread()->gc.nursery();
    956  nursery.removeMallocedBufferDuringMinorGC(data.values());
    957 
    958  size_t size = detail::ProxyValueArray::sizeOf(numReservedSlots());
    959  AddCellMemory(this, size, MemoryUse::ProxyExternalValueArray);
    960 }
    961 
    962 const JSClassOps js::ProxyClassOps = {
    963    nullptr,             // addProperty
    964    nullptr,             // delProperty
    965    nullptr,             // enumerate
    966    nullptr,             // newEnumerate
    967    nullptr,             // resolve
    968    nullptr,             // mayResolve
    969    proxy_Finalize,      // finalize
    970    nullptr,             // call
    971    nullptr,             // construct
    972    ProxyObject::trace,  // trace
    973 };
    974 
    975 const ClassExtension js::ProxyClassExtension = {
    976    proxy_ObjectMoved,  // objectMovedOp
    977 };
    978 
    979 const ObjectOps js::ProxyObjectOps = {
    980    proxy_LookupProperty,             // lookupProperty
    981    Proxy::defineProperty,            // defineProperty
    982    Proxy::has,                       // hasProperty
    983    Proxy::get,                       // getProperty
    984    Proxy::set,                       // setProperty
    985    Proxy::getOwnPropertyDescriptor,  // getOwnPropertyDescriptor
    986    proxy_DeleteProperty,             // deleteProperty
    987    Proxy::getElements,               // getElements
    988    Proxy::fun_toString,              // funToString
    989 };
    990 
    991 static const JSFunctionSpec proxy_static_methods[] = {
    992    JS_FN("revocable", proxy_revocable, 2, 0),
    993    JS_FS_END,
    994 };
    995 
    996 static const ClassSpec ProxyClassSpec = {
    997    GenericCreateConstructor<js::proxy, 2, gc::AllocKind::FUNCTION>,
    998    nullptr,
    999    proxy_static_methods,
   1000    nullptr,
   1001 };
   1002 
   1003 const JSClass js::ProxyClass = PROXY_CLASS_DEF_WITH_CLASS_SPEC(
   1004    "Proxy",
   1005    JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy) | JSCLASS_HAS_RESERVED_SLOTS(2),
   1006    &ProxyClassSpec);
   1007 
   1008 JS_PUBLIC_API JSObject* js::NewProxyObject(JSContext* cx,
   1009                                           const BaseProxyHandler* handler,
   1010                                           HandleValue priv, JSObject* proto_,
   1011                                           const ProxyOptions& options) {
   1012  AssertHeapIsIdle();
   1013  CHECK_THREAD(cx);
   1014 
   1015  // This can be called from the compartment wrap hooks while in a realm with a
   1016  // gray global. Trigger the read barrier on the global to ensure this is
   1017  // unmarked.
   1018  cx->realm()->maybeGlobal();
   1019 
   1020  if (proto_ != TaggedProto::LazyProto) {
   1021    cx->check(proto_);  // |priv| might be cross-compartment.
   1022  }
   1023 
   1024  if (options.lazyProto()) {
   1025    MOZ_ASSERT(!proto_);
   1026    proto_ = TaggedProto::LazyProto;
   1027  }
   1028 
   1029  return ProxyObject::New(cx, handler, priv, TaggedProto(proto_),
   1030                          options.clasp());
   1031 }
   1032 
   1033 void ProxyObject::renew(const BaseProxyHandler* handler, const Value& priv) {
   1034  MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
   1035  MOZ_ASSERT(getClass() == &ProxyClass);
   1036  MOZ_ASSERT(!IsWindowProxy(this));
   1037  MOZ_ASSERT(hasDynamicPrototype());
   1038 
   1039  setHandler(handler);
   1040  setCrossCompartmentPrivate(priv);
   1041  for (size_t i = 0; i < numReservedSlots(); i++) {
   1042    setReservedSlot(i, UndefinedValue());
   1043  }
   1044 }
   1045 
   1046 // This implementation of HostEnsureCanAddPrivateElement is designed to work in
   1047 // collaboration with Gecko to support the HTML implementation, which applies
   1048 // only to Proxy type objects, and as a result we can simply provide proxy
   1049 // handlers to correctly match the required semantics.
   1050 bool DefaultHostEnsureCanAddPrivateElementCallback(JSContext* cx,
   1051                                                   HandleValue val) {
   1052  if (!val.isObject()) {
   1053    return true;
   1054  }
   1055 
   1056  Rooted<JSObject*> valObj(cx, &val.toObject());
   1057  if (!IsProxy(valObj)) {
   1058    return true;
   1059  }
   1060 
   1061  if (GetProxyHandler(valObj)->throwOnPrivateField()) {
   1062    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1063                              JSMSG_ILLEGAL_PRIVATE_EXOTIC);
   1064    return false;
   1065  }
   1066  return true;
   1067 }