tor-browser

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

BaseProxyHandler.cpp (13177B)


      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 "jsapi.h"
      8 #include "NamespaceImports.h"
      9 
     10 #include "gc/GC.h"
     11 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     12 #include "js/Proxy.h"
     13 #include "proxy/DeadObjectProxy.h"
     14 #include "vm/Interpreter.h"
     15 #include "vm/ProxyObject.h"
     16 #include "vm/WrapperObject.h"
     17 
     18 #include "vm/JSContext-inl.h"
     19 #include "vm/JSObject-inl.h"
     20 
     21 using namespace js;
     22 
     23 using JS::IsArrayAnswer;
     24 
     25 bool BaseProxyHandler::enter(JSContext* cx, HandleObject wrapper, HandleId id,
     26                             Action act, bool mayThrow, bool* bp) const {
     27  *bp = true;
     28  return true;
     29 }
     30 
     31 bool BaseProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id,
     32                           bool* bp) const {
     33  assertEnteredPolicy(cx, proxy, id, GET);
     34 
     35  // This method is not covered by any spec, but we follow ES 2016
     36  // (February 11, 2016) 9.1.7.1 fairly closely.
     37 
     38  // Step 2. (Step 1 is a superfluous assertion.)
     39  // Non-standard: Use our faster hasOwn trap.
     40  if (!hasOwn(cx, proxy, id, bp)) {
     41    return false;
     42  }
     43 
     44  // Step 3.
     45  if (*bp) {
     46    return true;
     47  }
     48 
     49  // The spec calls this variable "parent", but that word has weird
     50  // connotations in SpiderMonkey, so let's go with "proto".
     51  // Step 4.
     52  RootedObject proto(cx);
     53  if (!GetPrototype(cx, proxy, &proto)) {
     54    return false;
     55  }
     56 
     57  // Step 5.,5.a.
     58  if (proto) {
     59    return HasProperty(cx, proto, id, bp);
     60  }
     61 
     62  // Step 6.
     63  *bp = false;
     64  return true;
     65 }
     66 
     67 bool BaseProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id,
     68                              bool* bp) const {
     69  assertEnteredPolicy(cx, proxy, id, GET);
     70  Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
     71  if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) {
     72    return false;
     73  }
     74  *bp = desc.isSome();
     75  return true;
     76 }
     77 
     78 bool BaseProxyHandler::get(JSContext* cx, HandleObject proxy,
     79                           HandleValue receiver, HandleId id,
     80                           MutableHandleValue vp) const {
     81  assertEnteredPolicy(cx, proxy, id, GET);
     82 
     83  // This method is not covered by any spec, but we follow ES 2016
     84  // (January 21, 2016) 9.1.8 fairly closely.
     85 
     86  // Step 2. (Step 1 is a superfluous assertion.)
     87  Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
     88  if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) {
     89    return false;
     90  }
     91  if (desc.isSome()) {
     92    desc->assertComplete();
     93  }
     94 
     95  // Step 3.
     96  if (desc.isNothing()) {
     97    // The spec calls this variable "parent", but that word has weird
     98    // connotations in SpiderMonkey, so let's go with "proto".
     99    // Step 3.a.
    100    RootedObject proto(cx);
    101    if (!GetPrototype(cx, proxy, &proto)) {
    102      return false;
    103    }
    104 
    105    // Step 3.b.
    106    if (!proto) {
    107      vp.setUndefined();
    108      return true;
    109    }
    110 
    111    // Step 3.c.
    112    return GetProperty(cx, proto, receiver, id, vp);
    113  }
    114 
    115  // Step 4.
    116  if (desc->isDataDescriptor()) {
    117    vp.set(desc->value());
    118    return true;
    119  }
    120 
    121  // Step 5.
    122  MOZ_ASSERT(desc->isAccessorDescriptor());
    123  RootedObject getter(cx, desc->getter());
    124 
    125  // Step 6.
    126  if (!getter) {
    127    vp.setUndefined();
    128    return true;
    129  }
    130 
    131  // Step 7.
    132  RootedValue getterFunc(cx, ObjectValue(*getter));
    133  return CallGetter(cx, receiver, getterFunc, vp);
    134 }
    135 
    136 bool BaseProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id,
    137                           HandleValue v, HandleValue receiver,
    138                           ObjectOpResult& result) const {
    139  assertEnteredPolicy(cx, proxy, id, SET);
    140 
    141  // This method is not covered by any spec, but we follow ES6 draft rev 28
    142  // (2014 Oct 14) 9.1.9 fairly closely, adapting it slightly for
    143  // SpiderMonkey's particular foibles.
    144 
    145  // Steps 2-3.  (Step 1 is a superfluous assertion.)
    146  Rooted<mozilla::Maybe<PropertyDescriptor>> ownDesc(cx);
    147  if (!getOwnPropertyDescriptor(cx, proxy, id, &ownDesc)) {
    148    return false;
    149  }
    150  if (ownDesc.isSome()) {
    151    ownDesc->assertComplete();
    152  }
    153 
    154  // The rest is factored out into a separate function with a weird name.
    155  // This algorithm continues just below.
    156  return SetPropertyIgnoringNamedGetter(cx, proxy, id, v, receiver, ownDesc,
    157                                        result);
    158 }
    159 
    160 bool js::SetPropertyIgnoringNamedGetter(
    161    JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
    162    HandleValue receiver, Handle<mozilla::Maybe<PropertyDescriptor>> ownDesc_,
    163    ObjectOpResult& result) {
    164  Rooted<PropertyDescriptor> ownDesc(cx);
    165 
    166  // Step 4.
    167  if (ownDesc_.isNothing()) {
    168    // The spec calls this variable "parent", but that word has weird
    169    // connotations in SpiderMonkey, so let's go with "proto".
    170    RootedObject proto(cx);
    171    if (!GetPrototype(cx, obj, &proto)) {
    172      return false;
    173    }
    174    if (proto) {
    175      return SetProperty(cx, proto, id, v, receiver, result);
    176    }
    177 
    178    // Step 4.d.
    179    ownDesc.set(PropertyDescriptor::Data(
    180        UndefinedValue(),
    181        {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
    182         JS::PropertyAttribute::Writable}));
    183  } else {
    184    ownDesc.set(*ownDesc_);
    185  }
    186 
    187  // Step 5.
    188  if (ownDesc.isDataDescriptor()) {
    189    // Steps 5.a-b.
    190    if (!ownDesc.writable()) {
    191      return result.fail(JSMSG_READ_ONLY);
    192    }
    193    if (!receiver.isObject()) {
    194      return result.fail(JSMSG_SET_NON_OBJECT_RECEIVER);
    195    }
    196    RootedObject receiverObj(cx, &receiver.toObject());
    197 
    198    // Steps 5.c-d.
    199    Rooted<mozilla::Maybe<PropertyDescriptor>> existingDescriptor(cx);
    200    if (!GetOwnPropertyDescriptor(cx, receiverObj, id, &existingDescriptor)) {
    201      return false;
    202    }
    203 
    204    // Step 5.e.
    205    if (existingDescriptor.isSome()) {
    206      // Step 5.e.i.
    207      if (existingDescriptor->isAccessorDescriptor()) {
    208        return result.fail(JSMSG_OVERWRITING_ACCESSOR);
    209      }
    210 
    211      // Step 5.e.ii.
    212      if (!existingDescriptor->writable()) {
    213        return result.fail(JSMSG_READ_ONLY);
    214      }
    215    }
    216 
    217    // Steps 5.e.iii-iv. and 5.f.i.
    218    Rooted<PropertyDescriptor> desc(cx);
    219    if (existingDescriptor.isSome()) {
    220      desc = PropertyDescriptor::Empty();
    221      desc.setValue(v);
    222    } else {
    223      desc = PropertyDescriptor::Data(v, {JS::PropertyAttribute::Configurable,
    224                                          JS::PropertyAttribute::Enumerable,
    225                                          JS::PropertyAttribute::Writable});
    226    }
    227    return DefineProperty(cx, receiverObj, id, desc, result);
    228  }
    229 
    230  // Step 6.
    231  MOZ_ASSERT(ownDesc.isAccessorDescriptor());
    232  RootedObject setter(cx);
    233  if (ownDesc.hasSetter()) {
    234    setter = ownDesc.setter();
    235  }
    236  if (!setter) {
    237    return result.fail(JSMSG_GETTER_ONLY);
    238  }
    239  RootedValue setterValue(cx, ObjectValue(*setter));
    240  if (!CallSetter(cx, receiver, setterValue, v)) {
    241    return false;
    242  }
    243  return result.succeed();
    244 }
    245 
    246 bool BaseProxyHandler::getOwnEnumerablePropertyKeys(
    247    JSContext* cx, HandleObject proxy, MutableHandleIdVector props) const {
    248  assertEnteredPolicy(cx, proxy, JS::PropertyKey::Void(), ENUMERATE);
    249  MOZ_ASSERT(props.length() == 0);
    250 
    251  if (!ownPropertyKeys(cx, proxy, props)) {
    252    return false;
    253  }
    254 
    255  /* Select only the enumerable properties through in-place iteration. */
    256  RootedId id(cx);
    257  size_t i = 0;
    258  for (size_t j = 0, len = props.length(); j < len; j++) {
    259    MOZ_ASSERT(i <= j);
    260    id = props[j];
    261    if (id.isSymbol()) {
    262      continue;
    263    }
    264 
    265    AutoWaivePolicy policy(cx, proxy, id, BaseProxyHandler::GET);
    266    Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
    267    if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) {
    268      return false;
    269    }
    270    if (desc.isSome()) {
    271      desc->assertComplete();
    272    }
    273 
    274    if (desc.isSome() && desc->enumerable()) {
    275      props[i++].set(id);
    276    }
    277  }
    278 
    279  MOZ_ASSERT(i <= props.length());
    280  if (!props.resize(i)) {
    281    return false;
    282  }
    283 
    284  return true;
    285 }
    286 
    287 bool BaseProxyHandler::enumerate(JSContext* cx, HandleObject proxy,
    288                                 MutableHandleIdVector props) const {
    289  assertEnteredPolicy(cx, proxy, JS::PropertyKey::Void(), ENUMERATE);
    290 
    291  // GetPropertyKeys will invoke getOwnEnumerablePropertyKeys along the proto
    292  // chain for us.
    293  MOZ_ASSERT(props.empty());
    294  return GetPropertyKeys(cx, proxy, 0, props);
    295 }
    296 
    297 bool BaseProxyHandler::call(JSContext* cx, HandleObject proxy,
    298                            const CallArgs& args) const {
    299  MOZ_CRASH("callable proxies should implement call trap");
    300 }
    301 
    302 bool BaseProxyHandler::construct(JSContext* cx, HandleObject proxy,
    303                                 const CallArgs& args) const {
    304  MOZ_CRASH("callable proxies should implement construct trap");
    305 }
    306 
    307 const char* BaseProxyHandler::className(JSContext* cx,
    308                                        HandleObject proxy) const {
    309  return proxy->isCallable() ? "Function" : "Object";
    310 }
    311 
    312 JSString* BaseProxyHandler::fun_toString(JSContext* cx, HandleObject proxy,
    313                                         bool isToSource) const {
    314  if (proxy->isCallable()) {
    315    return JS_NewStringCopyZ(cx, "function () {\n    [native code]\n}");
    316  }
    317 
    318  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    319                            JSMSG_INCOMPATIBLE_PROTO, "Function", "toString",
    320                            "object");
    321  return nullptr;
    322 }
    323 
    324 RegExpShared* BaseProxyHandler::regexp_toShared(JSContext* cx,
    325                                                HandleObject proxy) const {
    326  MOZ_CRASH("This should have been a wrapped regexp");
    327 }
    328 
    329 bool BaseProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy,
    330                                        MutableHandleValue vp) const {
    331  vp.setUndefined();
    332  return true;
    333 }
    334 
    335 bool BaseProxyHandler::nativeCall(JSContext* cx, IsAcceptableThis test,
    336                                  NativeImpl impl, const CallArgs& args) const {
    337  ReportIncompatible(cx, args);
    338  return false;
    339 }
    340 
    341 bool BaseProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy,
    342                                       ESClass* cls) const {
    343  *cls = ESClass::Other;
    344  return true;
    345 }
    346 
    347 bool BaseProxyHandler::isArray(JSContext* cx, HandleObject proxy,
    348                               IsArrayAnswer* answer) const {
    349  *answer = IsArrayAnswer::NotArray;
    350  return true;
    351 }
    352 
    353 void BaseProxyHandler::trace(JSTracer* trc, JSObject* proxy) const {}
    354 
    355 void BaseProxyHandler::finalize(JS::GCContext* gcx, JSObject* proxy) const {}
    356 
    357 size_t BaseProxyHandler::objectMoved(JSObject* proxy, JSObject* old) const {
    358  return 0;
    359 }
    360 
    361 bool BaseProxyHandler::getPrototype(JSContext* cx, HandleObject proxy,
    362                                    MutableHandleObject protop) const {
    363  MOZ_CRASH("must override getPrototype with dynamic prototype");
    364 }
    365 
    366 bool BaseProxyHandler::setPrototype(JSContext* cx, HandleObject proxy,
    367                                    HandleObject proto,
    368                                    ObjectOpResult& result) const {
    369  // Disallow sets of protos on proxies with dynamic prototypes but no hook.
    370  // This keeps us away from the footgun of having the first proto set opt
    371  // you out of having dynamic protos altogether.
    372  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    373                            JSMSG_CANT_SET_PROTO_OF, "incompatible Proxy");
    374  return false;
    375 }
    376 
    377 bool BaseProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy,
    378                                             bool* succeeded) const {
    379  *succeeded = false;
    380  return true;
    381 }
    382 
    383 bool BaseProxyHandler::getElements(JSContext* cx, HandleObject proxy,
    384                                   uint32_t begin, uint32_t end,
    385                                   ElementAdder* adder) const {
    386  assertEnteredPolicy(cx, proxy, JS::PropertyKey::Void(), GET);
    387 
    388  return js::GetElementsWithAdder(cx, proxy, proxy, begin, end, adder);
    389 }
    390 
    391 bool BaseProxyHandler::isCallable(JSObject* obj) const { return false; }
    392 
    393 bool BaseProxyHandler::isConstructor(JSObject* obj) const { return false; }
    394 
    395 JS_PUBLIC_API void js::NukeNonCCWProxy(JSContext* cx, HandleObject proxy) {
    396  MOZ_ASSERT(proxy->is<ProxyObject>());
    397  MOZ_ASSERT(!proxy->is<CrossCompartmentWrapperObject>());
    398 
    399  // (NotifyGCNukeWrapper() only needs to be called on CCWs.)
    400 
    401  // The proxy is about to be replaced, so we need to do any necessary
    402  // cleanup first.
    403  proxy->as<ProxyObject>().handler()->finalize(cx->gcContext(), proxy);
    404 
    405  proxy->as<ProxyObject>().nuke();
    406 
    407  MOZ_ASSERT(IsDeadProxyObject(proxy));
    408 }
    409 
    410 JS_PUBLIC_API void js::NukeRemovedCrossCompartmentWrapper(JSContext* cx,
    411                                                          JSObject* wrapper) {
    412  MOZ_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
    413 
    414  NotifyGCNukeWrapper(cx, wrapper);
    415 
    416  // We don't need to call finalize here because the CCW finalizer doesn't do
    417  // anything. Skipping finalize means that |wrapper| doesn't need to be rooted
    418  // to pass the hazard analysis, which is needed because this method is called
    419  // from some tricky places inside transplanting where rooting can be
    420  // difficult.
    421 
    422  wrapper->as<ProxyObject>().nuke();
    423 
    424  MOZ_ASSERT(IsDeadProxyObject(wrapper));
    425 }