tor-browser

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

ObjectOperations-inl.h (13568B)


      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 /* Fundamental operations on objects. */
      8 
      9 #ifndef vm_ObjectOperations_inl_h
     10 #define vm_ObjectOperations_inl_h
     11 
     12 #include "vm/ObjectOperations.h"
     13 
     14 #include "mozilla/Assertions.h"  // MOZ_ASSERT
     15 #include "mozilla/Attributes.h"  // MOZ_ALWAYS_INLINE
     16 #include "mozilla/Likely.h"      // MOZ_UNLIKELY
     17 
     18 #include <stdint.h>  // uint32_t
     19 
     20 #include "js/Class.h"  // js::{Delete,Get,Has}PropertyOp, JSMayResolveOp, JS::ObjectOpResult
     21 #include "js/GCAPI.h"         // JS::AutoSuppressGCAnalysis
     22 #include "js/Id.h"            // JS::PropertyKey, jsid
     23 #include "js/RootingAPI.h"    // JS::Handle, JS::MutableHandle, JS::Rooted
     24 #include "js/Value.h"         // JS::ObjectValue, JS::Value
     25 #include "proxy/Proxy.h"      // js::Proxy
     26 #include "vm/JSContext.h"     // JSContext
     27 #include "vm/JSObject.h"      // JSObject
     28 #include "vm/NativeObject.h"  // js::NativeObject, js::Native{Get,Has,Set}Property, js::NativeGetPropertyNoGC, js::Qualified
     29 #include "vm/ProxyObject.h"   // js::ProxyObject
     30 #include "vm/StringType.h"    // js::NameToId
     31 #include "vm/SymbolType.h"    // JS::Symbol
     32 
     33 #include "vm/JSAtomUtils-inl.h"  // js::PrimitiveValueToId, js::IndexToId
     34 
     35 namespace js {
     36 
     37 // The functions below are the fundamental operations on objects. See the
     38 // comment about "Standard internal methods" in jsapi.h.
     39 
     40 /*
     41 * ES6 [[GetPrototypeOf]]. Get obj's prototype, storing it in protop.
     42 *
     43 * If obj is definitely not a proxy, the infallible obj->getProto() can be used
     44 * instead. See the comment on JSObject::getTaggedProto().
     45 */
     46 inline bool GetPrototype(JSContext* cx, JS::Handle<JSObject*> obj,
     47                         JS::MutableHandle<JSObject*> protop) {
     48  if (obj->hasDynamicPrototype()) {
     49    MOZ_ASSERT(obj->is<ProxyObject>());
     50    return Proxy::getPrototype(cx, obj, protop);
     51  }
     52 
     53  protop.set(obj->staticPrototype());
     54  return true;
     55 }
     56 
     57 /*
     58 * ES6 [[IsExtensible]]. Extensible objects can have new properties defined on
     59 * them. Inextensible objects can't, and their [[Prototype]] slot is fixed as
     60 * well.
     61 */
     62 inline bool IsExtensible(JSContext* cx, JS::Handle<JSObject*> obj,
     63                         bool* extensible) {
     64  if (obj->is<ProxyObject>()) {
     65    return Proxy::isExtensible(cx, obj, extensible);
     66  }
     67 
     68  *extensible = obj->nonProxyIsExtensible();
     69 
     70  // If the following assertion fails, there's somewhere else a missing
     71  // call to shrinkCapacityToInitializedLength() which needs to be found and
     72  // fixed.
     73  MOZ_ASSERT_IF(obj->is<NativeObject>() && !*extensible,
     74                obj->as<NativeObject>().getDenseInitializedLength() ==
     75                    obj->as<NativeObject>().getDenseCapacity());
     76  return true;
     77 }
     78 
     79 /*
     80 * ES6 [[Has]]. Set *foundp to true if `id in obj` (that is, if obj has an own
     81 * or inherited property obj[id]), false otherwise.
     82 */
     83 inline bool HasProperty(JSContext* cx, JS::Handle<JSObject*> obj,
     84                        JS::Handle<jsid> id, bool* foundp) {
     85  if (HasPropertyOp op = obj->getOpsHasProperty()) {
     86    return op(cx, obj, id, foundp);
     87  }
     88 
     89  return NativeHasProperty(cx, obj.as<NativeObject>(), id, foundp);
     90 }
     91 
     92 inline bool HasProperty(JSContext* cx, JS::Handle<JSObject*> obj,
     93                        PropertyName* name, bool* foundp) {
     94  JS::Rooted<jsid> id(cx, NameToId(name));
     95  return HasProperty(cx, obj, id, foundp);
     96 }
     97 
     98 /*
     99 * ES6 [[Get]]. Get the value of the property `obj[id]`, or undefined if no
    100 * such property exists.
    101 *
    102 * Typically obj == receiver; if obj != receiver then the caller is most likely
    103 * a proxy using GetProperty to finish a property get that started out as
    104 * `receiver[id]`, and we've already searched the prototype chain up to `obj`.
    105 */
    106 inline bool GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
    107                        JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
    108                        JS::MutableHandle<JS::Value> vp) {
    109  if (GetPropertyOp op = obj->getOpsGetProperty()) {
    110    return op(cx, obj, receiver, id, vp);
    111  }
    112 
    113  return NativeGetProperty(cx, obj.as<NativeObject>(), receiver, id, vp);
    114 }
    115 
    116 inline bool GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
    117                        JS::Handle<JS::Value> receiver, PropertyName* name,
    118                        JS::MutableHandle<JS::Value> vp) {
    119  JS::Rooted<jsid> id(cx, NameToId(name));
    120  return GetProperty(cx, obj, receiver, id, vp);
    121 }
    122 
    123 inline bool GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
    124                        JS::Handle<JSObject*> receiver, JS::Handle<jsid> id,
    125                        JS::MutableHandle<JS::Value> vp) {
    126  JS::Rooted<JS::Value> receiverValue(cx, JS::ObjectValue(*receiver));
    127  return GetProperty(cx, obj, receiverValue, id, vp);
    128 }
    129 
    130 inline bool GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
    131                        JS::Handle<JSObject*> receiver, PropertyName* name,
    132                        JS::MutableHandle<JS::Value> vp) {
    133  JS::Rooted<JS::Value> receiverValue(cx, JS::ObjectValue(*receiver));
    134  return GetProperty(cx, obj, receiverValue, name, vp);
    135 }
    136 
    137 inline bool GetElement(JSContext* cx, JS::Handle<JSObject*> obj,
    138                       JS::Handle<JS::Value> receiver, uint32_t index,
    139                       JS::MutableHandle<JS::Value> vp) {
    140  JS::Rooted<jsid> id(cx);
    141  if (!IndexToId(cx, index, &id)) {
    142    return false;
    143  }
    144 
    145  return GetProperty(cx, obj, receiver, id, vp);
    146 }
    147 
    148 inline bool GetElement(JSContext* cx, JS::Handle<JSObject*> obj,
    149                       JS::Handle<JSObject*> receiver, uint32_t index,
    150                       JS::MutableHandle<JS::Value> vp) {
    151  JS::Rooted<JS::Value> receiverValue(cx, JS::ObjectValue(*receiver));
    152  return GetElement(cx, obj, receiverValue, index, vp);
    153 }
    154 
    155 inline bool GetElementLargeIndex(JSContext* cx, JS::Handle<JSObject*> obj,
    156                                 JS::Handle<JSObject*> receiver, uint64_t index,
    157                                 JS::MutableHandle<JS::Value> vp) {
    158  JS::Rooted<jsid> id(cx);
    159  if (!IndexToId(cx, index, &id)) {
    160    return false;
    161  }
    162 
    163  JS::Rooted<JS::Value> receiverValue(cx, JS::ObjectValue(*receiver));
    164  return GetProperty(cx, obj, receiverValue, id, vp);
    165 }
    166 
    167 inline bool GetPropertyNoGC(JSContext* cx, JSObject* obj,
    168                            const JS::Value& receiver, jsid id, JS::Value* vp) {
    169  if (obj->getOpsGetProperty()) {
    170    return false;
    171  }
    172 
    173  return NativeGetPropertyNoGC(cx, &obj->as<NativeObject>(), receiver, id, vp);
    174 }
    175 
    176 inline bool GetPropertyNoGC(JSContext* cx, JSObject* obj,
    177                            const JS::Value& receiver, PropertyName* name,
    178                            JS::Value* vp) {
    179  return GetPropertyNoGC(cx, obj, receiver, NameToId(name), vp);
    180 }
    181 
    182 inline bool GetElementNoGC(JSContext* cx, JSObject* obj,
    183                           const JS::Value& receiver, uint32_t index,
    184                           JS::Value* vp) {
    185  if (obj->getOpsGetProperty()) {
    186    return false;
    187  }
    188 
    189  if (index > PropertyKey::IntMax) {
    190    return false;
    191  }
    192 
    193  return GetPropertyNoGC(cx, obj, receiver, PropertyKey::Int(index), vp);
    194 }
    195 
    196 static MOZ_ALWAYS_INLINE bool ClassMayResolveId(const JSAtomState& names,
    197                                                const JSClass* clasp, jsid id,
    198                                                JSObject* maybeObj) {
    199  MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp);
    200 
    201  if (!clasp->getResolve()) {
    202    // Sanity check: we should only have a mayResolve hook if we have a
    203    // resolve hook.
    204    MOZ_ASSERT(!clasp->getMayResolve(),
    205               "Class with mayResolve hook but no resolve hook");
    206    return false;
    207  }
    208 
    209  if (JSMayResolveOp mayResolve = clasp->getMayResolve()) {
    210    // Tell the analysis our mayResolve hooks won't trigger GC.
    211    JS::AutoSuppressGCAnalysis nogc;
    212    if (!mayResolve(names, id, maybeObj)) {
    213      return false;
    214    }
    215  }
    216 
    217  return true;
    218 }
    219 
    220 // Returns whether |obj| or an object on its proto chain may have an interesting
    221 // symbol property (see JSObject::hasInterestingSymbolProperty). If it returns
    222 // true, *holder is set to the object that may have this property.
    223 MOZ_ALWAYS_INLINE bool MaybeHasInterestingSymbolProperty(
    224    JSContext* cx, JSObject* obj, JS::Symbol* symbol,
    225    JSObject** holder /* = nullptr */) {
    226  MOZ_ASSERT(symbol->isInterestingSymbol());
    227 
    228  jsid id = PropertyKey::Symbol(symbol);
    229  do {
    230    if (obj->maybeHasInterestingSymbolProperty() ||
    231        MOZ_UNLIKELY(
    232            ClassMayResolveId(cx->names(), obj->getClass(), id, obj))) {
    233      if (holder) {
    234        *holder = obj;
    235      }
    236      return true;
    237    }
    238    obj = obj->staticPrototype();
    239  } while (obj);
    240 
    241  return false;
    242 }
    243 
    244 // Like GetProperty but optimized for interesting symbol properties like
    245 // @@toStringTag.
    246 MOZ_ALWAYS_INLINE bool GetInterestingSymbolProperty(
    247    JSContext* cx, JS::Handle<JSObject*> obj, JS::Symbol* sym,
    248    JS::MutableHandle<JS::Value> vp) {
    249  JSObject* holder;
    250  if (!MaybeHasInterestingSymbolProperty(cx, obj, sym, &holder)) {
    251 #ifdef DEBUG
    252    JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
    253    JS::Rooted<jsid> id(cx, PropertyKey::Symbol(sym));
    254    if (!GetProperty(cx, obj, receiver, id, vp)) {
    255      return false;
    256    }
    257    MOZ_ASSERT(vp.isUndefined());
    258 #endif
    259 
    260    vp.setUndefined();
    261    return true;
    262  }
    263 
    264  JS::Rooted<JSObject*> holderRoot(cx, holder);
    265  JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
    266  JS::Rooted<jsid> id(cx, PropertyKey::Symbol(sym));
    267  return GetProperty(cx, holderRoot, receiver, id, vp);
    268 }
    269 
    270 /*
    271 * ES6 [[Set]]. Carry out the assignment `obj[id] = v`.
    272 *
    273 * The `receiver` argument has to do with how [[Set]] interacts with the
    274 * prototype chain and proxies. It's hard to explain and ES6 doesn't really
    275 * try. Long story short, if you just want bog-standard assignment, pass
    276 * `ObjectValue(*obj)` as receiver. Or better, use one of the signatures that
    277 * doesn't have a receiver parameter.
    278 *
    279 * Callers pass obj != receiver e.g. when a proxy is involved, obj is the
    280 * proxy's target, and the proxy is using SetProperty to finish an assignment
    281 * that started out as `receiver[id] = v`, by delegating it to obj.
    282 */
    283 inline bool SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
    284                        JS::Handle<jsid> id, JS::Handle<JS::Value> v,
    285                        JS::Handle<JS::Value> receiver,
    286                        JS::ObjectOpResult& result) {
    287  if (obj->getOpsSetProperty()) {
    288    return JSObject::nonNativeSetProperty(cx, obj, id, v, receiver, result);
    289  }
    290 
    291  return NativeSetProperty<Qualified>(cx, obj.as<NativeObject>(), id, v,
    292                                      receiver, result);
    293 }
    294 
    295 inline bool SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
    296                        JS::Handle<jsid> id, JS::Handle<JS::Value> v) {
    297  JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
    298  JS::ObjectOpResult result;
    299  return SetProperty(cx, obj, id, v, receiver, result) &&
    300         result.checkStrict(cx, obj, id);
    301 }
    302 
    303 inline bool SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
    304                        PropertyName* name, JS::Handle<JS::Value> v,
    305                        JS::Handle<JS::Value> receiver,
    306                        JS::ObjectOpResult& result) {
    307  JS::Rooted<jsid> id(cx, NameToId(name));
    308  return SetProperty(cx, obj, id, v, receiver, result);
    309 }
    310 
    311 inline bool SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
    312                        PropertyName* name, JS::Handle<JS::Value> v) {
    313  JS::Rooted<jsid> id(cx, NameToId(name));
    314  JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
    315  JS::ObjectOpResult result;
    316  return SetProperty(cx, obj, id, v, receiver, result) &&
    317         result.checkStrict(cx, obj, id);
    318 }
    319 
    320 inline bool SetElement(JSContext* cx, JS::Handle<JSObject*> obj, uint32_t index,
    321                       JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
    322                       JS::ObjectOpResult& result) {
    323  if (obj->getOpsSetProperty()) {
    324    return JSObject::nonNativeSetElement(cx, obj, index, v, receiver, result);
    325  }
    326 
    327  return NativeSetElement(cx, obj.as<NativeObject>(), index, v, receiver,
    328                          result);
    329 }
    330 
    331 /*
    332 * ES6 draft rev 31 (15 Jan 2015) 7.3.3 Put (O, P, V, Throw), except that on
    333 * success, the spec says this is supposed to return a boolean value, which we
    334 * don't bother doing.
    335 */
    336 inline bool PutProperty(JSContext* cx, JS::Handle<JSObject*> obj,
    337                        JS::Handle<jsid> id, JS::Handle<JS::Value> v,
    338                        bool strict) {
    339  JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
    340  JS::ObjectOpResult result;
    341  return SetProperty(cx, obj, id, v, receiver, result) &&
    342         result.checkStrictModeError(cx, obj, id, strict);
    343 }
    344 
    345 /*
    346 * ES6 [[Delete]]. Equivalent to the JS code `delete obj[id]`.
    347 */
    348 inline bool DeleteProperty(JSContext* cx, JS::Handle<JSObject*> obj,
    349                           JS::Handle<jsid> id, JS::ObjectOpResult& result) {
    350  if (DeletePropertyOp op = obj->getOpsDeleteProperty()) {
    351    return op(cx, obj, id, result);
    352  }
    353 
    354  return NativeDeleteProperty(cx, obj.as<NativeObject>(), id, result);
    355 }
    356 
    357 inline bool DeleteElement(JSContext* cx, JS::Handle<JSObject*> obj,
    358                          uint32_t index, JS::ObjectOpResult& result) {
    359  JS::Rooted<jsid> id(cx);
    360  if (!IndexToId(cx, index, &id)) {
    361    return false;
    362  }
    363 
    364  return DeleteProperty(cx, obj, id, result);
    365 }
    366 
    367 } /* namespace js */
    368 
    369 #endif /* vm_ObjectOperations_inl_h */