js/src/jsobjinlines.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     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/. */
     7 #ifndef jsobjinlines_h
     8 #define jsobjinlines_h
    10 #include "jsobj.h"
    12 #include "vm/ArrayObject.h"
    13 #include "vm/DateObject.h"
    14 #include "vm/NumberObject.h"
    15 #include "vm/Probes.h"
    16 #include "vm/ScopeObject.h"
    17 #include "vm/StringObject.h"
    19 #include "jsatominlines.h"
    20 #include "jscompartmentinlines.h"
    21 #include "jsgcinlines.h"
    22 #include "jsinferinlines.h"
    24 #include "vm/ObjectImpl-inl.h"
    26 /* static */ inline bool
    27 JSObject::setGenericAttributes(JSContext *cx, js::HandleObject obj,
    28                                js::HandleId id, unsigned *attrsp)
    29 {
    30     js::types::MarkTypePropertyNonData(cx, obj, id);
    31     js::GenericAttributesOp op = obj->getOps()->setGenericAttributes;
    32     return (op ? op : js::baseops::SetAttributes)(cx, obj, id, attrsp);
    33 }
    35 /* static */ inline bool
    36 JSObject::changePropertyAttributes(JSContext *cx, js::HandleObject obj,
    37                                    js::HandleShape shape, unsigned attrs)
    38 {
    39     return !!changeProperty<js::SequentialExecution>(cx, obj, shape, attrs, 0,
    40                                                      shape->getter(), shape->setter());
    41 }
    43 /* static */ inline bool
    44 JSObject::deleteProperty(JSContext *cx, js::HandleObject obj, js::HandlePropertyName name,
    45                          bool *succeeded)
    46 {
    47     JS::RootedId id(cx, js::NameToId(name));
    48     js::types::MarkTypePropertyNonData(cx, obj, id);
    49     js::DeletePropertyOp op = obj->getOps()->deleteProperty;
    50     return (op ? op : js::baseops::DeleteProperty)(cx, obj, name, succeeded);
    51 }
    53 /* static */ inline bool
    54 JSObject::deleteElement(JSContext *cx, js::HandleObject obj, uint32_t index, bool *succeeded)
    55 {
    56     JS::RootedId id(cx);
    57     if (!js::IndexToId(cx, index, &id))
    58         return false;
    59     js::types::MarkTypePropertyNonData(cx, obj, id);
    60     js::DeleteElementOp op = obj->getOps()->deleteElement;
    61     return (op ? op : js::baseops::DeleteElement)(cx, obj, index, succeeded);
    62 }
    64 /* static */ inline bool
    65 JSObject::watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
    66                 JS::HandleObject callable)
    67 {
    68     js::WatchOp op = obj->getOps()->watch;
    69     return (op ? op : js::baseops::Watch)(cx, obj, id, callable);
    70 }
    72 /* static */ inline bool
    73 JSObject::unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id)
    74 {
    75     js::UnwatchOp op = obj->getOps()->unwatch;
    76     return (op ? op : js::baseops::Unwatch)(cx, obj, id);
    77 }
    79 inline void
    80 JSObject::finalize(js::FreeOp *fop)
    81 {
    82     js::probes::FinalizeObject(this);
    84 #ifdef DEBUG
    85     JS_ASSERT(isTenured());
    86     if (!IsBackgroundFinalized(tenuredGetAllocKind())) {
    87         /* Assert we're on the main thread. */
    88         JS_ASSERT(CurrentThreadCanAccessRuntime(fop->runtime()));
    89     }
    90 #endif
    91     const js::Class *clasp = getClass();
    92     if (clasp->finalize)
    93         clasp->finalize(fop, this);
    95     finish(fop);
    96 }
    98 inline void
    99 JSObject::setLastPropertyInfallible(js::Shape *shape)
   100 {
   101     JS_ASSERT(!shape->inDictionary());
   102     JS_ASSERT(shape->compartment() == compartment());
   103     JS_ASSERT(!inDictionaryMode());
   104     JS_ASSERT(slotSpan() == shape->slotSpan());
   105     JS_ASSERT(numFixedSlots() == shape->numFixedSlots());
   107     shape_ = shape;
   108 }
   110 inline void
   111 JSObject::removeLastProperty(js::ExclusiveContext *cx)
   112 {
   113     JS_ASSERT(canRemoveLastProperty());
   114     JS::RootedObject self(cx, this);
   115     js::RootedShape prev(cx, lastProperty()->previous());
   116     JS_ALWAYS_TRUE(setLastProperty(cx, self, prev));
   117 }
   119 inline bool
   120 JSObject::canRemoveLastProperty()
   121 {
   122     /*
   123      * Check that the information about the object stored in the last
   124      * property's base shape is consistent with that stored in the previous
   125      * shape. If not consistent, then the last property cannot be removed as it
   126      * will induce a change in the object itself, and the object must be
   127      * converted to dictionary mode instead. See BaseShape comment in jsscope.h
   128      */
   129     JS_ASSERT(!inDictionaryMode());
   130     js::Shape *previous = lastProperty()->previous().get();
   131     return previous->getObjectParent() == lastProperty()->getObjectParent()
   132         && previous->getObjectMetadata() == lastProperty()->getObjectMetadata()
   133         && previous->getObjectFlags() == lastProperty()->getObjectFlags();
   134 }
   136 inline void
   137 JSObject::setShouldConvertDoubleElements()
   138 {
   139     JS_ASSERT(is<js::ArrayObject>() && !hasEmptyElements());
   140     getElementsHeader()->setShouldConvertDoubleElements();
   141 }
   143 inline void
   144 JSObject::clearShouldConvertDoubleElements()
   145 {
   146     JS_ASSERT(is<js::ArrayObject>() && !hasEmptyElements());
   147     getElementsHeader()->clearShouldConvertDoubleElements();
   148 }
   150 inline bool
   151 JSObject::setDenseElementIfHasType(uint32_t index, const js::Value &val)
   152 {
   153     if (!js::types::HasTypePropertyId(this, JSID_VOID, val))
   154         return false;
   155     setDenseElementMaybeConvertDouble(index, val);
   156     return true;
   157 }
   159 inline void
   160 JSObject::setDenseElementWithType(js::ExclusiveContext *cx, uint32_t index,
   161                                   const js::Value &val)
   162 {
   163     // Avoid a slow AddTypePropertyId call if the type is the same as the type
   164     // of the previous element.
   165     js::types::Type thisType = js::types::GetValueType(val);
   166     if (index == 0 || js::types::GetValueType(elements[index - 1]) != thisType)
   167         js::types::AddTypePropertyId(cx, this, JSID_VOID, thisType);
   168     setDenseElementMaybeConvertDouble(index, val);
   169 }
   171 inline void
   172 JSObject::initDenseElementWithType(js::ExclusiveContext *cx, uint32_t index,
   173                                    const js::Value &val)
   174 {
   175     JS_ASSERT(!shouldConvertDoubleElements());
   176     js::types::AddTypePropertyId(cx, this, JSID_VOID, val);
   177     initDenseElement(index, val);
   178 }
   180 inline void
   181 JSObject::setDenseElementHole(js::ExclusiveContext *cx, uint32_t index)
   182 {
   183     js::types::MarkTypeObjectFlags(cx, this, js::types::OBJECT_FLAG_NON_PACKED);
   184     setDenseElement(index, js::MagicValue(JS_ELEMENTS_HOLE));
   185 }
   187 /* static */ inline void
   188 JSObject::removeDenseElementForSparseIndex(js::ExclusiveContext *cx,
   189                                            js::HandleObject obj, uint32_t index)
   190 {
   191     js::types::MarkTypeObjectFlags(cx, obj,
   192                                    js::types::OBJECT_FLAG_NON_PACKED |
   193                                    js::types::OBJECT_FLAG_SPARSE_INDEXES);
   194     if (obj->containsDenseElement(index))
   195         obj->setDenseElement(index, js::MagicValue(JS_ELEMENTS_HOLE));
   196 }
   198 inline bool
   199 JSObject::writeToIndexWouldMarkNotPacked(uint32_t index)
   200 {
   201     return getElementsHeader()->initializedLength < index;
   202 }
   204 inline void
   205 JSObject::markDenseElementsNotPacked(js::ExclusiveContext *cx)
   206 {
   207     JS_ASSERT(isNative());
   208     MarkTypeObjectFlags(cx, this, js::types::OBJECT_FLAG_NON_PACKED);
   209 }
   211 inline void
   212 JSObject::ensureDenseInitializedLengthNoPackedCheck(js::ThreadSafeContext *cx, uint32_t index,
   213                                                     uint32_t extra)
   214 {
   215     JS_ASSERT(cx->isThreadLocal(this));
   217     /*
   218      * Ensure that the array's contents have been initialized up to index, and
   219      * mark the elements through 'index + extra' as initialized in preparation
   220      * for a write.
   221      */
   222     JS_ASSERT(index + extra <= getDenseCapacity());
   223     uint32_t &initlen = getElementsHeader()->initializedLength;
   225     if (initlen < index + extra) {
   226         JSRuntime *rt = runtimeFromAnyThread();
   227         size_t offset = initlen;
   228         for (js::HeapSlot *sp = elements + initlen;
   229              sp != elements + (index + extra);
   230              sp++, offset++)
   231             sp->init(rt, this, js::HeapSlot::Element, offset, js::MagicValue(JS_ELEMENTS_HOLE));
   232         initlen = index + extra;
   233     }
   234 }
   236 inline void
   237 JSObject::ensureDenseInitializedLength(js::ExclusiveContext *cx, uint32_t index, uint32_t extra)
   238 {
   239     if (writeToIndexWouldMarkNotPacked(index))
   240         markDenseElementsNotPacked(cx);
   241     ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
   242 }
   244 inline void
   245 JSObject::ensureDenseInitializedLengthPreservePackedFlag(js::ThreadSafeContext *cx,
   246                                                          uint32_t index, uint32_t extra)
   247 {
   248     JS_ASSERT(!writeToIndexWouldMarkNotPacked(index));
   249     ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
   250 }
   252 JSObject::EnsureDenseResult
   253 JSObject::extendDenseElements(js::ThreadSafeContext *cx,
   254                               uint32_t requiredCapacity, uint32_t extra)
   255 {
   256     JS_ASSERT(cx->isThreadLocal(this));
   258     /*
   259      * Don't grow elements for non-extensible objects or watched objects. Dense
   260      * elements can be added/written with no extensible or watchpoint checks as
   261      * long as there is capacity for them.
   262      */
   263     if (!nonProxyIsExtensible() || watched()) {
   264         JS_ASSERT(getDenseCapacity() == 0);
   265         return ED_SPARSE;
   266     }
   268     /*
   269      * Don't grow elements for objects which already have sparse indexes.
   270      * This avoids needing to count non-hole elements in willBeSparseElements
   271      * every time a new index is added.
   272      */
   273     if (isIndexed())
   274         return ED_SPARSE;
   276     /*
   277      * We use the extra argument also as a hint about number of non-hole
   278      * elements to be inserted.
   279      */
   280     if (requiredCapacity > MIN_SPARSE_INDEX &&
   281         willBeSparseElements(requiredCapacity, extra)) {
   282         return ED_SPARSE;
   283     }
   285     if (!growElements(cx, requiredCapacity))
   286         return ED_FAILED;
   288     return ED_OK;
   289 }
   291 inline JSObject::EnsureDenseResult
   292 JSObject::ensureDenseElementsNoPackedCheck(js::ThreadSafeContext *cx, uint32_t index, uint32_t extra)
   293 {
   294     JS_ASSERT(isNative());
   296     uint32_t currentCapacity = getDenseCapacity();
   298     uint32_t requiredCapacity;
   299     if (extra == 1) {
   300         /* Optimize for the common case. */
   301         if (index < currentCapacity) {
   302             ensureDenseInitializedLengthNoPackedCheck(cx, index, 1);
   303             return ED_OK;
   304         }
   305         requiredCapacity = index + 1;
   306         if (requiredCapacity == 0) {
   307             /* Overflow. */
   308             return ED_SPARSE;
   309         }
   310     } else {
   311         requiredCapacity = index + extra;
   312         if (requiredCapacity < index) {
   313             /* Overflow. */
   314             return ED_SPARSE;
   315         }
   316         if (requiredCapacity <= currentCapacity) {
   317             ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
   318             return ED_OK;
   319         }
   320     }
   322     EnsureDenseResult edr = extendDenseElements(cx, requiredCapacity, extra);
   323     if (edr != ED_OK)
   324         return edr;
   326     ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
   327     return ED_OK;
   328 }
   330 inline JSObject::EnsureDenseResult
   331 JSObject::ensureDenseElements(js::ExclusiveContext *cx, uint32_t index, uint32_t extra)
   332 {
   333     if (writeToIndexWouldMarkNotPacked(index))
   334         markDenseElementsNotPacked(cx);
   335     return ensureDenseElementsNoPackedCheck(cx, index, extra);
   336 }
   338 inline JSObject::EnsureDenseResult
   339 JSObject::ensureDenseElementsPreservePackedFlag(js::ThreadSafeContext *cx, uint32_t index,
   340                                                 uint32_t extra)
   341 {
   342     JS_ASSERT(!writeToIndexWouldMarkNotPacked(index));
   343     return ensureDenseElementsNoPackedCheck(cx, index, extra);
   344 }
   346 inline js::Value
   347 JSObject::getDenseOrTypedArrayElement(uint32_t idx)
   348 {
   349     if (is<js::TypedArrayObject>())
   350         return as<js::TypedArrayObject>().getElement(idx);
   351     return getDenseElement(idx);
   352 }
   354 /* static */ inline bool
   355 JSObject::setSingletonType(js::ExclusiveContext *cx, js::HandleObject obj)
   356 {
   357     JS_ASSERT_IF(cx->isJSContext(),
   358                  !IsInsideNursery(cx->asJSContext()->runtime(), obj.get()));
   360     js::types::TypeObject *type = cx->getSingletonType(obj->getClass(), obj->getTaggedProto());
   361     if (!type)
   362         return false;
   364     obj->type_ = type;
   365     return true;
   366 }
   368 inline js::types::TypeObject*
   369 JSObject::getType(JSContext *cx)
   370 {
   371     JS_ASSERT(cx->compartment() == compartment());
   372     if (hasLazyType()) {
   373         JS::RootedObject self(cx, this);
   374         if (cx->compartment() != compartment())
   375             MOZ_CRASH();
   376         return makeLazyType(cx, self);
   377     }
   378     return static_cast<js::types::TypeObject*>(type_);
   379 }
   381 /* static */ inline bool
   382 JSObject::clearType(JSContext *cx, js::HandleObject obj)
   383 {
   384     JS_ASSERT(!obj->hasSingletonType());
   385     JS_ASSERT(cx->compartment() == obj->compartment());
   387     js::types::TypeObject *type = cx->getNewType(obj->getClass(), nullptr);
   388     if (!type)
   389         return false;
   391     obj->type_ = type;
   392     return true;
   393 }
   395 inline void
   396 JSObject::setType(js::types::TypeObject *newType)
   397 {
   398     JS_ASSERT(newType);
   399     JS_ASSERT(!hasSingletonType());
   400     type_ = newType;
   401 }
   403 /* static */ inline bool
   404 JSObject::getProto(JSContext *cx, js::HandleObject obj, js::MutableHandleObject protop)
   405 {
   406     if (obj->getTaggedProto().isLazy()) {
   407         JS_ASSERT(obj->is<js::ProxyObject>());
   408         return js::Proxy::getPrototypeOf(cx, obj, protop);
   409     } else {
   410         protop.set(obj->getTaggedProto().toObjectOrNull());
   411         return true;
   412     }
   413 }
   415 /* static */ inline bool
   416 JSObject::setProto(JSContext *cx, JS::HandleObject obj, JS::HandleObject proto, bool *succeeded)
   417 {
   418     /* Proxies live in their own little world. */
   419     if (obj->getTaggedProto().isLazy()) {
   420         JS_ASSERT(obj->is<js::ProxyObject>());
   421         return js::Proxy::setPrototypeOf(cx, obj, proto, succeeded);
   422     }
   424     /*
   425      * Disallow mutating the [[Prototype]] on ArrayBuffer objects, which
   426      * due to their complicated delegate-object shenanigans can't easily
   427      * have a mutable [[Prototype]].
   428      */
   429     if (obj->is<js::ArrayBufferObject>()) {
   430         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
   431                              "incompatible ArrayBuffer");
   432         return false;
   433     }
   435     /*
   436      * Disallow mutating the [[Prototype]] on Typed Objects, per the spec.
   437      */
   438     if (obj->is<js::TypedObject>()) {
   439         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
   440                              "incompatible TypedObject");
   441         return false;
   442     }
   444     /*
   445      * Explicitly disallow mutating the [[Prototype]] of Location objects
   446      * for flash-related security reasons.
   447      */
   448     if (!strcmp(obj->getClass()->name, "Location")) {
   449         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
   450                              "incompatible Location object");
   451         return false;
   452     }
   454     /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
   455     bool extensible;
   456     if (!JSObject::isExtensible(cx, obj, &extensible))
   457         return false;
   458     if (!extensible) {
   459         *succeeded = false;
   460         return true;
   461     }
   463     /* ES6 9.1.2 step 6 forbids generating cyclical prototype chains. */
   464     js::RootedObject obj2(cx);
   465     for (obj2 = proto; obj2; ) {
   466         if (obj2 == obj) {
   467             *succeeded = false;
   468             return true;
   469         }
   471         if (!JSObject::getProto(cx, obj2, &obj2))
   472             return false;
   473     }
   475     return SetClassAndProto(cx, obj, obj->getClass(), proto, succeeded);
   476 }
   478 inline bool
   479 JSObject::isVarObj()
   480 {
   481     if (is<js::DebugScopeObject>())
   482         return as<js::DebugScopeObject>().scope().isVarObj();
   483     return lastProperty()->hasObjectFlag(js::BaseShape::VAROBJ);
   484 }
   486 /* static */ inline JSObject *
   487 JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
   488                  js::HandleShape shape, js::HandleTypeObject type,
   489                  js::HeapSlot *extantSlots /* = nullptr */)
   490 {
   491     /*
   492      * Callers must use dynamicSlotsCount to size the initial slot array of the
   493      * object. We can't check the allocated capacity of the dynamic slots, but
   494      * make sure their presence is consistent with the shape.
   495      */
   496     JS_ASSERT(shape && type);
   497     JS_ASSERT(type->clasp() == shape->getObjectClass());
   498     JS_ASSERT(type->clasp() != &js::ArrayObject::class_);
   499     JS_ASSERT_IF(!ClassCanHaveFixedData(type->clasp()),
   500                  js::gc::GetGCKindSlots(kind, type->clasp()) == shape->numFixedSlots());
   501     JS_ASSERT_IF(type->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind));
   502     JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap);
   503     JS_ASSERT_IF(extantSlots, dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(),
   504                                                 type->clasp()));
   506     const js::Class *clasp = type->clasp();
   507     size_t nDynamicSlots = 0;
   508     if (!extantSlots)
   509         nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp);
   511     JSObject *obj = js::NewGCObject<js::CanGC>(cx, kind, nDynamicSlots, heap);
   512     if (!obj)
   513         return nullptr;
   515     obj->shape_.init(shape);
   516     obj->type_.init(type);
   517     if (extantSlots) {
   518 #ifdef JSGC_GENERATIONAL
   519         if (cx->isJSContext())
   520             cx->asJSContext()->runtime()->gcNursery.notifyInitialSlots(obj, extantSlots);
   521 #endif
   522         obj->slots = extantSlots;
   523     }
   524     obj->elements = js::emptyObjectElements;
   526     if (clasp->hasPrivate())
   527         obj->privateRef(shape->numFixedSlots()) = nullptr;
   529     size_t span = shape->slotSpan();
   530     if (span)
   531         obj->initializeSlotRange(0, span);
   533     // JSFunction's fixed slots expect POD-style initialization.
   534     if (type->clasp()->isJSFunction())
   535         memset(obj->fixedSlots(), 0, sizeof(js::HeapSlot) * GetGCKindSlots(kind));
   537     return obj;
   538 }
   540 /* static */ inline js::ArrayObject *
   541 JSObject::createArray(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
   542                       js::HandleShape shape, js::HandleTypeObject type,
   543                       uint32_t length)
   544 {
   545     JS_ASSERT(shape && type);
   546     JS_ASSERT(type->clasp() == shape->getObjectClass());
   547     JS_ASSERT(type->clasp() == &js::ArrayObject::class_);
   548     JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap);
   550     /*
   551      * Arrays use their fixed slots to store elements, and must have enough
   552      * space for the elements header and also be marked as having no space for
   553      * named properties stored in those fixed slots.
   554      */
   555     JS_ASSERT(shape->numFixedSlots() == 0);
   556     size_t nDynamicSlots = dynamicSlotsCount(0, shape->slotSpan(), type->clasp());
   557     JSObject *obj = js::NewGCObject<js::CanGC>(cx, kind, nDynamicSlots, heap);
   558     if (!obj)
   559         return nullptr;
   561     uint32_t capacity = js::gc::GetGCKindSlots(kind) - js::ObjectElements::VALUES_PER_HEADER;
   563     obj->shape_.init(shape);
   564     obj->type_.init(type);
   565     obj->setFixedElements();
   566     new (obj->getElementsHeader()) js::ObjectElements(capacity, length);
   568     size_t span = shape->slotSpan();
   569     if (span)
   570         obj->initializeSlotRange(0, span);
   572     return &obj->as<js::ArrayObject>();
   573 }
   575 inline void
   576 JSObject::finish(js::FreeOp *fop)
   577 {
   578     if (hasDynamicSlots())
   579         fop->free_(slots);
   581     if (hasDynamicElements()) {
   582         js::ObjectElements *elements = getElementsHeader();
   583         fop->free_(elements);
   584     }
   585 }
   587 /* static */ inline bool
   588 JSObject::hasProperty(JSContext *cx, js::HandleObject obj,
   589                       js::HandleId id, bool *foundp)
   590 {
   591     JS::RootedObject pobj(cx);
   592     js::RootedShape prop(cx);
   593     if (!lookupGeneric(cx, obj, id, &pobj, &prop)) {
   594         *foundp = false;  /* initialize to shut GCC up */
   595         return false;
   596     }
   597     *foundp = !!prop;
   598     return true;
   599 }
   601 inline bool
   602 JSObject::nativeSetSlotIfHasType(js::Shape *shape, const js::Value &value)
   603 {
   604     if (!js::types::HasTypePropertyId(this, shape->propid(), value))
   605         return false;
   606     nativeSetSlot(shape->slot(), value);
   607     return true;
   608 }
   610 inline void
   611 JSObject::nativeSetSlotWithType(js::ExclusiveContext *cx, js::Shape *shape,
   612                                 const js::Value &value)
   613 {
   614     nativeSetSlot(shape->slot(), value);
   615     js::types::AddTypePropertyId(cx, this, shape->propid(), value);
   616 }
   618 /* static */ inline bool
   619 JSObject::getElement(JSContext *cx, js::HandleObject obj, js::HandleObject receiver,
   620                      uint32_t index, js::MutableHandleValue vp)
   621 {
   622     js::ElementIdOp op = obj->getOps()->getElement;
   623     if (op)
   624         return op(cx, obj, receiver, index, vp);
   626     JS::RootedId id(cx);
   627     if (!js::IndexToId(cx, index, &id))
   628         return false;
   629     return getGeneric(cx, obj, receiver, id, vp);
   630 }
   632 /* static */ inline bool
   633 JSObject::getElementNoGC(JSContext *cx, JSObject *obj, JSObject *receiver,
   634                          uint32_t index, js::Value *vp)
   635 {
   636     js::ElementIdOp op = obj->getOps()->getElement;
   637     if (op)
   638         return false;
   640     if (index > JSID_INT_MAX)
   641         return false;
   642     return getGenericNoGC(cx, obj, receiver, INT_TO_JSID(index), vp);
   643 }
   645 inline js::GlobalObject &
   646 JSObject::global() const
   647 {
   648 #ifdef DEBUG
   649     JSObject *obj = const_cast<JSObject *>(this);
   650     while (JSObject *parent = obj->getParent())
   651         obj = parent;
   652 #endif
   653     return *compartment()->maybeGlobal();
   654 }
   656 inline bool
   657 JSObject::isOwnGlobal() const
   658 {
   659     return &global() == this;
   660 }
   662 namespace js {
   664 PropDesc::PropDesc(const Value &getter, const Value &setter,
   665                    Enumerability enumerable, Configurability configurable)
   666   : pd_(UndefinedValue()),
   667     value_(UndefinedValue()),
   668     get_(getter), set_(setter),
   669     attrs(JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED |
   670           (enumerable ? JSPROP_ENUMERATE : 0) |
   671           (configurable ? 0 : JSPROP_PERMANENT)),
   672     hasGet_(true), hasSet_(true),
   673     hasValue_(false), hasWritable_(false), hasEnumerable_(true), hasConfigurable_(true),
   674     isUndefined_(false)
   675 {
   676     MOZ_ASSERT(getter.isUndefined() || js_IsCallable(getter));
   677     MOZ_ASSERT(setter.isUndefined() || js_IsCallable(setter));
   678 }
   680 static MOZ_ALWAYS_INLINE bool
   681 IsFunctionObject(const js::Value &v)
   682 {
   683     return v.isObject() && v.toObject().is<JSFunction>();
   684 }
   686 static MOZ_ALWAYS_INLINE bool
   687 IsFunctionObject(const js::Value &v, JSFunction **fun)
   688 {
   689     if (v.isObject() && v.toObject().is<JSFunction>()) {
   690         *fun = &v.toObject().as<JSFunction>();
   691         return true;
   692     }
   693     return false;
   694 }
   696 static MOZ_ALWAYS_INLINE bool
   697 IsNativeFunction(const js::Value &v)
   698 {
   699     JSFunction *fun;
   700     return IsFunctionObject(v, &fun) && fun->isNative();
   701 }
   703 static MOZ_ALWAYS_INLINE bool
   704 IsNativeFunction(const js::Value &v, JSFunction **fun)
   705 {
   706     return IsFunctionObject(v, fun) && (*fun)->isNative();
   707 }
   709 static MOZ_ALWAYS_INLINE bool
   710 IsNativeFunction(const js::Value &v, JSNative native)
   711 {
   712     JSFunction *fun;
   713     return IsFunctionObject(v, &fun) && fun->maybeNative() == native;
   714 }
   716 /*
   717  * When we have an object of a builtin class, we don't quite know what its
   718  * valueOf/toString methods are, since these methods may have been overwritten
   719  * or shadowed. However, we can still do better than the general case by
   720  * hard-coding the necessary properties for us to find the native we expect.
   721  *
   722  * TODO: a per-thread shape-based cache would be faster and simpler.
   723  */
   724 static MOZ_ALWAYS_INLINE bool
   725 ClassMethodIsNative(JSContext *cx, JSObject *obj, const Class *clasp, jsid methodid, JSNative native)
   726 {
   727     JS_ASSERT(!obj->is<ProxyObject>());
   728     JS_ASSERT(obj->getClass() == clasp);
   730     Value v;
   731     if (!HasDataProperty(cx, obj, methodid, &v)) {
   732         JSObject *proto = obj->getProto();
   733         if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, proto, methodid, &v))
   734             return false;
   735     }
   737     return js::IsNativeFunction(v, native);
   738 }
   740 /* ES5 9.1 ToPrimitive(input). */
   741 static MOZ_ALWAYS_INLINE bool
   742 ToPrimitive(JSContext *cx, MutableHandleValue vp)
   743 {
   744     if (vp.isPrimitive())
   745         return true;
   747     JSObject *obj = &vp.toObject();
   749     /* Optimize new String(...).valueOf(). */
   750     if (obj->is<StringObject>()) {
   751         jsid id = NameToId(cx->names().valueOf);
   752         if (ClassMethodIsNative(cx, obj, &StringObject::class_, id, js_str_toString)) {
   753             vp.setString(obj->as<StringObject>().unbox());
   754             return true;
   755         }
   756     }
   758     /* Optimize new Number(...).valueOf(). */
   759     if (obj->is<NumberObject>()) {
   760         jsid id = NameToId(cx->names().valueOf);
   761         if (ClassMethodIsNative(cx, obj, &NumberObject::class_, id, js_num_valueOf)) {
   762             vp.setNumber(obj->as<NumberObject>().unbox());
   763             return true;
   764         }
   765     }
   767     RootedObject objRoot(cx, obj);
   768     return JSObject::defaultValue(cx, objRoot, JSTYPE_VOID, vp);
   769 }
   771 /* ES5 9.1 ToPrimitive(input, PreferredType). */
   772 static MOZ_ALWAYS_INLINE bool
   773 ToPrimitive(JSContext *cx, JSType preferredType, MutableHandleValue vp)
   774 {
   775     JS_ASSERT(preferredType != JSTYPE_VOID); /* Use the other ToPrimitive! */
   776     if (vp.isPrimitive())
   777         return true;
   778     RootedObject obj(cx, &vp.toObject());
   779     return JSObject::defaultValue(cx, obj, preferredType, vp);
   780 }
   782 /*
   783  * Return true if this is a compiler-created internal function accessed by
   784  * its own object. Such a function object must not be accessible to script
   785  * or embedding code.
   786  */
   787 inline bool
   788 IsInternalFunctionObject(JSObject *funobj)
   789 {
   790     JSFunction *fun = &funobj->as<JSFunction>();
   791     return fun->isLambda() && !funobj->getParent();
   792 }
   794 class AutoPropDescArrayRooter : private AutoGCRooter
   795 {
   796   public:
   797     AutoPropDescArrayRooter(JSContext *cx)
   798       : AutoGCRooter(cx, DESCRIPTORS), descriptors(cx)
   799     { }
   801     PropDesc *append() {
   802         if (!descriptors.append(PropDesc()))
   803             return nullptr;
   804         return &descriptors.back();
   805     }
   807     bool reserve(size_t n) {
   808         return descriptors.reserve(n);
   809     }
   811     PropDesc& operator[](size_t i) {
   812         JS_ASSERT(i < descriptors.length());
   813         return descriptors[i];
   814     }
   816     friend void AutoGCRooter::trace(JSTracer *trc);
   818   private:
   819     PropDescArray descriptors;
   820 };
   822 /*
   823  * Make an object with the specified prototype. If parent is null, it will
   824  * default to the prototype's global if the prototype is non-null.
   825  */
   826 JSObject *
   827 NewObjectWithGivenProto(ExclusiveContext *cx, const js::Class *clasp, TaggedProto proto, JSObject *parent,
   828                         gc::AllocKind allocKind, NewObjectKind newKind);
   830 inline JSObject *
   831 NewObjectWithGivenProto(ExclusiveContext *cx, const js::Class *clasp, TaggedProto proto, JSObject *parent,
   832                         NewObjectKind newKind = GenericObject)
   833 {
   834     gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
   835     return NewObjectWithGivenProto(cx, clasp, proto, parent, allocKind, newKind);
   836 }
   838 inline JSObject *
   839 NewObjectWithGivenProto(ExclusiveContext *cx, const js::Class *clasp, JSObject *proto, JSObject *parent,
   840                         NewObjectKind newKind = GenericObject)
   841 {
   842     return NewObjectWithGivenProto(cx, clasp, TaggedProto(proto), parent, newKind);
   843 }
   845 inline JSProtoKey
   846 GetClassProtoKey(const js::Class *clasp)
   847 {
   848     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
   849     if (key != JSProto_Null)
   850         return key;
   851     if (clasp->flags & JSCLASS_IS_ANONYMOUS)
   852         return JSProto_Object;
   853     return JSProto_Null;
   854 }
   856 inline bool
   857 FindProto(ExclusiveContext *cx, const js::Class *clasp, MutableHandleObject proto)
   858 {
   859     if (!FindClassPrototype(cx, proto, clasp))
   860         return false;
   862     if (!proto) {
   863         // We're looking for the prototype of a class that is currently being
   864         // resolved; the global object's resolve hook is on the
   865         // stack. js::FindClassPrototype detects this goofy case and returns
   866         // true with proto null. Fall back on Object.prototype.
   867         JS_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp) == JSProto_Null);
   868         return GetBuiltinPrototype(cx, JSProto_Object, proto);
   869     }
   870     return true;
   871 }
   873 /*
   874  * Make an object with the prototype set according to the specified prototype or class:
   875  *
   876  * if proto is non-null:
   877  *   use the specified proto
   878  * for a built-in class:
   879  *   use the memoized original value of the class constructor .prototype
   880  *   property object
   881  * else if available
   882  *   the current value of .prototype
   883  * else
   884  *   Object.prototype.
   885  *
   886  * The class prototype will be fetched from the parent's global. If global is
   887  * null, the context's active global will be used, and the resulting object's
   888  * parent will be that global.
   889  */
   890 JSObject *
   891 NewObjectWithClassProtoCommon(ExclusiveContext *cx, const js::Class *clasp, JSObject *proto, JSObject *parent,
   892                               gc::AllocKind allocKind, NewObjectKind newKind);
   894 inline JSObject *
   895 NewObjectWithClassProto(ExclusiveContext *cx, const js::Class *clasp, JSObject *proto, JSObject *parent,
   896                         gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
   897 {
   898     return NewObjectWithClassProtoCommon(cx, clasp, proto, parent, allocKind, newKind);
   899 }
   901 inline JSObject *
   902 NewObjectWithClassProto(ExclusiveContext *cx, const js::Class *clasp, JSObject *proto, JSObject *parent,
   903                         NewObjectKind newKind = GenericObject)
   904 {
   905     gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
   906     return NewObjectWithClassProto(cx, clasp, proto, parent, allocKind, newKind);
   907 }
   909 template<typename T>
   910 inline T *
   911 NewObjectWithProto(ExclusiveContext *cx, JSObject *proto, JSObject *parent,
   912                    NewObjectKind newKind = GenericObject)
   913 {
   914     JSObject *obj = NewObjectWithClassProto(cx, &T::class_, proto, parent, newKind);
   915     if (!obj)
   916         return nullptr;
   918     return &obj->as<T>();
   919 }
   921 /*
   922  * Create a native instance of the given class with parent and proto set
   923  * according to the context's active global.
   924  */
   925 inline JSObject *
   926 NewBuiltinClassInstance(ExclusiveContext *cx, const Class *clasp, gc::AllocKind allocKind,
   927                         NewObjectKind newKind = GenericObject)
   928 {
   929     return NewObjectWithClassProto(cx, clasp, nullptr, nullptr, allocKind, newKind);
   930 }
   932 inline JSObject *
   933 NewBuiltinClassInstance(ExclusiveContext *cx, const Class *clasp, NewObjectKind newKind = GenericObject)
   934 {
   935     gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
   936     return NewBuiltinClassInstance(cx, clasp, allocKind, newKind);
   937 }
   939 template<typename T>
   940 inline T *
   941 NewBuiltinClassInstance(ExclusiveContext *cx, NewObjectKind newKind = GenericObject)
   942 {
   943     JSObject *obj = NewBuiltinClassInstance(cx, &T::class_, newKind);
   944     if (!obj)
   945         return nullptr;
   947     return &obj->as<T>();
   948 }
   950 template<typename T>
   951 inline T *
   952 NewBuiltinClassInstance(ExclusiveContext *cx, gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
   953 {
   954     JSObject *obj = NewBuiltinClassInstance(cx, &T::class_, allocKind, newKind);
   955     if (!obj)
   956         return nullptr;
   958     return &obj->as<T>();
   959 }
   961 // Used to optimize calls to (new Object())
   962 bool
   963 NewObjectScriptedCall(JSContext *cx, MutableHandleObject obj);
   965 /* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
   966 static inline JSObject *
   967 CopyInitializerObject(JSContext *cx, HandleObject baseobj, NewObjectKind newKind = GenericObject)
   968 {
   969     JS_ASSERT(baseobj->getClass() == &JSObject::class_);
   970     JS_ASSERT(!baseobj->inDictionaryMode());
   972     gc::AllocKind allocKind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots());
   973     allocKind = gc::GetBackgroundAllocKind(allocKind);
   974     JS_ASSERT_IF(baseobj->isTenured(), allocKind == baseobj->tenuredGetAllocKind());
   975     RootedObject obj(cx);
   976     obj = NewBuiltinClassInstance(cx, &JSObject::class_, allocKind, newKind);
   977     if (!obj)
   978         return nullptr;
   980     RootedObject metadata(cx, obj->getMetadata());
   981     RootedShape lastProp(cx, baseobj->lastProperty());
   982     if (!JSObject::setLastProperty(cx, obj, lastProp))
   983         return nullptr;
   984     if (metadata && !JSObject::setMetadata(cx, obj, metadata))
   985         return nullptr;
   987     return obj;
   988 }
   990 JSObject *
   991 NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind allocKind,
   992                   NewObjectKind newKind = GenericObject);
   994 inline JSObject *
   995 NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
   996                   NewObjectKind newKind = GenericObject)
   997 {
   998     gc::AllocKind allocKind = gc::GetGCObjectKind(type->clasp());
   999     return NewObjectWithType(cx, type, parent, allocKind, newKind);
  1002 JSObject *
  1003 NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent,
  1004                   gc::AllocKind allocKind, HandleShape shape,
  1005                   NewObjectKind newKind = GenericObject);
  1007 /*
  1008  * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
  1009  * the object, zero if the final size is unknown. This should only be used for
  1010  * objects that do not require any fixed slots.
  1011  */
  1012 static inline gc::AllocKind
  1013 GuessObjectGCKind(size_t numSlots)
  1015     if (numSlots)
  1016         return gc::GetGCObjectKind(numSlots);
  1017     return gc::FINALIZE_OBJECT4;
  1020 static inline gc::AllocKind
  1021 GuessArrayGCKind(size_t numSlots)
  1023     if (numSlots)
  1024         return gc::GetGCArrayKind(numSlots);
  1025     return gc::FINALIZE_OBJECT8;
  1028 inline bool
  1029 ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
  1031     if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
  1032         return Proxy::objectClassIs(obj, classValue, cx);
  1034     switch (classValue) {
  1035       case ESClass_Array: return obj->is<ArrayObject>();
  1036       case ESClass_Number: return obj->is<NumberObject>();
  1037       case ESClass_String: return obj->is<StringObject>();
  1038       case ESClass_Boolean: return obj->is<BooleanObject>();
  1039       case ESClass_RegExp: return obj->is<RegExpObject>();
  1040       case ESClass_ArrayBuffer:
  1041         return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
  1042       case ESClass_Date: return obj->is<DateObject>();
  1044     MOZ_ASSUME_UNREACHABLE("bad classValue");
  1047 inline bool
  1048 IsObjectWithClass(const Value &v, ESClassValue classValue, JSContext *cx)
  1050     if (!v.isObject())
  1051         return false;
  1052     RootedObject obj(cx, &v.toObject());
  1053     return ObjectClassIs(obj, classValue, cx);
  1056 static MOZ_ALWAYS_INLINE bool
  1057 NewObjectMetadata(ExclusiveContext *cxArg, JSObject **pmetadata)
  1059     // The metadata callback is invoked before each created object, except when
  1060     // analysis/compilation is active, to avoid recursion.
  1061     JS_ASSERT(!*pmetadata);
  1062     if (JSContext *cx = cxArg->maybeJSContext()) {
  1063         if (MOZ_UNLIKELY((size_t)cx->compartment()->hasObjectMetadataCallback()) &&
  1064             !cx->compartment()->activeAnalysis)
  1066             // Use AutoEnterAnalysis to prohibit both any GC activity under the
  1067             // callback, and any reentering of JS via Invoke() etc.
  1068             types::AutoEnterAnalysis enter(cx);
  1070             if (!cx->compartment()->callObjectMetadataCallback(cx, pmetadata))
  1071                 return false;
  1074     return true;
  1077 inline bool
  1078 DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value,
  1079                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  1081     Rooted<jsid> id(cx, NameToId(name));
  1082     return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
  1085 namespace baseops {
  1087 inline bool
  1088 LookupProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name,
  1089                MutableHandleObject objp, MutableHandleShape propp)
  1091     Rooted<jsid> id(cx, NameToId(name));
  1092     return LookupProperty<CanGC>(cx, obj, id, objp, propp);
  1095 inline bool
  1096 DefineProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value,
  1097                JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
  1099     Rooted<jsid> id(cx, NameToId(name));
  1100     return DefineGeneric(cx, obj, id, value, getter, setter, attrs);
  1103 } /* namespace baseops */
  1105 } /* namespace js */
  1107 extern JSObject *
  1108 js_InitClass(JSContext *cx, js::HandleObject obj, JSObject *parent_proto,
  1109              const js::Class *clasp, JSNative constructor, unsigned nargs,
  1110              const JSPropertySpec *ps, const JSFunctionSpec *fs,
  1111              const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
  1112              JSObject **ctorp = nullptr,
  1113              js::gc::AllocKind ctorKind = JSFunction::FinalizeKind);
  1115 #endif /* jsobjinlines_h */

mercurial