2019-03-11 03:26:37 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#ifndef vm_NativeObject_inl_h
|
|
|
|
#define vm_NativeObject_inl_h
|
|
|
|
|
|
|
|
#include "vm/NativeObject.h"
|
|
|
|
|
|
|
|
#include "jscntxt.h"
|
|
|
|
|
|
|
|
#include "builtin/TypedObject.h"
|
|
|
|
#include "proxy/Proxy.h"
|
|
|
|
#include "vm/ProxyObject.h"
|
|
|
|
#include "vm/TypedArrayObject.h"
|
|
|
|
|
|
|
|
#include "jsobjinlines.h"
|
|
|
|
|
|
|
|
namespace js {
|
|
|
|
|
|
|
|
inline uint8_t*
|
|
|
|
NativeObject::fixedData(size_t nslots) const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(ClassCanHaveFixedData(getClass()));
|
|
|
|
MOZ_ASSERT(nslots == numFixedSlots() + (hasPrivate() ? 1 : 0));
|
|
|
|
return reinterpret_cast<uint8_t*>(&fixedSlots()[nslots]);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
NativeObject::removeLastProperty(ExclusiveContext* cx)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(canRemoveLastProperty());
|
|
|
|
JS_ALWAYS_TRUE(setLastProperty(cx, lastProperty()->previous()));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
NativeObject::canRemoveLastProperty()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Check that the information about the object stored in the last
|
|
|
|
* property's base shape is consistent with that stored in the previous
|
|
|
|
* shape. If not consistent, then the last property cannot be removed as it
|
|
|
|
* will induce a change in the object itself, and the object must be
|
|
|
|
* converted to dictionary mode instead. See BaseShape comment in jsscope.h
|
|
|
|
*/
|
|
|
|
MOZ_ASSERT(!inDictionaryMode());
|
|
|
|
Shape* previous = lastProperty()->previous().get();
|
|
|
|
return previous->getObjectFlags() == lastProperty()->getObjectFlags();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
NativeObject::setShouldConvertDoubleElements()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(is<ArrayObject>() && !hasEmptyElements());
|
|
|
|
getElementsHeader()->setShouldConvertDoubleElements();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
NativeObject::clearShouldConvertDoubleElements()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(is<ArrayObject>() && !hasEmptyElements());
|
|
|
|
getElementsHeader()->clearShouldConvertDoubleElements();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
NativeObject::setDenseElementWithType(ExclusiveContext* cx, uint32_t index, const Value& val)
|
|
|
|
{
|
|
|
|
// Avoid a slow AddTypePropertyId call if the type is the same as the type
|
|
|
|
// of the previous element.
|
|
|
|
TypeSet::Type thisType = TypeSet::GetValueType(val);
|
|
|
|
if (index == 0 || TypeSet::GetValueType(elements_[index - 1]) != thisType)
|
|
|
|
AddTypePropertyId(cx, this, JSID_VOID, thisType);
|
|
|
|
setDenseElementMaybeConvertDouble(index, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
NativeObject::initDenseElementWithType(ExclusiveContext* cx, uint32_t index, const Value& val)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!shouldConvertDoubleElements());
|
|
|
|
if (val.isMagic(JS_ELEMENTS_HOLE))
|
|
|
|
markDenseElementsNotPacked(cx);
|
|
|
|
else
|
|
|
|
AddTypePropertyId(cx, this, JSID_VOID, val);
|
|
|
|
initDenseElement(index, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
NativeObject::setDenseElementHole(ExclusiveContext* cx, uint32_t index)
|
|
|
|
{
|
|
|
|
MarkObjectGroupFlags(cx, this, OBJECT_FLAG_NON_PACKED);
|
|
|
|
setDenseElement(index, MagicValue(JS_ELEMENTS_HOLE));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ inline void
|
|
|
|
NativeObject::removeDenseElementForSparseIndex(ExclusiveContext* cx,
|
|
|
|
HandleNativeObject obj, uint32_t index)
|
|
|
|
{
|
|
|
|
MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_NON_PACKED | OBJECT_FLAG_SPARSE_INDEXES);
|
|
|
|
if (obj->containsDenseElement(index))
|
|
|
|
obj->setDenseElementUnchecked(index, MagicValue(JS_ELEMENTS_HOLE));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
NativeObject::writeToIndexWouldMarkNotPacked(uint32_t index)
|
|
|
|
{
|
|
|
|
return getElementsHeader()->initializedLength < index;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
NativeObject::markDenseElementsNotPacked(ExclusiveContext* cx)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(isNative());
|
|
|
|
MarkObjectGroupFlags(cx, this, OBJECT_FLAG_NON_PACKED);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
NativeObject::ensureDenseInitializedLengthNoPackedCheck(ExclusiveContext* cx, uint32_t index,
|
|
|
|
uint32_t extra)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
|
|
|
|
MOZ_ASSERT(!denseElementsAreFrozen());
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure that the array's contents have been initialized up to index, and
|
|
|
|
* mark the elements through 'index + extra' as initialized in preparation
|
|
|
|
* for a write.
|
|
|
|
*/
|
|
|
|
MOZ_ASSERT(index + extra <= getDenseCapacity());
|
|
|
|
uint32_t& initlen = getElementsHeader()->initializedLength;
|
|
|
|
|
|
|
|
if (initlen < index + extra) {
|
|
|
|
size_t offset = initlen;
|
|
|
|
for (HeapSlot* sp = elements_ + initlen;
|
|
|
|
sp != elements_ + (index + extra);
|
|
|
|
sp++, offset++)
|
|
|
|
{
|
|
|
|
sp->init(this, HeapSlot::Element, offset, MagicValue(JS_ELEMENTS_HOLE));
|
|
|
|
}
|
|
|
|
initlen = index + extra;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
NativeObject::ensureDenseInitializedLength(ExclusiveContext* cx, uint32_t index, uint32_t extra)
|
|
|
|
{
|
|
|
|
if (writeToIndexWouldMarkNotPacked(index))
|
|
|
|
markDenseElementsNotPacked(cx);
|
|
|
|
ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
|
|
|
|
}
|
|
|
|
|
|
|
|
DenseElementResult
|
|
|
|
NativeObject::extendDenseElements(ExclusiveContext* cx,
|
|
|
|
uint32_t requiredCapacity, uint32_t extra)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
|
|
|
|
MOZ_ASSERT(!denseElementsAreFrozen());
|
|
|
|
|
|
|
|
/*
|
2019-12-25 04:44:59 -08:00
|
|
|
* Don't grow elements for non-extensible objects. Dense elements can be
|
|
|
|
* added/written with no extensible checks as long as there is capacity
|
|
|
|
* for them.
|
2019-03-11 03:26:37 -07:00
|
|
|
*/
|
2019-12-25 04:44:59 -08:00
|
|
|
if (!nonProxyIsExtensible()) {
|
2019-03-11 03:26:37 -07:00
|
|
|
MOZ_ASSERT(getDenseCapacity() == 0);
|
|
|
|
return DenseElementResult::Incomplete;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't grow elements for objects which already have sparse indexes.
|
|
|
|
* This avoids needing to count non-hole elements in willBeSparseElements
|
|
|
|
* every time a new index is added.
|
|
|
|
*/
|
|
|
|
if (isIndexed())
|
|
|
|
return DenseElementResult::Incomplete;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We use the extra argument also as a hint about number of non-hole
|
|
|
|
* elements to be inserted.
|
|
|
|
*/
|
|
|
|
if (requiredCapacity > MIN_SPARSE_INDEX &&
|
|
|
|
willBeSparseElements(requiredCapacity, extra)) {
|
|
|
|
return DenseElementResult::Incomplete;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!growElements(cx, requiredCapacity))
|
|
|
|
return DenseElementResult::Failure;
|
|
|
|
|
|
|
|
return DenseElementResult::Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline DenseElementResult
|
|
|
|
NativeObject::ensureDenseElements(ExclusiveContext* cx, uint32_t index, uint32_t extra)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(isNative());
|
|
|
|
|
|
|
|
if (writeToIndexWouldMarkNotPacked(index))
|
|
|
|
markDenseElementsNotPacked(cx);
|
|
|
|
|
|
|
|
if (!maybeCopyElementsForWrite(cx))
|
|
|
|
return DenseElementResult::Failure;
|
|
|
|
|
|
|
|
uint32_t currentCapacity = getDenseCapacity();
|
|
|
|
|
|
|
|
uint32_t requiredCapacity;
|
|
|
|
if (extra == 1) {
|
|
|
|
/* Optimize for the common case. */
|
|
|
|
if (index < currentCapacity) {
|
|
|
|
ensureDenseInitializedLengthNoPackedCheck(cx, index, 1);
|
|
|
|
return DenseElementResult::Success;
|
|
|
|
}
|
|
|
|
requiredCapacity = index + 1;
|
|
|
|
if (requiredCapacity == 0) {
|
|
|
|
/* Overflow. */
|
|
|
|
return DenseElementResult::Incomplete;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
requiredCapacity = index + extra;
|
|
|
|
if (requiredCapacity < index) {
|
|
|
|
/* Overflow. */
|
|
|
|
return DenseElementResult::Incomplete;
|
|
|
|
}
|
|
|
|
if (requiredCapacity <= currentCapacity) {
|
|
|
|
ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
|
|
|
|
return DenseElementResult::Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DenseElementResult result = extendDenseElements(cx, requiredCapacity, extra);
|
|
|
|
if (result != DenseElementResult::Success)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
|
|
|
|
return DenseElementResult::Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Value
|
|
|
|
NativeObject::getDenseOrTypedArrayElement(uint32_t idx)
|
|
|
|
{
|
|
|
|
if (is<TypedArrayObject>())
|
|
|
|
return as<TypedArrayObject>().getElement(idx);
|
|
|
|
return getDenseElement(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ inline NativeObject*
|
|
|
|
NativeObject::copy(ExclusiveContext* cx, gc::AllocKind kind, gc::InitialHeap heap,
|
|
|
|
HandleNativeObject templateObject)
|
|
|
|
{
|
|
|
|
RootedShape shape(cx, templateObject->lastProperty());
|
|
|
|
RootedObjectGroup group(cx, templateObject->group());
|
|
|
|
MOZ_ASSERT(!templateObject->denseElementsAreCopyOnWrite());
|
|
|
|
|
|
|
|
JSObject* baseObj = create(cx, kind, heap, shape, group);
|
|
|
|
if (!baseObj)
|
|
|
|
return nullptr;
|
|
|
|
NativeObject* obj = &baseObj->as<NativeObject>();
|
|
|
|
|
|
|
|
size_t span = shape->slotSpan();
|
|
|
|
if (span) {
|
|
|
|
uint32_t numFixed = templateObject->numFixedSlots();
|
|
|
|
const Value* fixed = &templateObject->getSlot(0);
|
|
|
|
// Only copy elements which are registered in the shape, even if the
|
|
|
|
// number of fixed slots is larger.
|
|
|
|
if (span < numFixed)
|
|
|
|
numFixed = span;
|
|
|
|
obj->copySlotRange(0, fixed, numFixed);
|
|
|
|
|
|
|
|
if (numFixed < span) {
|
|
|
|
uint32_t numSlots = span - numFixed;
|
|
|
|
const Value* slots = &templateObject->getSlot(numFixed);
|
|
|
|
obj->copySlotRange(numFixed, slots, numSlots);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
NativeObject::setSlotWithType(ExclusiveContext* cx, Shape* shape,
|
|
|
|
const Value& value, bool overwriting)
|
|
|
|
{
|
|
|
|
setSlot(shape->slot(), value);
|
|
|
|
|
|
|
|
if (overwriting)
|
|
|
|
shape->setOverwritten();
|
|
|
|
|
|
|
|
AddTypePropertyId(cx, this, shape->propid(), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
NativeObject::updateShapeAfterMovingGC()
|
|
|
|
{
|
|
|
|
Shape* shape = shape_.unbarrieredGet();
|
|
|
|
if (IsForwarded(shape))
|
|
|
|
shape_.unsafeSet(Forwarded(shape));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
|
|
|
|
static inline PlainObject*
|
|
|
|
CopyInitializerObject(JSContext* cx, HandlePlainObject baseobj, NewObjectKind newKind = GenericObject)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!baseobj->inDictionaryMode());
|
|
|
|
|
|
|
|
gc::AllocKind allocKind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots());
|
|
|
|
allocKind = gc::GetBackgroundAllocKind(allocKind);
|
|
|
|
MOZ_ASSERT_IF(baseobj->isTenured(), allocKind == baseobj->asTenured().getAllocKind());
|
|
|
|
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
|
|
|
|
if (!obj)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if (!obj->setLastProperty(cx, baseobj->lastProperty()))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline NativeObject*
|
|
|
|
NewNativeObjectWithGivenTaggedProto(ExclusiveContext* cx, const Class* clasp,
|
|
|
|
Handle<TaggedProto> proto,
|
|
|
|
gc::AllocKind allocKind, NewObjectKind newKind)
|
|
|
|
{
|
|
|
|
return MaybeNativeObject(NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind,
|
|
|
|
newKind));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline NativeObject*
|
|
|
|
NewNativeObjectWithGivenTaggedProto(ExclusiveContext* cx, const Class* clasp,
|
|
|
|
Handle<TaggedProto> proto,
|
|
|
|
NewObjectKind newKind = GenericObject)
|
|
|
|
{
|
|
|
|
return MaybeNativeObject(NewObjectWithGivenTaggedProto(cx, clasp, proto, newKind));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline NativeObject*
|
|
|
|
NewNativeObjectWithGivenProto(ExclusiveContext* cx, const Class* clasp,
|
|
|
|
HandleObject proto,
|
|
|
|
gc::AllocKind allocKind, NewObjectKind newKind)
|
|
|
|
{
|
|
|
|
return MaybeNativeObject(NewObjectWithGivenProto(cx, clasp, proto, allocKind, newKind));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline NativeObject*
|
|
|
|
NewNativeObjectWithGivenProto(ExclusiveContext* cx, const Class* clasp,
|
|
|
|
HandleObject proto,
|
|
|
|
NewObjectKind newKind = GenericObject)
|
|
|
|
{
|
|
|
|
return MaybeNativeObject(NewObjectWithGivenProto(cx, clasp, proto, newKind));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline NativeObject*
|
|
|
|
NewNativeObjectWithClassProto(ExclusiveContext* cx, const Class* clasp, HandleObject proto,
|
|
|
|
gc::AllocKind allocKind,
|
|
|
|
NewObjectKind newKind = GenericObject)
|
|
|
|
{
|
|
|
|
return MaybeNativeObject(NewObjectWithClassProto(cx, clasp, proto, allocKind, newKind));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline NativeObject*
|
|
|
|
NewNativeObjectWithClassProto(ExclusiveContext* cx, const Class* clasp, HandleObject proto,
|
|
|
|
NewObjectKind newKind = GenericObject)
|
|
|
|
{
|
|
|
|
return MaybeNativeObject(NewObjectWithClassProto(cx, clasp, proto, newKind));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Call obj's resolve hook.
|
|
|
|
*
|
|
|
|
* cx and id are the parameters initially passed to the ongoing lookup;
|
|
|
|
* propp and recursedp are its out parameters.
|
|
|
|
*
|
|
|
|
* There are four possible outcomes:
|
|
|
|
*
|
|
|
|
* - On failure, report an error or exception and return false.
|
|
|
|
*
|
|
|
|
* - If we are already resolving a property of obj, set *recursedp = true,
|
|
|
|
* and return true.
|
|
|
|
*
|
|
|
|
* - If the resolve hook finds or defines the sought property, set propp
|
|
|
|
* appropriately, set *recursedp = false, and return true.
|
|
|
|
*
|
|
|
|
* - Otherwise no property was resolved. Set propp to nullptr and
|
|
|
|
* *recursedp = false and return true.
|
|
|
|
*/
|
|
|
|
static MOZ_ALWAYS_INLINE bool
|
|
|
|
CallResolveOp(JSContext* cx, HandleNativeObject obj, HandleId id, MutableHandleShape propp,
|
|
|
|
bool* recursedp)
|
|
|
|
{
|
|
|
|
// Avoid recursion on (obj, id) already being resolved on cx.
|
|
|
|
AutoResolving resolving(cx, obj, id);
|
|
|
|
if (resolving.alreadyStarted()) {
|
|
|
|
// Already resolving id in obj, suppress recursion.
|
|
|
|
*recursedp = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
*recursedp = false;
|
|
|
|
|
|
|
|
bool resolved = false;
|
|
|
|
if (!obj->getClass()->getResolve()(cx, obj, id, &resolved))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!resolved)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Assert the mayResolve hook, if there is one, returns true for this
|
|
|
|
// property.
|
|
|
|
MOZ_ASSERT_IF(obj->getClass()->getMayResolve(),
|
|
|
|
obj->getClass()->getMayResolve()(cx->names(), id, obj));
|
|
|
|
|
|
|
|
if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
|
|
|
|
MarkDenseOrTypedArrayElementFound<CanGC>(propp);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(!obj->is<TypedArrayObject>());
|
|
|
|
|
|
|
|
propp.set(obj->lookup(cx, id));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static MOZ_ALWAYS_INLINE bool
|
|
|
|
ClassMayResolveId(const JSAtomState& names, const Class* clasp, jsid id, JSObject* maybeObj)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp);
|
|
|
|
|
|
|
|
if (!clasp->getResolve()) {
|
|
|
|
// Sanity check: we should only have a mayResolve hook if we have a
|
|
|
|
// resolve hook.
|
|
|
|
MOZ_ASSERT(!clasp->getMayResolve(), "Class with mayResolve hook but no resolve hook");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (JSMayResolveOp mayResolve = clasp->getMayResolve()) {
|
|
|
|
// Tell the analysis our mayResolve hooks won't trigger GC.
|
|
|
|
JS::AutoSuppressGCAnalysis nogc;
|
|
|
|
if (!mayResolve(names, id, maybeObj))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <AllowGC allowGC>
|
|
|
|
static MOZ_ALWAYS_INLINE bool
|
|
|
|
LookupOwnPropertyInline(ExclusiveContext* cx,
|
|
|
|
typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
|
|
|
|
typename MaybeRooted<jsid, allowGC>::HandleType id,
|
|
|
|
typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp,
|
|
|
|
bool* donep)
|
|
|
|
{
|
|
|
|
// Check for a native dense element.
|
|
|
|
if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
|
|
|
|
MarkDenseOrTypedArrayElementFound<allowGC>(propp);
|
|
|
|
*donep = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for a typed array element. Integer lookups always finish here
|
|
|
|
// so that integer properties on the prototype are ignored even for out
|
|
|
|
// of bounds accesses.
|
|
|
|
if (obj->template is<TypedArrayObject>()) {
|
|
|
|
uint64_t index;
|
|
|
|
if (IsTypedArrayIndex(id, &index)) {
|
|
|
|
if (index < obj->template as<TypedArrayObject>().length()) {
|
|
|
|
MarkDenseOrTypedArrayElementFound<allowGC>(propp);
|
|
|
|
} else {
|
|
|
|
propp.set(nullptr);
|
|
|
|
}
|
|
|
|
*donep = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for a native property.
|
|
|
|
if (Shape* shape = obj->lookup(cx, id)) {
|
|
|
|
propp.set(shape);
|
|
|
|
*donep = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// id was not found in obj. Try obj's resolve hook, if any.
|
|
|
|
if (obj->getClass()->getResolve()) {
|
|
|
|
if (!cx->shouldBeJSContext() || !allowGC)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool recursed;
|
|
|
|
if (!CallResolveOp(cx->asJSContext(),
|
|
|
|
MaybeRooted<NativeObject*, allowGC>::toHandle(obj),
|
|
|
|
MaybeRooted<jsid, allowGC>::toHandle(id),
|
|
|
|
MaybeRooted<Shape*, allowGC>::toMutableHandle(propp),
|
|
|
|
&recursed))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (recursed) {
|
|
|
|
propp.set(nullptr);
|
|
|
|
*donep = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (propp) {
|
|
|
|
*donep = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
propp.set(nullptr);
|
|
|
|
*donep = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Simplified version of LookupOwnPropertyInline that doesn't call resolve
|
|
|
|
* hooks.
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
NativeLookupOwnPropertyNoResolve(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
|
|
|
|
MutableHandleShape result)
|
|
|
|
{
|
|
|
|
// Check for a native dense element.
|
|
|
|
if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
|
|
|
|
MarkDenseOrTypedArrayElementFound<CanGC>(result);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for a typed array element.
|
|
|
|
if (obj->is<TypedArrayObject>()) {
|
|
|
|
uint64_t index;
|
|
|
|
if (IsTypedArrayIndex(id, &index)) {
|
|
|
|
if (index < obj->as<TypedArrayObject>().length())
|
|
|
|
MarkDenseOrTypedArrayElementFound<CanGC>(result);
|
|
|
|
else
|
|
|
|
result.set(nullptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for a native property.
|
|
|
|
result.set(obj->lookup(cx, id));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <AllowGC allowGC>
|
|
|
|
static MOZ_ALWAYS_INLINE bool
|
|
|
|
LookupPropertyInline(ExclusiveContext* cx,
|
|
|
|
typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
|
|
|
|
typename MaybeRooted<jsid, allowGC>::HandleType id,
|
|
|
|
typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
|
|
|
|
typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
|
|
|
|
{
|
|
|
|
/* NB: The logic of this procedure is implicitly reflected in
|
|
|
|
* BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
|
|
|
|
* If this changes, please remember to update the logic there as well.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Search scopes starting with obj and following the prototype link. */
|
|
|
|
typename MaybeRooted<NativeObject*, allowGC>::RootType current(cx, obj);
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
bool done;
|
|
|
|
if (!LookupOwnPropertyInline<allowGC>(cx, current, id, propp, &done))
|
|
|
|
return false;
|
|
|
|
if (done) {
|
|
|
|
if (propp)
|
|
|
|
objp.set(current);
|
|
|
|
else
|
|
|
|
objp.set(nullptr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->staticPrototype());
|
|
|
|
|
|
|
|
if (!proto)
|
|
|
|
break;
|
|
|
|
if (!proto->isNative()) {
|
|
|
|
if (!cx->shouldBeJSContext() || !allowGC)
|
|
|
|
return false;
|
|
|
|
return LookupProperty(cx->asJSContext(),
|
|
|
|
MaybeRooted<JSObject*, allowGC>::toHandle(proto),
|
|
|
|
MaybeRooted<jsid, allowGC>::toHandle(id),
|
|
|
|
MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
|
|
|
|
MaybeRooted<Shape*, allowGC>::toMutableHandle(propp));
|
|
|
|
}
|
|
|
|
|
|
|
|
current = &proto->template as<NativeObject>();
|
|
|
|
}
|
|
|
|
|
|
|
|
objp.set(nullptr);
|
|
|
|
propp.set(nullptr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
ThrowIfNotConstructing(JSContext *cx, const CallArgs &args, const char *builtinName)
|
|
|
|
{
|
|
|
|
if (args.isConstructing())
|
|
|
|
return true;
|
|
|
|
return JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_ERROR, GetErrorMessage, nullptr,
|
|
|
|
JSMSG_BUILTIN_CTOR_NO_NEW, builtinName);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
IsPackedArray(JSObject* obj)
|
|
|
|
{
|
|
|
|
return obj->is<ArrayObject>() && !obj->hasLazyGroup() &&
|
|
|
|
!obj->group()->hasAllFlags(OBJECT_FLAG_NON_PACKED) &&
|
|
|
|
obj->as<ArrayObject>().getDenseInitializedLength() == obj->as<ArrayObject>().length();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace js
|
|
|
|
|
|
|
|
#endif /* vm_NativeObject_inl_h */
|