author | Terrence Cole <terrence@mozilla.com> |
Thu, 29 Nov 2012 10:22:10 -0800 | |
changeset 115971 | a41d57f0102000c6007738736ffbe3fb13ce86e7 |
parent 115970 | 4cc6db74ba94ad273bb44ad4488b585a0c69e1d9 |
child 115972 | 2fc7800b9847e6ace0f0b6f10a99aa99cda59d0a |
push id | 24034 |
push user | emorley@mozilla.com |
push date | Fri, 14 Dec 2012 15:28:57 +0000 |
treeherder | autoland@50d8f411d305 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | sfink |
bugs | 816776 |
milestone | 20.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2070,17 +2070,17 @@ BindLet(JSContext *cx, BindData *data, H } /* * Define the let binding's property before storing pn in the the binding's * slot indexed by blockCount off the class-reserved slot base. */ bool redeclared; RootedId id(cx, NameToId(name)); - Shape *shape = StaticBlockObject::addVar(cx, blockObj, id, blockCount, &redeclared); + RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id, blockCount, &redeclared)); if (!shape) { if (redeclared) ReportRedeclaration(cx, parser, pn, false, name); return false; } /* Store pn in the static block object. */ blockObj->setDefinitionParseNode(blockCount, reinterpret_cast<Definition *>(pn));
--- a/js/src/frontend/SharedContext-inl.h +++ b/js/src/frontend/SharedContext-inl.h @@ -118,17 +118,17 @@ frontend::LexicalLookup(ContextT *ct, Ha if (stmt->type == STMT_WITH) break; // Skip statements that do not introduce a new scope if (!stmt->isBlockScope) continue; StaticBlockObject &blockObj = *stmt->blockObj; - Shape *shape = blockObj.nativeLookup(ct->sc->context, AtomToId(atom)); + UnrootedShape shape = blockObj.nativeLookup(ct->sc->context, AtomToId(atom)); if (shape) { JS_ASSERT(shape->hasShortID()); if (slotp) *slotp = blockObj.stackDepth() + shape->shortid(); return stmt; } }
--- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -66,30 +66,30 @@ PushMarkStack(GCMarker *gcmarker, JSObje static inline void PushMarkStack(GCMarker *gcmarker, JSFunction *thing); static inline void PushMarkStack(GCMarker *gcmarker, JSScript *thing); static inline void -PushMarkStack(GCMarker *gcmarker, Shape *thing); +PushMarkStack(GCMarker *gcmarker, UnrootedShape thing); static inline void PushMarkStack(GCMarker *gcmarker, JSString *thing); static inline void PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing); namespace js { namespace gc { static void MarkChildren(JSTracer *trc, JSString *str); static void MarkChildren(JSTracer *trc, JSScript *script); -static void MarkChildren(JSTracer *trc, Shape *shape); +static void MarkChildren(JSTracer *trc, UnrootedShape shape); static void MarkChildren(JSTracer *trc, UnrootedBaseShape base); static void MarkChildren(JSTracer *trc, types::TypeObject *type); static void MarkChildren(JSTracer *trc, ion::IonCode *code); #if JS_HAS_XML_SUPPORT static void MarkChildren(JSTracer *trc, JSXML *xml); #endif } /* namespace gc */ @@ -287,22 +287,24 @@ Is##base##Marked(type **thingp) } \ \ bool \ Is##base##Marked(EncapsulatedPtr<type> *thingp) \ { \ return IsMarked<type>(thingp->unsafeGet()); \ } \ \ -bool Is##base##AboutToBeFinalized(type **thingp) \ +bool \ +Is##base##AboutToBeFinalized(type **thingp) \ { \ return IsAboutToBeFinalized<type>(thingp); \ } \ \ -bool Is##base##AboutToBeFinalized(EncapsulatedPtr<type> *thingp) \ +bool \ +Is##base##AboutToBeFinalized(EncapsulatedPtr<type> *thingp) \ { \ return IsAboutToBeFinalized<type>(thingp->unsafeGet()); \ } DeclMarkerImpl(BaseShape, BaseShape) DeclMarkerImpl(BaseShape, UnownedBaseShape) DeclMarkerImpl(IonCode, ion::IonCode) DeclMarkerImpl(Object, ArgumentsObject) @@ -726,20 +728,20 @@ PushMarkStack(GCMarker *gcmarker, JSScri * refer to other scripts only indirectly (like via nested functions) and * we cannot get to deep recursion. */ if (thing->markIfUnmarked(gcmarker->getMarkColor())) MarkChildren(gcmarker, thing); } static void -ScanShape(GCMarker *gcmarker, Shape *shape); +ScanShape(GCMarker *gcmarker, UnrootedShape shape); static void -PushMarkStack(GCMarker *gcmarker, Shape *thing) +PushMarkStack(GCMarker *gcmarker, UnrootedShape thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); /* We mark shapes directly rather than pushing on the stack. */ if (thing->markIfUnmarked(gcmarker->getMarkColor())) ScanShape(gcmarker, thing); } @@ -761,17 +763,17 @@ PushMarkStack(GCMarker *gcmarker, Unroot JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); /* We mark base shapes directly rather than pushing on the stack. */ if (thing->markIfUnmarked(gcmarker->getMarkColor())) ScanBaseShape(gcmarker, thing); } static void -ScanShape(GCMarker *gcmarker, Shape *shape) +ScanShape(GCMarker *gcmarker, UnrootedShape shape) { restart: PushMarkStack(gcmarker, shape->base()); const EncapsulatedId &id = shape->propidRef(); if (JSID_IS_STRING(id)) PushMarkStack(gcmarker, JSID_TO_STRING(id)); else if (JS_UNLIKELY(JSID_IS_OBJECT(id))) @@ -925,17 +927,17 @@ gc::MarkChildren(JSTracer *trc, JSString static void gc::MarkChildren(JSTracer *trc, JSScript *script) { script->markChildren(trc); } static void -gc::MarkChildren(JSTracer *trc, Shape *shape) +gc::MarkChildren(JSTracer *trc, UnrootedShape shape) { shape->markChildren(trc); } static void gc::MarkChildren(JSTracer *trc, UnrootedBaseShape base) { base->markChildren(trc); @@ -985,17 +987,17 @@ MarkCycleCollectorChildren(JSTracer *trc * This function is used by the cycle collector to trace through a * shape. The cycle collector does not care about shapes or base * shapes, so those are not marked. Instead, any shapes or base shapes * that are encountered have their children marked. Stack space is * bounded. If two shapes in a row have the same parent pointer, the * parent pointer will only be marked once. */ void -gc::MarkCycleCollectorChildren(JSTracer *trc, Shape *shape) +gc::MarkCycleCollectorChildren(JSTracer *trc, UnrootedShape shape) { JSObject *prevParent = NULL; do { MarkCycleCollectorChildren(trc, shape->base(), &prevParent); MarkId(trc, &shape->propidRef(), "propid"); shape = shape->previous(); } while (shape); } @@ -1016,17 +1018,17 @@ ScanTypeObject(GCMarker *gcmarker, types if (TaggedProto(type->proto).isObject()) PushMarkStack(gcmarker, type->proto); if (type->singleton && !type->lazy()) PushMarkStack(gcmarker, type->singleton); if (type->newScript) { PushMarkStack(gcmarker, type->newScript->fun); - PushMarkStack(gcmarker, type->newScript->shape); + PushMarkStack(gcmarker, type->newScript->shape.get()); } if (type->interpretedFunction) PushMarkStack(gcmarker, type->interpretedFunction); } static void gc::MarkChildren(JSTracer *trc, types::TypeObject *type) @@ -1351,17 +1353,17 @@ GCMarker::processMarkStackTop(SliceBudge if (budget.isOverBudget()) { pushObject(obj); return; } types::TypeObject *type = obj->typeFromGC(); PushMarkStack(this, type); - Shape *shape = obj->lastProperty(); + UnrootedShape shape = obj->lastProperty(); PushMarkStack(this, shape); /* Call the trace hook if necessary. */ Class *clasp = shape->getObjectClass(); if (clasp->trace) { if (clasp == &ArrayClass) { JS_ASSERT(!shape->isNative()); vp = obj->getDenseArrayElements();
--- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -231,17 +231,17 @@ void MarkChildren(JSTracer *trc, JSObject *obj); /* * Trace through the shape and any shapes it contains to mark * non-shape children. This is exposed to the JS API as * JS_TraceShapeCycleCollectorChildren. */ void -MarkCycleCollectorChildren(JSTracer *trc, Shape *shape); +MarkCycleCollectorChildren(JSTracer *trc, UnrootedShape shape); void PushArena(GCMarker *gcmarker, ArenaHeader *aheader); /*** Generic ***/ /* * The Mark() functions interface should only be used by code that must be
--- a/js/src/gc/Root.h +++ b/js/src/gc/Root.h @@ -274,25 +274,27 @@ class MutableHandle : public js::Mutable template <typename S> MutableHandle(MutableHandle<S> handle, typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0) { this->ptr = reinterpret_cast<const T *>(handle.address()); } template <typename S> - inline - MutableHandle(js::Rooted<S> *root, - typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0); + inline MutableHandle(js::Rooted<S> *root, + typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0); void set(T v) { JS_ASSERT(!js::RootMethods<T>::poisoned(v)); *ptr = v; } + template <typename S> + inline void set(const js::Unrooted<S> &v); + /* * This may be called only if the location of the T is guaranteed * to be marked (for some reason other than being a Rooted), * e.g., if it is guaranteed to be reachable from an implicit root. * * Create a MutableHandle from a raw location of a T. */ static MutableHandle fromMarkedLocation(T *p) { @@ -473,16 +475,23 @@ class Unrooted /* See notes for Unrooted::Unrooted(const T &) */ Unrooted &operator=(T other) { JS_ASSERT(other != UninitializedTag()); if (ptr_ == UninitializedTag()) EnterAssertNoGCScope(); ptr_ = other; return *this; } + Unrooted &operator=(Unrooted other) { + JS_ASSERT(other.ptr_ != UninitializedTag()); + if (ptr_ == UninitializedTag()) + EnterAssertNoGCScope(); + ptr_ = other.ptr_; + return *this; + } operator T() const { return (ptr_ == UninitializedTag()) ? NULL : ptr_; } T *operator&() { return &ptr_; } const T operator->() const { JS_ASSERT(ptr_ != UninitializedTag()); return ptr_; } bool operator==(const T &other) { return ptr_ == other; } bool operator!=(const T &other) { return ptr_ != other; } private: @@ -854,16 +863,23 @@ Handle<T>::Handle(MutableHandle<S> &root template <typename T> template <typename S> inline MutableHandle<T>::MutableHandle(js::Rooted<S> *root, typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy) { ptr = root->address(); } +template <typename T> template <typename S> +inline void MutableHandle<T>::set(const js::Unrooted<S> &v) +{ + JS_ASSERT(!js::RootMethods<T>::poisoned(v)); + *ptr = static_cast<S>(v); +} + /* * The scoped guard object AutoAssertNoGC forces the GC to assert if a GC is * attempted while the guard object is live. If you have a GC-unsafe operation * to perform, use this guard object to protect your operation. */ class AutoAssertNoGC { MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
--- a/js/src/ion/CompilerRoot.h +++ b/js/src/ion/CompilerRoot.h @@ -36,26 +36,29 @@ class CompilerRoot : public CompilerRoot JS_ASSERT(!ptr); ptr = root; next = rootList; rootList = this; } public: operator T () const { return static_cast<T>(ptr); } + operator Unrooted<T> () const { return static_cast<T>(ptr); } T operator ->() const { return static_cast<T>(ptr); } private: CompilerRoot() MOZ_DELETE; CompilerRoot(const CompilerRoot<T> &) MOZ_DELETE; CompilerRoot<T> &operator =(const CompilerRoot<T> &) MOZ_DELETE; }; -typedef CompilerRoot<JSObject*> CompilerRootObject; +typedef CompilerRoot<JSObject*> CompilerRootObject; typedef CompilerRoot<JSFunction*> CompilerRootFunction; +typedef CompilerRoot<JSScript*> CompilerRootScript; typedef CompilerRoot<PropertyName*> CompilerRootPropertyName; +typedef CompilerRoot<Shape*> CompilerRootShape; typedef CompilerRoot<Value> CompilerRootValue; } // namespace ion } // namespace js #endif // jsion_ion_gc_h__
--- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -4849,16 +4849,18 @@ TestSingletonPropertyTypes(JSContext *cx // value at the top of the stack. // (4) If a type barrier is in place, and has a single type, an unbox // instruction replaces the top of the stack. // (5) Lastly, a type barrier instruction replaces the top of the stack. bool IonBuilder::pushTypeBarrier(MInstruction *ins, types::StackTypeSet *actual, types::StackTypeSet *observed) { + AutoAssertNoGC nogc; + // If the instruction has no side effects, we'll resume the entire operation. // The actual type barrier will occur in the interpreter. If the // instruction is effectful, even if it has a singleton type, there // must be a resume point capturing the original def, and resuming // to that point will explicitly monitor the new type. if (!actual) { JS_ASSERT(!observed); @@ -4966,17 +4968,17 @@ IonBuilder::jsop_getgname(HandleProperty RootedObject globalObj(cx, &script()->global()); JS_ASSERT(globalObj->isNative()); RootedId id(cx, NameToId(name)); // For the fastest path, the property must be found, and it must be found // as a normal data property on exactly the global object. - const js::Shape *shape = globalObj->nativeLookup(cx, id); + RootedShape shape(cx, globalObj->nativeLookup(cx, id)); if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) return jsop_getname(name); types::HeapTypeSet *propertyTypes = oracle->globalPropertyTypeSet(script(), pc, id); if (propertyTypes && propertyTypes->isOwnProperty(cx, globalObj->getType(cx), true)) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. return jsop_getname(name); @@ -5043,17 +5045,17 @@ IonBuilder::jsop_setgname(HandleProperty types::HeapTypeSet *propertyTypes = oracle->globalPropertyWrite(script(), pc, id, &canSpecialize); // This should only happen for a few names like __proto__. if (!canSpecialize || globalObj->watched()) return jsop_setprop(name); // For the fastest path, the property must be found, and it must be found // as a normal data property on exactly the global object. - const js::Shape *shape = globalObj->nativeLookup(cx, id); + RootedShape shape(cx, globalObj->nativeLookup(cx, id)); if (!shape || !shape->hasDefaultSetter() || !shape->writable() || !shape->hasSlot()) return jsop_setprop(name); if (propertyTypes && propertyTypes->isOwnProperty(cx, globalObj->getType(cx), true)) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. return jsop_setprop(name); } @@ -6140,17 +6142,17 @@ IonBuilder::invalidatedIdempotentCache() return true; builder = builder->callerBuilder_; } while (builder); return false; } bool -IonBuilder::loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType) +IonBuilder::loadSlot(MDefinition *obj, HandleShape shape, MIRType rvalType) { JS_ASSERT(shape->hasDefaultGetter()); JS_ASSERT(shape->hasSlot()); RootedScript scriptRoot(cx, script()); types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc); types::StackTypeSet *types = oracle->propertyRead(script(), pc); @@ -6170,17 +6172,17 @@ IonBuilder::loadSlot(MDefinition *obj, S current->add(load); current->push(load); load->setResultType(rvalType); return pushTypeBarrier(load, types, barrier); } bool -IonBuilder::storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier) +IonBuilder::storeSlot(MDefinition *obj, UnrootedShape shape, MDefinition *value, bool needsBarrier) { JS_ASSERT(shape->hasDefaultSetter()); JS_ASSERT(shape->writable()); JS_ASSERT(shape->hasSlot()); if (shape->slot() < shape->numFixedSlots()) { MStoreFixedSlot *store = MStoreFixedSlot::New(obj, shape->slot(), value); current->add(store); @@ -6395,38 +6397,39 @@ IonBuilder::getPropTryCommonGetter(bool *emitted = true; return true; } bool IonBuilder::getPropTryMonomorphic(bool *emitted, HandleId id, types::StackTypeSet *barrier, TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes) { + AssertCanGC(); JS_ASSERT(*emitted == false); bool accessGetter = oracle->propertyReadAccessGetter(script(), pc); if (unary.ival != MIRType_Object) return true; - Shape *objShape = mjit::GetPICSingleShape(cx, script(), pc, info().constructing()); + RootedShape objShape(cx, mjit::GetPICSingleShape(cx, script(), pc, info().constructing())); if (!objShape || objShape->inDictionary()) { spew("GETPROP not monomorphic"); return true; } MDefinition *obj = current->pop(); // The JM IC was monomorphic, so we inline the property access as long as // the shape is not in dictionary made. We cannot be sure that the shape is // still a lastProperty, and calling Shape::search() on dictionary mode // shapes that aren't lastProperty is invalid. obj = addShapeGuard(obj, objShape, Bailout_CachedShapeGuard); spew("Inlining monomorphic GETPROP"); - Shape *shape = objShape->search(cx, id); + RootedShape shape(cx, objShape->search(cx, id)); JS_ASSERT(shape); MIRType rvalType = unary.rval; if (barrier || IsNullOrUndefined(unary.rval) || accessGetter) rvalType = MIRType_Value; if (!loadSlot(obj, shape, rvalType)) return false; @@ -6556,35 +6559,35 @@ IonBuilder::jsop_setprop(HandlePropertyN } oracle->binaryOp(script(), pc); MSetPropertyInstruction *ins; if (monitored) { ins = MCallSetProperty::New(obj, value, name, script()->strict); } else { - Shape *objShape; + UnrootedShape objShape; if ((objShape = mjit::GetPICSingleShape(cx, script(), pc, info().constructing())) && !objShape->inDictionary()) { // The JM IC was monomorphic, so we inline the property access as // long as the shape is not in dictionary mode. We cannot be sure // that the shape is still a lastProperty, and calling Shape::search // on dictionary mode shapes that aren't lastProperty is invalid. obj = addShapeGuard(obj, objShape, Bailout_CachedShapeGuard); - Shape *shape = objShape->search(cx, NameToId(name)); + UnrootedShape shape = DropUnrooted(objShape)->search(cx, NameToId(name)); JS_ASSERT(shape); spew("Inlining monomorphic SETPROP"); jsid typeId = types::MakeTypeId(cx, id); bool needsBarrier = oracle->propertyWriteNeedsBarrier(script(), pc, typeId); - return storeSlot(obj, shape, value, needsBarrier); + return storeSlot(obj, DropUnrooted(shape), value, needsBarrier); } spew("SETPROP not monomorphic"); ins = MSetPropertyCache::New(obj, value, name, script()->strict); if (!binaryTypes.lhsTypes || binaryTypes.lhsTypes->propertyNeedsBarrier(cx, id)) ins->setNeedsBarrier(); @@ -6957,17 +6960,17 @@ IonBuilder::addBoundsCheck(MDefinition * // If a bounds check failed in the past, don't optimize bounds checks. if (failedBoundsCheck_) check->setNotMovable(); return check; } MInstruction * -IonBuilder::addShapeGuard(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind) +IonBuilder::addShapeGuard(MDefinition *obj, const UnrootedShape shape, BailoutKind bailoutKind) { MGuardShape *guard = MGuardShape::New(obj, shape, bailoutKind); current->add(guard); // If a shape guard failed in the past, don't optimize shape guard. if (failedShapeGuard_) guard->setNotMovable();
--- a/js/src/ion/IonBuilder.h +++ b/js/src/ion/IonBuilder.h @@ -299,24 +299,24 @@ class IonBuilder : public MIRGenerator MInstruction *createDeclEnvObject(MDefinition *callee, MDefinition *scopeObj); MInstruction *createCallObject(MDefinition *callee, MDefinition *scopeObj); bool makeCall(HandleFunction target, uint32_t argc, bool constructing); MDefinition *walkScopeChain(unsigned hops); MInstruction *addBoundsCheck(MDefinition *index, MDefinition *length); - MInstruction *addShapeGuard(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind); + MInstruction *addShapeGuard(MDefinition *obj, const UnrootedShape shape, BailoutKind bailoutKind); JSObject *getNewArrayTemplateObject(uint32_t count); bool invalidatedIdempotentCache(); - bool loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType); - bool storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier); + bool loadSlot(MDefinition *obj, HandleShape shape, MIRType rvalType); + bool storeSlot(MDefinition *obj, UnrootedShape shape, MDefinition *value, bool needsBarrier); // jsop_getprop() helpers. bool getPropTryArgumentsLength(bool *emitted); bool getPropTryConstant(bool *emitted, HandleId id, types::StackTypeSet *barrier, types::StackTypeSet *types, TypeOracle::UnaryTypes unaryTypes); bool getPropTryDefiniteSlot(bool *emitted, HandlePropertyName name, types::StackTypeSet *barrier, types::StackTypeSet *types, TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes);
--- a/js/src/ion/IonCaches.cpp +++ b/js/src/ion/IonCaches.cpp @@ -144,29 +144,29 @@ IsCacheableProtoChain(JSObject *obj, JSO if (!proto || !proto->isNative()) return false; obj = proto; } return true; } static bool -IsCacheableGetPropReadSlot(JSObject *obj, JSObject *holder, const Shape *shape) +IsCacheableGetPropReadSlot(JSObject *obj, JSObject *holder, UnrootedShape shape) { if (!shape || !IsCacheableProtoChain(obj, holder)) return false; if (!shape->hasSlot() || !shape->hasDefaultGetter()) return false; return true; } static bool -IsCacheableNoProperty(JSObject *obj, JSObject *holder, const Shape *shape, jsbytecode *pc, +IsCacheableNoProperty(JSObject *obj, JSObject *holder, UnrootedShape shape, jsbytecode *pc, const TypedOrValueRegister &output) { if (shape) return false; JS_ASSERT(!holder); // Just because we didn't find the property on the object doesn't mean it @@ -207,30 +207,30 @@ IsCacheableNoProperty(JSObject *obj, JSO // monitor and invalidate the script. if (!output.hasValue()) return false; return true; } static bool -IsCacheableGetPropCallNative(JSObject *obj, JSObject *holder, const Shape *shape) +IsCacheableGetPropCallNative(JSObject *obj, JSObject *holder, UnrootedShape shape) { if (!shape || !IsCacheableProtoChain(obj, holder)) return false; if (!shape->hasGetterValue() || !shape->getterValue().isObject()) return false; return shape->getterValue().toObject().isFunction() && shape->getterValue().toObject().toFunction()->isNative(); } static bool -IsCacheableGetPropCallPropertyOp(JSObject *obj, JSObject *holder, const Shape *shape) +IsCacheableGetPropCallPropertyOp(JSObject *obj, JSObject *holder, UnrootedShape shape) { if (!shape || !IsCacheableProtoChain(obj, holder)) return false; if (shape->hasSlot() || shape->hasGetterValue() || shape->hasDefaultGetter()) return false; return true; @@ -238,17 +238,17 @@ IsCacheableGetPropCallPropertyOp(JSObjec struct GetNativePropertyStub { CodeOffsetJump exitOffset; CodeOffsetJump rejoinOffset; CodeOffsetLabel stubCodePatchOffset; void generateReadSlot(JSContext *cx, MacroAssembler &masm, JSObject *obj, PropertyName *propName, - JSObject *holder, const Shape *shape, Register object, TypedOrValueRegister output, + JSObject *holder, HandleShape shape, Register object, TypedOrValueRegister output, RepatchLabel *failures, Label *nonRepatchFailures = NULL) { // If there's a single jump to |failures|, we can patch the shape guard // jump directly. Otherwise, jump to the end of the stub, so there's a // common point to patch. bool multipleFailureJumps = (nonRepatchFailures != NULL) && nonRepatchFailures->used(); exitOffset = masm.branchPtrWithPatch(Assembler::NotEqual, Address(object, JSObject::offsetOfShape()), @@ -349,17 +349,17 @@ struct GetNativePropertyStub exitOffset = masm.jumpWithPatch(&exit_); masm.bind(&exit_); } else { masm.bind(failures); } } bool generateCallGetter(JSContext *cx, MacroAssembler &masm, JSObject *obj, - PropertyName *propName, JSObject *holder, const Shape *shape, + PropertyName *propName, JSObject *holder, HandleShape shape, RegisterSet &liveRegs, Register object, TypedOrValueRegister output, void *returnAddr, jsbytecode *pc, RepatchLabel *failures, Label *nonRepatchFailures = NULL) { // Initial shape check. Label stubFailure; masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfShape()), ImmGCPtr(obj->lastProperty()), &stubFailure); @@ -614,17 +614,17 @@ struct GetNativePropertyStub masm.bind(&exit_); return true; } }; bool IonCacheGetProperty::attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder, - const Shape *shape) + HandleShape shape) { MacroAssembler masm; RepatchLabel failures; GetNativePropertyStub getprop; getprop.generateReadSlot(cx, masm, obj, name(), holder, shape, object(), output(), &failures); Linker linker(masm); @@ -649,19 +649,20 @@ IonCacheGetProperty::attachReadSlot(JSCo IonSpew(IonSpew_InlineCaches, "Generated native GETPROP stub at %p %s", code->raw(), idempotent() ? "(idempotent)" : "(not idempotent)"); return true; } bool IonCacheGetProperty::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, - JSObject *holder, const Shape *shape, + JSObject *holder, HandleShape shape, const SafepointIndex *safepointIndex, void *returnAddr) { + AssertCanGC(); MacroAssembler masm; RepatchLabel failures; JS_ASSERT(!idempotent()); JS_ASSERT(allowGetters()); // Need to set correct framePushed on the masm so that exit frame descriptors are // properly constructed. @@ -1131,18 +1132,18 @@ IonCacheSetProperty::attachSetterCall(JS IonSpew(IonSpew_InlineCaches, "Generated SETPROP calling case stub at %p", code->raw()); return true; } bool IonCacheSetProperty::attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, - const Shape *oldShape, const Shape *newShape, - const Shape *propShape) + HandleShape oldShape, HandleShape newShape, + HandleShape propShape) { MacroAssembler masm; Label failures; /* Guard the type of the object */ masm.branchPtr(Assembler::NotEqual, Address(object(), JSObject::offsetOfType()), ImmGCPtr(obj->type()), &failures); @@ -1151,17 +1152,17 @@ IonCacheSetProperty::attachNativeAdding( masm.branchTestObjShape(Assembler::NotEqual, object(), oldShape, &failures); Label protoFailures; masm.push(object()); // save object reg because we clobber it JSObject *proto = obj->getProto(); Register protoReg = object(); while (proto) { - Shape *protoShape = proto->lastProperty(); + UnrootedShape protoShape = proto->lastProperty(); // load next prototype masm.loadPtr(Address(protoReg, JSObject::offsetOfType()), protoReg); masm.loadPtr(Address(protoReg, offsetof(types::TypeObject, proto)), protoReg); // ensure that the prototype is not NULL and that its shape matches masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &protoFailures); masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, &protoFailures); @@ -1243,17 +1244,17 @@ IsPropertyInlineable(JSObject *obj, IonC return false; return true; } static bool IsPropertySetInlineable(JSContext *cx, HandleObject obj, HandleId id, MutableHandleShape pshape) { - Shape *shape = obj->nativeLookup(cx, id); + UnrootedShape shape = obj->nativeLookup(cx, id); if (!shape) return false; if (!shape->hasSlot()) return false; if (!shape->hasDefaultSetter()) @@ -1294,17 +1295,17 @@ IsPropertySetterCallInlineable(JSContext static bool IsPropertyAddInlineable(JSContext *cx, HandleObject obj, jsid id, uint32_t oldSlots, MutableHandleShape pShape) { // This is not a Add, the property exists. if (pShape.get()) return false; - Shape *shape = obj->nativeLookup(cx, id); + RootedShape shape(cx, obj->nativeLookup(cx, id)); if (!shape || shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter()) return false; // If object has a non-default resolve hook, don't inline if (obj->getClass()->resolve != JS_ResolveStub) return false; if (!obj->isExtensible() || !shape->writable()) @@ -1314,17 +1315,17 @@ IsPropertyAddInlineable(JSContext *cx, H // are native, and that all prototypes have no getter or setter // defined on the property for (JSObject *proto = obj->getProto(); proto; proto = proto->getProto()) { // if prototype is non-native, don't optimize if (!proto->isNative()) return false; // if prototype defines this property in a non-plain way, don't optimize - const Shape *protoShape = proto->nativeLookup(cx, id); + UnrootedShape protoShape = proto->nativeLookup(cx, id); if (protoShape && !protoShape->hasDefaultSetter()) return false; // Otherise, if there's no such property, watch out for a resolve hook that would need // to be invoked and thus prevent inlining of property addition. if (proto->getClass()->resolve != JS_ResolveStub) return false; } @@ -1371,26 +1372,26 @@ js::ion::SetPropertyCache(JSContext *cx, cache.incrementStubCount(); if (!cache.attachSetterCall(cx, ion, obj, holder, shape, returnAddr)) return false; } } } uint32_t oldSlots = obj->numDynamicSlots(); - const Shape *oldShape = obj->lastProperty(); + RootedShape oldShape(cx, obj->lastProperty()); // Set/Add the property on the object, the inlined cache are setup for the next execution. if (!SetProperty(cx, obj, name, value, cache.strict(), isSetName)) return false; // The property did not exists before, now we can try again to inline the // procedure which is adding the property. if (inlinable && IsPropertyAddInlineable(cx, obj, id, oldSlots, &shape)) { - const Shape *newShape = obj->lastProperty(); + RootedShape newShape(cx, obj->lastProperty()); cache.incrementStubCount(); if (!cache.attachNativeAdding(cx, ion, obj, oldShape, newShape, shape)) return false; } return true; } @@ -1614,17 +1615,17 @@ IonCacheBindName::attachGlobal(JSContext updateLastJump(exitJump); IonSpew(IonSpew_InlineCaches, "Generated BINDNAME global stub at %p", code->raw()); return true; } static inline void GenerateScopeChainGuard(MacroAssembler &masm, JSObject *scopeObj, - Register scopeObjReg, Shape *shape, Label *failures) + Register scopeObjReg, UnrootedShape shape, Label *failures) { AutoAssertNoGC nogc; if (scopeObj->isCall()) { // We can skip a guard on the call object if the script's bindings are // guaranteed to be immutable (and thus cannot introduce shadowing // variables). CallObject *callObj = &scopeObj->asCall(); if (!callObj->isForEval()) { @@ -1784,18 +1785,20 @@ js::ion::BindNameCache(JSContext *cx, si IonSpew(IonSpew_InlineCaches, "BINDNAME uncacheable scope chain"); } } return holder; } bool -IonCacheName::attach(JSContext *cx, IonScript *ion, HandleObject scopeChain, HandleObject holder, Shape *shape) +IonCacheName::attach(JSContext *cx, IonScript *ion, HandleObject scopeChain, HandleObject holder, + HandleShape shape) { + AssertCanGC(); MacroAssembler masm; Label failures; Register scratchReg = outputReg().valueReg().scratchReg(); masm.mov(scopeChainReg(), scratchReg); GenerateScopeChainGuards(masm, scopeChain, holder, scratchReg, &failures);
--- a/js/src/ion/IonCaches.h +++ b/js/src/ion/IonCaches.h @@ -268,19 +268,19 @@ class IonCacheGetProperty : public IonCa } Register object() const { return u.getprop.object; } PropertyName *name() const { return u.getprop.name; } TypedOrValueRegister output() const { return u.getprop.output.data(); } bool allowGetters() const { return u.getprop.allowGetters; } bool attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder, - const Shape *shape); + HandleShape shape); bool attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder, - const Shape *shape, + HandleShape shape, const SafepointIndex *safepointIndex, void *returnAddr); }; class IonCacheSetProperty : public IonCache { public: IonCacheSetProperty(CodeOffsetJump initialJump, CodeOffsetLabel rejoinLabel, @@ -300,18 +300,18 @@ class IonCacheSetProperty : public IonCa Register object() const { return u.setprop.object; } PropertyName *name() const { return u.setprop.name; } ConstantOrRegister value() const { return u.setprop.value.data(); } bool strict() const { return u.setprop.strict; } bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape); bool attachSetterCall(JSContext *cx, IonScript *ion, HandleObject obj, HandleObject holder, HandleShape shape, void *returnAddr); - bool attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, const Shape *oldshape, - const Shape *newshape, const Shape *propshape); + bool attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldshape, + HandleShape newshape, HandleShape propshape); }; class IonCacheGetElement : public IonCache { public: IonCacheGetElement(CodeOffsetJump initialJump, CodeOffsetLabel rejoinLabel, CodeOffsetLabel cacheLabel, @@ -407,17 +407,17 @@ class IonCacheName : public IonCache TypedOrValueRegister outputReg() const { return u.name.output.data(); } bool isTypeOf() const { return kind_ == NameTypeOf; } bool attach(JSContext *cx, IonScript *ion, HandleObject scopeChain, HandleObject obj, - Shape *shape); + HandleShape shape); }; bool GetPropertyCache(JSContext *cx, size_t cacheIndex, HandleObject obj, MutableHandleValue vp); bool SetPropertyCache(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value, bool isSetName);
--- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -4450,18 +4450,18 @@ class MGetElementCache return this; } }; class MBindNameCache : public MUnaryInstruction, public SingleObjectPolicy { - PropertyName *name_; - JSScript *script_; + CompilerRootPropertyName name_; + CompilerRootScript script_; jsbytecode *pc_; MBindNameCache(MDefinition *scopeChain, PropertyName *name, JSScript *script, jsbytecode *pc) : MUnaryInstruction(scopeChain), name_(name), script_(script), pc_(pc) { setResultType(MIRType_Object); } @@ -4490,43 +4490,43 @@ class MBindNameCache } }; // Guard on an object's shape. class MGuardShape : public MUnaryInstruction, public SingleObjectPolicy { - const Shape *shape_; + CompilerRootShape shape_; BailoutKind bailoutKind_; - MGuardShape(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind) + MGuardShape(MDefinition *obj, UnrootedShape shape, BailoutKind bailoutKind) : MUnaryInstruction(obj), shape_(shape), bailoutKind_(bailoutKind) { setGuard(); setMovable(); setResultType(MIRType_Object); } public: INSTRUCTION_HEADER(GuardShape) - static MGuardShape *New(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind) { + static MGuardShape *New(MDefinition *obj, UnrootedShape shape, BailoutKind bailoutKind) { return new MGuardShape(obj, shape, bailoutKind); } TypePolicy *typePolicy() { return this; } MDefinition *obj() const { return getOperand(0); } - const Shape *shape() const { + const UnrootedShape shape() const { return shape_; } BailoutKind bailoutKind() const { return bailoutKind_; } bool congruentTo(MDefinition * const &ins) const { if (!ins->isGuardShape()) return false;
--- a/js/src/ion/VMFunctions.cpp +++ b/js/src/ion/VMFunctions.cpp @@ -137,17 +137,17 @@ bool InitProp(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value) { // Copy the incoming value. This may be overwritten; the return value is discarded. RootedValue rval(cx, value); RootedId id(cx, NameToId(name)); if (name == cx->names().proto) return baseops::SetPropertyHelper(cx, obj, obj, id, 0, &rval, false); - return !!DefineNativeProperty(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, 0, 0, 0); + return DefineNativeProperty(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, 0, 0, 0); } template<bool Equal> bool LooselyEqual(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res) { bool equal; if (!js::LooselyEqual(cx, lhs, rhs, &equal))
--- a/js/src/ion/shared/Assembler-shared.h +++ b/js/src/ion/shared/Assembler-shared.h @@ -93,16 +93,22 @@ struct ImmWord // Used for immediates which require relocation. struct ImmGCPtr { uintptr_t value; explicit ImmGCPtr(const gc::Cell *ptr) : value(reinterpret_cast<uintptr_t>(ptr)) { } + + // ImmGCPtr is rooted so we can convert safely directly from Unrooted<T>. + template <typename T> + explicit ImmGCPtr(Unrooted<T> ptr) + : value(reinterpret_cast<uintptr_t>(static_cast<T>(ptr))) + { } }; // Specifies a hardcoded, absolute address. struct AbsoluteAddress { void *addr; explicit AbsoluteAddress(void *addr) : addr(addr)
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3824,18 +3824,18 @@ DefinePropertyById(JSContext *cx, Handle ? JS_FUNC_TO_DATA_PTR(JSObject *, getter) : NULL, (attrs & JSPROP_SETTER) ? JS_FUNC_TO_DATA_PTR(JSObject *, setter) : NULL); JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); if (flags != 0 && obj->isNative()) { - return !!DefineNativeProperty(cx, obj, id, value, getter, setter, - attrs, flags, tinyid); + return DefineNativeProperty(cx, obj, id, value, getter, setter, + attrs, flags, tinyid); } return JSObject::defineGeneric(cx, obj, id, value, getter, setter, attrs); } JS_PUBLIC_API(JSBool) JS_DefinePropertyById(JSContext *cx, JSObject *objArg, jsid idArg, jsval valueArg, JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) { @@ -4472,53 +4472,55 @@ JS_DeleteElement(JSContext *cx, JSObject JS_PUBLIC_API(JSBool) JS_DeleteProperty(JSContext *cx, JSObject *objArg, const char *name) { jsval junk; return JS_DeleteProperty2(cx, objArg, name, &junk); } -static Shape * +static UnrootedShape LastConfigurableShape(JSObject *obj) { for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) { - Shape *shape = &r.front(); + UnrootedShape shape = &r.front(); if (shape->configurable()) return shape; } - return NULL; + return UnrootedShape(NULL); } JS_PUBLIC_API(void) JS_ClearNonGlobalObject(JSContext *cx, JSObject *objArg) { RootedObject obj(cx, objArg); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); JS_ASSERT(!obj->isGlobal()); if (!obj->isNative()) return; /* Remove all configurable properties from obj. */ - while (Shape *shape = LastConfigurableShape(obj)) { + RootedShape shape(cx); + while ((shape = LastConfigurableShape(obj))) { if (!obj->removeProperty(cx, shape->propid())) return; } /* Set all remaining writable plain data properties to undefined. */ for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) { - Shape *shape = &r.front(); + UnrootedShape shape = &r.front(); if (shape->isDataDescriptor() && shape->writable() && shape->hasDefaultSetter() && - shape->hasSlot()) { + shape->hasSlot()) + { obj->nativeSetSlot(shape->slot(), UndefinedValue()); } } } JS_PUBLIC_API(void) JS_SetAllNonReservedSlotsToUndefined(JSContext *cx, JSObject *objArg) { @@ -4551,17 +4553,17 @@ JS_Enumerate(JSContext *cx, JSObject *ob return NULL; return ida; } /* * XXX reverse iterator for properties, unreverse and meld with jsinterp.c's * prop_iterator_class somehow... * + preserve the obj->enumerate API while optimizing the native object case - * + native case here uses a Shape *, but that iterates in reverse! + * + native case here uses a JSShape *, but that iterates in reverse! * + so we make non-native match, by reverse-iterating after JS_Enumerating */ const uint32_t JSSLOT_ITER_INDEX = 0; static void prop_iter_finalize(FreeOp *fop, RawObject obj) { void *pdata = obj->getPrivate(); @@ -4583,17 +4585,17 @@ prop_iter_trace(JSTracer *trc, RawObject return; if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() < 0) { /* * Native case: just mark the next property to visit. We don't need a * barrier here because the pointer is updated via setPrivate, which * always takes a barrier. */ - Shape *tmp = (Shape *)pdata; + UnrootedShape tmp = static_cast<RawShape>(pdata); MarkShapeUnbarriered(trc, &tmp, "prop iter shape"); obj->setPrivateUnbarriered(tmp); } else { /* Non-native case: mark each id in the JSIdArray private. */ JSIdArray *ida = (JSIdArray *) pdata; MarkIdRange(trc, ida->length, ida->vector, "prop iter"); } } @@ -4648,42 +4650,39 @@ JS_NewPropertyIterator(JSContext *cx, JS return iterobj; } JS_PUBLIC_API(JSBool) JS_NextProperty(JSContext *cx, JSObject *iterobjArg, jsid *idp) { RootedObject iterobj(cx, iterobjArg); AutoAssertNoGC nogc; - int32_t i; - Shape *shape; - JSIdArray *ida; AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, iterobj); - i = iterobj->getSlot(JSSLOT_ITER_INDEX).toInt32(); + int32_t i = iterobj->getSlot(JSSLOT_ITER_INDEX).toInt32(); if (i < 0) { /* Native case: private data is a property tree node pointer. */ JS_ASSERT(iterobj->getParent()->isNative()); - shape = (Shape *) iterobj->getPrivate(); + UnrootedShape shape = static_cast<RawShape>(iterobj->getPrivate()); while (shape->previous() && !shape->enumerable()) shape = shape->previous(); if (!shape->previous()) { JS_ASSERT(shape->isEmptyShape()); *idp = JSID_VOID; } else { - iterobj->setPrivateGCThing(const_cast<Shape *>(shape->previous().get())); + iterobj->setPrivateGCThing(const_cast<RawShape>(shape->previous().get())); *idp = shape->propid(); } } else { /* Non-native case: use the ida enumerated when iterobj was created. */ - ida = (JSIdArray *) iterobj->getPrivate(); + JSIdArray *ida = (JSIdArray *) iterobj->getPrivate(); JS_ASSERT(i <= ida->length); STATIC_ASSUME(i <= ida->length); if (i == 0) { *idp = JSID_VOID; } else { *idp = ida->vector[--i]; iterobj->setSlot(JSSLOT_ITER_INDEX, Int32Value(i)); }
--- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -201,24 +201,24 @@ js::StringIsArrayIndex(JSLinearString *s JS_ASSERT(index <= MAX_ARRAY_INDEX); *indexp = index; return true; } return false; } -Shape * +UnrootedShape js::GetDenseArrayShape(JSContext *cx, HandleObject globalObj) { JS_ASSERT(globalObj); JSObject *proto = globalObj->global().getOrCreateArrayPrototype(cx); if (!proto) - return NULL; + return UnrootedShape(NULL); return EmptyShape::getInitialShape(cx, &ArrayClass, proto, proto->getParent(), gc::FINALIZE_OBJECT0); } bool JSObject::willBeSparseDenseArray(unsigned requiredCapacity, unsigned newElementsHint) { @@ -260,17 +260,17 @@ JSObject::arrayGetOwnDataElement(JSConte *vp = getDenseArrayElement(uint32_t(i)); return true; } jsid id; if (!IndexToId(cx, i, &id)) return false; - Shape *shape = nativeLookup(cx, id); + UnrootedShape shape = nativeLookup(cx, id); if (!shape || !shape->isDataDescriptor()) vp->setMagic(JS_ARRAY_HOLE); else *vp = getSlot(shape->slot()); return true; } bool @@ -1249,18 +1249,18 @@ AddLengthProperty(JSContext *cx, HandleO */ RootedId lengthId(cx, NameToId(cx->names().length)); JS_ASSERT(!obj->nativeLookup(cx, lengthId)); if (!obj->allocateSlowArrayElements(cx)) return false; - return obj->addProperty(cx, lengthId, array_length_getter, array_length_setter, - SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0); + return JSObject::addProperty(cx, obj, lengthId, array_length_getter, array_length_setter, + SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0); } /* * Convert an array object from fast-and-dense to slow-and-flexible. */ /* static */ bool JSObject::makeDenseArraySlow(JSContext *cx, HandleObject obj) { @@ -1289,37 +1289,39 @@ JSObject::makeDenseArraySlow(JSContext * /* Root all values in the array during conversion. */ AutoValueArray autoArray(cx, (Value *) elems, arrayInitialized); /* * Save old map now, before calling InitScopeForObject. We'll have to undo * on error. This is gross, but a better way is not obvious. Note: the * exact contents of the array are not preserved on error. */ - js::Shape *oldShape = obj->lastProperty(); + RootedShape oldShape(cx, obj->lastProperty()); /* Create a native scope. */ - gc::AllocKind kind = obj->getAllocKind(); - Shape *shape = EmptyShape::getInitialShape(cx, &SlowArrayClass, obj->getProto(), - oldShape->getObjectParent(), kind); - if (!shape) - return false; - - /* - * In case an incremental GC is already running, we need to write barrier - * the elements before (temporarily) destroying them. - * - * Note: this has to happen after getInitialShape (which can trigger - * incremental GC) and *before* we overwrite shape, making us no longer a - * dense array. - */ - if (obj->compartment()->needsBarrier()) - obj->prepareElementRangeForOverwrite(0, arrayInitialized); - - obj->shape_ = shape; + { + gc::AllocKind kind = obj->getAllocKind(); + UnrootedShape shape = EmptyShape::getInitialShape(cx, &SlowArrayClass, obj->getProto(), + oldShape->getObjectParent(), kind); + if (!shape) + return false; + + /* + * In case an incremental GC is already running, we need to write barrier + * the elements before (temporarily) destroying them. + * + * Note: this has to happen after getInitialShape (which can trigger + * incremental GC) and *before* we overwrite shape, making us no longer a + * dense array. + */ + if (obj->compartment()->needsBarrier()) + obj->prepareElementRangeForOverwrite(0, arrayInitialized); + + obj->shape_ = shape; + } /* Reset to an empty dense array. */ obj->elements = emptyObjectElements; /* * Begin with the length property to share more of the property tree. * The getter/setter here will directly access the object's private value. */
--- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -71,17 +71,17 @@ NewDenseCopiedArray(JSContext *cx, uint3 extern JSObject * NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *values, RawObject proto = NULL); /* Create a sparse array. */ extern JSObject * NewSlowEmptyArray(JSContext *cx); /* Get the common shape used by all dense arrays with a prototype at globalObj. */ -extern Shape * +extern UnrootedShape GetDenseArrayShape(JSContext *cx, HandleObject globalObj); extern JSBool GetLengthProperty(JSContext *cx, HandleObject obj, uint32_t *lengthp); extern JSBool SetLengthProperty(JSContext *cx, HandleObject obj, double length);
--- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -423,16 +423,17 @@ js::InternNonIntElementId(JSContext *cx, vp.setString(atom); return true; } template<XDRMode mode> bool js::XDRAtom(XDRState<mode> *xdr, MutableHandleAtom atomp) { + AssertCanGC(); if (mode == XDR_ENCODE) { uint32_t nchars = atomp->length(); if (!xdr->codeUint32(&nchars)) return false; jschar *chars = const_cast<jschar *>(atomp->getChars(xdr->cx())); if (!chars) return false;
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -568,17 +568,17 @@ js_ReportErrorVA(JSContext *cx, unsigned } /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */ void js::ReportUsageError(JSContext *cx, HandleObject callee, const char *msg) { const char *usageStr = "usage"; PropertyName *usageAtom = Atomize(cx, usageStr, strlen(usageStr))->asPropertyName(); - DebugOnly<Shape *> shape = callee->nativeLookup(cx, NameToId(usageAtom)); + DebugOnly<RawShape> shape = static_cast<RawShape>(callee->nativeLookup(cx, NameToId(usageAtom))); JS_ASSERT(!shape->configurable()); JS_ASSERT(!shape->writable()); JS_ASSERT(shape->hasDefaultGetter()); jsval usage; if (!JS_LookupProperty(cx, callee, "usage", &usage)) return;
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -312,17 +312,17 @@ class NewObjectCache inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry); /* Fill an entry after a cache miss. */ inline void fillProto(EntryIndex entry, Class *clasp, js::TaggedProto proto, gc::AllocKind kind, JSObject *obj); inline void fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj); inline void fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj); /* Invalidate any entries which might produce an object with shape/proto. */ - void invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto); + void invalidateEntriesForShape(JSContext *cx, HandleShape shape, HandleObject proto); private: inline bool lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry); inline void fill(EntryIndex entry, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj); static inline void copyCachedToObject(JSObject *dst, JSObject *src); }; /*
--- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -793,17 +793,17 @@ JS_EvaluateInStackFrame(JSContext *cx, J return ok; } /************************************************************************/ /* This all should be reworked to avoid requiring JSScopeProperty types. */ static JSBool -GetPropertyDesc(JSContext *cx, JSObject *obj_, Shape *shape, JSPropertyDesc *pd) +GetPropertyDesc(JSContext *cx, JSObject *obj_, HandleShape shape, JSPropertyDesc *pd) { assertSameCompartment(cx, obj_); pd->id = IdToJsval(shape->propid()); RootedObject obj(cx, obj_); JSBool wasThrowing = cx->isExceptionPending(); Value lastException = UndefinedValue(); @@ -896,17 +896,17 @@ JS_GetPropertyDescArray(JSContext *cx, J for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) { pd[i].id = JSVAL_NULL; pd[i].value = JSVAL_NULL; pd[i].alias = JSVAL_NULL; if (!js_AddRoot(cx, &pd[i].id, NULL)) goto bad; if (!js_AddRoot(cx, &pd[i].value, NULL)) goto bad; - Shape *shape = const_cast<Shape *>(&r.front()); + RootedShape shape(cx, const_cast<Shape *>(&r.front())); if (!GetPropertyDesc(cx, obj, shape, &pd[i])) goto bad; if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL)) goto bad; if (++i == obj->propertyCount()) break; } pda->length = i;
--- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -251,17 +251,17 @@ JS_FRIEND_API(JSBool) JS_WrapAutoIdVector(JSContext *cx, js::AutoIdVector &props) { return cx->compartment->wrap(cx, props); } JS_FRIEND_API(void) JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape) { - MarkCycleCollectorChildren(trc, (Shape *)shape); + MarkCycleCollectorChildren(trc, static_cast<RawShape>(shape)); } static bool DefineHelpProperty(JSContext *cx, HandleObject obj, const char *prop, const char *value) { JSAtom *atom = Atomize(cx, value, strlen(value)); if (!atom) return false; @@ -900,17 +900,17 @@ js::IncrementalReferenceBarrier(void *pt uint32_t kind = gc::GetGCThingTraceKind(ptr); if (kind == JSTRACE_OBJECT) JSObject::writeBarrierPre(reinterpret_cast<RawObject>(ptr)); else if (kind == JSTRACE_STRING) JSString::writeBarrierPre(reinterpret_cast<RawString>(ptr)); else if (kind == JSTRACE_SCRIPT) JSScript::writeBarrierPre(reinterpret_cast<RawScript>(ptr)); else if (kind == JSTRACE_SHAPE) - Shape::writeBarrierPre((Shape *) ptr); + Shape::writeBarrierPre(reinterpret_cast<RawShape>(ptr)); else if (kind == JSTRACE_BASE_SHAPE) BaseShape::writeBarrierPre(reinterpret_cast<RawBaseShape>(ptr)); else if (kind == JSTRACE_TYPE_OBJECT) types::TypeObject::writeBarrierPre((types::TypeObject *) ptr); else JS_NOT_REACHED("invalid trace kind"); }
--- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -590,17 +590,17 @@ js_NewGCExternalString(JSContext *cx) } inline JSScript * js_NewGCScript(JSContext *cx) { return js::gc::NewGCThing<JSScript>(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript)); } -inline js::Shape * +inline js::UnrootedShape js_NewGCShape(JSContext *cx) { return js::gc::NewGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape)); } inline js::UnrootedBaseShape js_NewGCBaseShape(JSContext *cx) {
--- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -802,42 +802,43 @@ class TypeConstraintFilterPrimitive : pu void HeapTypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target) { add(cx, cx->typeLifoAlloc().new_<TypeConstraintFilterPrimitive>(target)); } /* If id is a normal slotful 'own' property of an object, get its shape. */ -static inline Shape * -GetSingletonShape(JSContext *cx, HandleObject obj, jsid id) -{ +static inline UnrootedShape +GetSingletonShape(JSContext *cx, RawObject obj, RawId id) +{ + AssertCanGC(); if (!obj->isNative()) - return NULL; - Shape *shape = obj->nativeLookup(cx, id); + return UnrootedShape(NULL); + UnrootedShape shape = DropUnrooted(obj)->nativeLookup(cx, DropUnrooted(id)); if (shape && shape->hasDefaultGetter() && shape->hasSlot()) return shape; - return NULL; + return UnrootedShape(NULL); } void ScriptAnalysis::pruneTypeBarriers(JSContext *cx, uint32_t offset) { + AssertCanGC(); TypeBarrier **pbarrier = &getCode(offset).typeBarriers; while (*pbarrier) { TypeBarrier *barrier = *pbarrier; if (barrier->target->hasType(barrier->type)) { /* Barrier is now obsolete, it can be removed. */ *pbarrier = barrier->next; continue; } if (barrier->singleton) { JS_ASSERT(barrier->type.isPrimitive(JSVAL_TYPE_UNDEFINED)); - RootedObject barrierSingleton(cx, barrier->singleton); - Shape *shape = GetSingletonShape(cx, barrierSingleton, barrier->singletonId); + UnrootedShape shape = GetSingletonShape(cx, barrier->singleton, barrier->singletonId); if (shape && !barrier->singleton->nativeGetSlot(shape->slot()).isUndefined()) { /* * When we analyzed the script the singleton had an 'own' * property which was undefined (probably a 'var' variable * added to a global object), but now it is defined. The only * way it can become undefined again is if an explicit assign * or deletion on the property occurs, which will update the * type set for the property directly and trigger construction @@ -1669,16 +1670,17 @@ class TypeConstraintFreezeObjectFlags : {} const char *kind() { return "freezeObjectFlags"; } void newType(JSContext *cx, TypeSet *source, Type type) {} void newObjectState(JSContext *cx, TypeObject *object, bool force) { + AutoAssertNoGC nogc; if (!marked && (object->hasAnyFlags(flags) || (!flags && force))) { marked = true; cx->compartment->types.addPendingRecompile(cx, info); } } }; bool @@ -2543,16 +2545,17 @@ TypeCompartment::nukeTypes(FreeOp *fop) #endif /* JS_METHODJIT */ pendingNukeTypes = false; } void TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info) { + AutoAssertNoGC nogc; CompilerOutput *co = info.compilerOutput(cx); if (co->pendingRecompilation) return; if (co->isValid()) CancelOffThreadIonCompile(cx->compartment, co->script); @@ -3022,23 +3025,24 @@ struct types::ObjectTableKey typedef JSObject * Lookup; static inline uint32_t hash(JSObject *obj) { return (uint32_t) (JSID_BITS(obj->lastProperty()->propid().get()) ^ obj->slotSpan() ^ obj->numFixedSlots() ^ ((uint32_t)obj->getTaggedProto().toWord() >> 2)); } - static inline bool match(const ObjectTableKey &v, JSObject *obj) { + static inline bool match(const ObjectTableKey &v, RawObject obj) { if (obj->slotSpan() != v.nslots || obj->numFixedSlots() != v.nfixed || obj->getTaggedProto() != v.proto) { return false; } - Shape *shape = obj->lastProperty(); + UnrootedShape shape = obj->lastProperty(); + obj = NULL; while (!shape->isEmptyShape()) { if (shape->propid() != v.ids[shape->slot()]) return false; shape = shape->previous(); } return true; } }; @@ -3070,28 +3074,28 @@ TypeCompartment::fixObjectType(JSContext * generic unknown type. */ JS_ASSERT(obj->isObject()); if (obj->slotSpan() == 0 || obj->inDictionaryMode()) return; ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(obj.get()); - Shape *baseShape = obj->lastProperty(); + RootedShape baseShape(cx, obj->lastProperty()); if (p) { /* The lookup ensures the shape matches, now check that the types match. */ Type *types = p->value.types; for (unsigned i = 0; i < obj->slotSpan(); i++) { Type ntype = GetValueTypeForTable(cx, obj->getSlot(i)); if (ntype != types[i]) { if (NumberTypes(ntype, types[i])) { if (types[i].isPrimitive(JSVAL_TYPE_INT32)) { types[i] = Type::DoubleType(); - Shape *shape = baseShape; + RootedShape shape(cx, baseShape); while (!shape->isEmptyShape()) { if (shape->slot() == i) { Type type = Type::DoubleType(); if (!p->value.object->unknownProperties()) { jsid id = MakeTypeId(cx, shape->propid()); p->value.object->addPropertyType(cx, id, type); } break; @@ -3122,17 +3126,17 @@ TypeCompartment::fixObjectType(JSContext } Type *types = cx->pod_calloc<Type>(obj->slotSpan()); if (!types) { cx->compartment->types.setPendingNukeTypes(cx); return; } - Shape *shape = baseShape; + RootedShape shape(cx, baseShape); while (!shape->isEmptyShape()) { ids[shape->slot()] = shape->propid(); types[shape->slot()] = GetValueTypeForTable(cx, obj->getSlot(shape->slot())); if (!objType->unknownProperties()) { jsid id = MakeTypeId(cx, shape->propid()); objType->addPropertyType(cx, id, types[shape->slot()]); } shape = shape->previous(); @@ -3189,17 +3193,17 @@ TypeObject::getFromPrototypes(JSContext return; protoTypes->addSubset(cx, types); proto->getType(cx)->getFromPrototypes(cx, id, protoTypes); } static inline void -UpdatePropertyType(JSContext *cx, TypeSet *types, HandleObject obj, Shape *shape, bool force) +UpdatePropertyType(JSContext *cx, TypeSet *types, RawObject obj, UnrootedShape shape, bool force) { types->setOwnProperty(cx, false); if (!shape->writable()) types->setOwnProperty(cx, true); if (shape->hasGetterValue() || shape->hasSetterValue()) { types->setOwnProperty(cx, true); types->addType(cx, Type::UnknownType()); @@ -3234,24 +3238,24 @@ TypeObject::addProperty(JSContext *cx, j * which don't go through a barrier when read by the VM or jitcode. * We don't need to handle arrays or other JIT'ed non-natives as * these are not (yet) singletons. */ RootedObject rSingleton(cx, singleton); if (JSID_IS_VOID(id)) { /* Go through all shapes on the object to get integer-valued properties. */ - Shape *shape = singleton->lastProperty(); + UnrootedShape shape = singleton->lastProperty(); while (!shape->isEmptyShape()) { if (JSID_IS_VOID(MakeTypeId(cx, shape->propid()))) UpdatePropertyType(cx, &base->types, rSingleton, shape, true); shape = shape->previous(); } } else if (!JSID_IS_EMPTY(id) && singleton->isNative()) { - Shape *shape = singleton->nativeLookup(cx, id); + UnrootedShape shape = singleton->nativeLookup(cx, id); if (shape) UpdatePropertyType(cx, &base->types, rSingleton, shape, false); } if (singleton->watched()) { /* * Mark the property as configured, to inhibit optimizations on it * and avoid bypassing the watchpoint handler. @@ -3273,17 +3277,17 @@ bool TypeObject::addDefiniteProperties(JSContext *cx, HandleObject obj) { if (unknownProperties()) return true; /* Mark all properties of obj as definite properties of this type. */ AutoEnterTypeInference enter(cx); - Shape *shape = obj->lastProperty(); + RootedShape shape(cx, obj->lastProperty()); while (!shape->isEmptyShape()) { jsid id = MakeTypeId(cx, shape->propid()); if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot()) && shape->slot() <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) { TypeSet *types = getProperty(cx, id, true); if (!types) return false; types->setDefinite(shape->slot()); @@ -3301,17 +3305,17 @@ TypeObject::matchDefiniteProperties(Hand for (unsigned i = 0; i < count; i++) { Property *prop = getProperty(i); if (!prop) continue; if (prop->types.definiteProperty()) { unsigned slot = prop->types.definiteSlot(); bool found = false; - Shape *shape = obj->lastProperty(); + UnrootedShape shape = obj->lastProperty(); while (!shape->isEmptyShape()) { if (shape->slot() == slot && shape->propid() == prop->id) { found = true; break; } shape = shape->previous(); } if (!found) @@ -3320,16 +3324,17 @@ TypeObject::matchDefiniteProperties(Hand } return true; } inline void InlineAddTypeProperty(JSContext *cx, TypeObject *obj, jsid id, Type type) { + AssertCanGC(); JS_ASSERT(id == MakeTypeId(cx, id)); AutoEnterTypeInference enter(cx); TypeSet *types = obj->getProperty(cx, id, true); if (!types || types->hasType(type)) return; @@ -3348,16 +3353,17 @@ void TypeObject::addPropertyType(JSContext *cx, jsid id, const Value &value) { InlineAddTypeProperty(cx, this, id, GetValueType(cx, value)); } void TypeObject::addPropertyType(JSContext *cx, const char *name, Type type) { + AssertCanGC(); jsid id = JSID_VOID; if (name) { JSAtom *atom = Atomize(cx, name, strlen(name)); if (!atom) { AutoEnterTypeInference enter(cx); cx->compartment->types.setPendingNukeTypes(cx); return; } @@ -3382,16 +3388,18 @@ TypeObject::markPropertyConfigured(JSCon TypeSet *types = getProperty(cx, id, true); if (types) types->setOwnProperty(cx, true); } void TypeObject::markStateChange(JSContext *cx) { + AutoAssertNoGC nogc; + if (unknownProperties()) return; AutoEnterTypeInference enter(cx); TypeSet *types = maybeGetProperty(cx, JSID_EMPTY); if (types) { TypeConstraint *constraint = types->constraintList; while (constraint) { @@ -5015,16 +5023,18 @@ CheckNewScriptProperties(JSContext *cx, type->newScript = (TypeNewScript *) cx->calloc_(numBytes); #endif if (!type->newScript) { cx->compartment->types.setPendingNukeTypes(cx); return; } + AutoAssertNoGC nogc; + type->newScript->fun = fun; type->newScript->allocKind = kind; type->newScript->shape = baseobj->lastProperty(); type->newScript->initializerList = (TypeNewScript::Initializer *) ((char *) type->newScript.get() + sizeof(TypeNewScript)); PodCopy(type->newScript->initializerList, initializerList.begin(), initializerList.length()); } @@ -5823,16 +5833,17 @@ JSObject::setNewTypeUnknown(JSContext *c } return true; } TypeObject * JSCompartment::getNewType(JSContext *cx, TaggedProto proto_, JSFunction *fun_, bool isDOM) { + AssertCanGC(); JS_ASSERT_IF(fun_, proto_.isObject()); JS_ASSERT_IF(proto_.isObject(), cx->compartment == proto_.toObject()->compartment()); if (!newTypeObjects.initialized() && !newTypeObjects.init()) return NULL; TypeObjectSet::AddPtr p = newTypeObjects.lookupForAdd(proto_); if (p) {
--- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -270,16 +270,17 @@ TypeFlagPrimitive(TypeFlags flags) /* * Get the canonical representation of an id to use when doing inference. This * maintains the constraint that if two different jsids map to the same property * in JS (e.g. 3 and "3"), they have the same type representation. */ inline jsid MakeTypeId(JSContext *cx, jsid id) { + AutoAssertNoGC nogc; JS_ASSERT(!JSID_IS_EMPTY(id)); /* * All integers must map to the aggregate property for index types, including * negative integers. */ if (JSID_IS_INT(id)) return JSID_VOID; @@ -541,54 +542,60 @@ TypeMonitorCall(JSContext *cx, const js: } return true; } inline bool TrackPropertyTypes(JSContext *cx, HandleObject obj, jsid id) { + AutoAssertNoGC nogc; + if (!cx->typeInferenceEnabled() || obj->hasLazyType() || obj->type()->unknownProperties()) return false; if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(cx, id)) return false; return true; } /* Add a possible type for a property of obj. */ inline void AddTypePropertyId(JSContext *cx, HandleObject obj, jsid id, Type type) { + AssertCanGC(); if (cx->typeInferenceEnabled()) id = MakeTypeId(cx, id); if (TrackPropertyTypes(cx, obj, id)) obj->type()->addPropertyType(cx, id, type); } inline void AddTypePropertyId(JSContext *cx, HandleObject obj, jsid id, const Value &value) { + AssertCanGC(); if (cx->typeInferenceEnabled()) id = MakeTypeId(cx, id); if (TrackPropertyTypes(cx, obj, id)) obj->type()->addPropertyType(cx, id, value); } inline void AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, Type type) { + AssertCanGC(); if (cx->typeInferenceEnabled() && !obj->unknownProperties()) obj->addPropertyType(cx, name, type); } inline void AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, const Value &value) { + AssertCanGC(); if (cx->typeInferenceEnabled() && !obj->unknownProperties()) obj->addPropertyType(cx, name, value); } /* Set one or more dynamic flags on a type object. */ inline void MarkTypeObjectFlags(JSContext *cx, RawObject obj, TypeObjectFlags flags) { @@ -624,18 +631,19 @@ MarkTypePropertyConfigured(JSContext *cx if (cx->typeInferenceEnabled()) id = MakeTypeId(cx, id); if (TrackPropertyTypes(cx, obj, id)) obj->type()->markPropertyConfigured(cx, id); } /* Mark a state change on a particular object. */ inline void -MarkObjectStateChange(JSContext *cx, HandleObject obj) +MarkObjectStateChange(JSContext *cx, RawObject obj) { + AutoAssertNoGC nogc; if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->unknownProperties()) obj->type()->markStateChange(cx); } /* * For an array or object which has not yet escaped and been referenced elsewhere, * pick a new type based on the object's current contents. */ @@ -1560,16 +1568,17 @@ TypeObject::getProperty(JSContext *cx, j types->setOwnProperty(cx, false); return types; } inline HeapTypeSet * TypeObject::maybeGetProperty(JSContext *cx, jsid id) { + AutoAssertNoGC nogc; JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id)); JS_ASSERT(!unknownProperties()); Property *prop = HashSetLookup<jsid,Property,Property> (propertySet, basePropertyCount(), id); return prop ? &prop->types : NULL;
--- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -169,25 +169,26 @@ ValuePropertyBearer(JSContext *cx, Stack return global.getOrCreateBooleanPrototype(cx); JS_ASSERT(v.isNull() || v.isUndefined()); js_ReportIsNullOrUndefined(cx, spindex, v, NullPtr()); return NULL; } inline bool -NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Shape *shape, +NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Shape *shapeArg, unsigned getHow, MutableHandleValue vp) { - if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { + if (shapeArg->isDataDescriptor() && shapeArg->hasDefaultGetter()) { /* Fast path for Object instance properties. */ - JS_ASSERT(shape->hasSlot()); - vp.set(pobj->nativeGetSlot(shape->slot())); + JS_ASSERT(shapeArg->hasSlot()); + vp.set(pobj->nativeGetSlot(shapeArg->slot())); } else { - if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp.address())) + RootedShape shape(cx, shapeArg); + if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp)) return false; } return true; } #if defined(DEBUG) && !defined(JS_THREADSAFE) && !defined(JSGC_ROOT_ANALYSIS) extern void AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found,
--- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -426,18 +426,18 @@ NewPropertyIteratorObject(JSContext *cx, } NativeIterator * NativeIterator::allocateIterator(JSContext *cx, uint32_t slength, const AutoIdVector &props) { size_t plength = props.length(); NativeIterator *ni = (NativeIterator *) cx->malloc_(sizeof(NativeIterator) - + plength * sizeof(JSString *) - + slength * sizeof(Shape *)); + + plength * sizeof(RawString) + + slength * sizeof(RawShape)); if (!ni) return NULL; AutoValueVector strings(cx); ni->props_array = ni->props_cursor = (HeapPtr<JSFlatString> *) (ni + 1); ni->props_end = ni->props_array + plength; if (plength) { for (size_t i = 0; i < plength; i++) { JSFlatString *str = IdToString(cx, props[i]); @@ -597,17 +597,17 @@ js::GetIterator(JSContext *cx, HandleObj RawObject obj = ToObject(cx, vp); if (!obj) return false; vp.setObject(*obj); return true; } - Vector<Shape *, 8> shapes(cx); + Vector<RawShape, 8> shapes(cx); uint32_t key = 0; bool keysOnly = (flags == JSITER_ENUMERATE); if (obj) { if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) { RawObject iterobj = op(cx, obj, !(flags & JSITER_FOREACH)); if (!iterobj) @@ -646,28 +646,29 @@ js::GetIterator(JSContext *cx, HandleObj /* * The iterator object for JSITER_ENUMERATE never escapes, so we * don't care for the proper parent/proto to be set. This also * allows us to re-use a previous iterator object that is not * currently active. */ { + AutoAssertNoGC nogc; RawObject pobj = obj; do { if (!pobj->isNative() || pobj->hasUncacheableProto() || obj->getOps()->enumerate || pobj->getClass()->enumerate != JS_EnumerateStub) { shapes.clear(); goto miss; } - Shape *shape = pobj->lastProperty(); + RawShape shape = pobj->lastProperty(); key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3); - if (!shapes.append((Shape *) shape)) + if (!shapes.append(shape)) return false; pobj = pobj->getProto(); } while (pobj); } PropertyIteratorObject *iterobj = cx->runtime->nativeIterCache.get(key); if (iterobj) { NativeIterator *ni = iterobj->getNativeIterator();
--- a/js/src/jsmemorymetrics.cpp +++ b/js/src/jsmemorymetrics.cpp @@ -192,17 +192,17 @@ StatsCellCallback(JSRuntime *rt, void *d } else { cStats->gcHeapStringsNormal += thingSize; cStats->stringCharsNonHuge += strSize; } break; } case JSTRACE_SHAPE: { - Shape *shape = static_cast<Shape*>(thing); + UnrootedShape shape = static_cast<RawShape>(thing); size_t propTableSize, kidsSize; shape->sizeOfExcludingThis(rtStats->mallocSizeOf, &propTableSize, &kidsSize); if (shape->inDictionary()) { cStats->gcHeapShapesDict += thingSize; cStats->shapesExtraDictTables += propTableSize; JS_ASSERT(kidsSize == 0); } else { if (shape->base()->getObjectParent() == shape->compartment()->maybeGlobal()) {
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1343,17 +1343,17 @@ DefinePropertyOnObject(JSContext *cx, Ha if (!shape->configurable() && (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()) && desc.isDataDescriptor() && (desc.hasWritable() ? desc.writable() : shape->writable())) { return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); } - if (!js_NativeGet(cx, obj, obj2, shape, 0, v.address())) + if (!js_NativeGet(cx, obj, obj2, shape, 0, &v)) return JS_FALSE; } if (desc.isDataDescriptor()) { if (!shape->isDataDescriptor()) break; bool same; @@ -1875,21 +1875,21 @@ JSObject::sealOrFreeze(JSContext *cx, Ha if (obj->isNative() && !obj->inDictionaryMode()) { /* * Seal/freeze non-dictionary objects by constructing a new shape * hierarchy mirroring the original one, which can be shared if many * objects with the same structure are sealed/frozen. If we use the * generic path below then any non-empty object will be converted to * dictionary mode. */ - Shape *last = EmptyShape::getInitialShape(cx, obj->getClass(), - obj->getTaggedProto(), - obj->getParent(), - obj->getAllocKind(), - obj->lastProperty()->getObjectFlags()); + RootedShape last(cx, EmptyShape::getInitialShape(cx, obj->getClass(), + obj->getTaggedProto(), + obj->getParent(), + obj->getAllocKind(), + obj->lastProperty()->getObjectFlags())); if (!last) return false; /* Get an in order list of the shapes in this object. */ AutoShapeVector shapes(cx); for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) { if (!shapes.append(&r.front())) return false; @@ -2108,16 +2108,17 @@ js_Object(JSContext *cx, unsigned argc, vp->setObject(*obj); return JS_TRUE; } static inline JSObject * NewObject(JSContext *cx, Class *clasp, types::TypeObject *type_, JSObject *parent, gc::AllocKind kind) { + AssertCanGC(); JS_ASSERT(clasp != &ArrayClass); JS_ASSERT_IF(clasp == &FunctionClass, kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind); JS_ASSERT_IF(parent, &parent->global() == cx->compartment->maybeGlobal()); RootedTypeObject type(cx, type_); RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(type->proto), @@ -2286,24 +2287,26 @@ js::NewReshapedObject(JSContext *cx, Han if (!res) return NULL; if (shape->isEmptyShape()) return res; /* Get all the ids in the object, in order. */ js::AutoIdVector ids(cx); - for (unsigned i = 0; i <= shape->slot(); i++) { - if (!ids.append(JSID_VOID)) - return NULL; - } - js::Shape *nshape = shape; - while (!nshape->isEmptyShape()) { - ids[nshape->slot()] = nshape->propid(); - nshape = nshape->previous(); + { + for (unsigned i = 0; i <= shape->slot(); i++) { + if (!ids.append(JSID_VOID)) + return NULL; + } + UnrootedShape nshape = shape; + while (!nshape->isEmptyShape()) { + ids[nshape->slot()] = nshape->propid(); + nshape = nshape->previous(); + } } /* Construct the new shape. */ RootedId id(cx); RootedValue undefinedValue(cx, UndefinedValue()); for (unsigned i = 0; i < ids.length(); i++) { id = ids[i]; if (!DefineNativeProperty(cx, res, id, undefinedValue, NULL, NULL, @@ -2335,19 +2338,20 @@ CreateThisForFunctionWithType(JSContext if (type->newScript) { /* * Make an object with the type's associated finalize kind and shape, * which reflects any properties that will definitely be added to the * object before it is read from. */ gc::AllocKind kind = type->newScript->allocKind; RootedObject res(cx, NewObjectWithType(cx, type, parent, kind)); - if (res) - JS_ALWAYS_TRUE(JSObject::setLastProperty(cx, res, - (Shape *) type->newScript->shape.get())); + if (res) { + RootedShape shape(cx, type->newScript->shape); + JS_ALWAYS_TRUE(JSObject::setLastProperty(cx, res, shape)); + } return res; } gc::AllocKind kind = NewObjectGCKind(&ObjectClass); return NewObjectWithType(cx, type, parent, kind); } JSObject * @@ -2545,31 +2549,34 @@ JS_CopyPropertiesFrom(JSContext *cx, JSO return true; AutoShapeVector shapes(cx); for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) { if (!shapes.append(&r.front())) return false; } + RootedShape shape(cx); + RootedValue v(cx); + RootedId id(cx); size_t n = shapes.length(); while (n > 0) { - Shape *shape = shapes[--n]; + shape = shapes[--n]; unsigned attrs = shape->attributes(); PropertyOp getter = shape->getter(); StrictPropertyOp setter = shape->setter(); AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); if ((attrs & JSPROP_GETTER) && !cx->compartment->wrap(cx, &getter)) return false; if ((attrs & JSPROP_SETTER) && !cx->compartment->wrap(cx, &setter)) return false; - RootedValue v(cx, shape->hasSlot() ? obj->getSlot(shape->slot()) : UndefinedValue()); + v = shape->hasSlot() ? obj->getSlot(shape->slot()) : UndefinedValue(); if (!cx->compartment->wrap(cx, v.address())) return false; - Rooted<jsid> id(cx, shape->propid()); + id = shape->propid(); if (!JSObject::defineGeneric(cx, target, id, v, getter, setter, attrs)) return false; } return true; } static bool CopySlots(JSContext *cx, JSObject *from, JSObject *to) @@ -2643,41 +2650,42 @@ js::CloneObjectLiteral(JSContext *cx, Ha return NewReshapedObject(cx, typeObj, parent, srcObj->getAllocKind(), shape); } struct JSObject::TradeGutsReserved { Vector<Value> avals; Vector<Value> bvals; int newafixed; int newbfixed; - Shape *newashape; - Shape *newbshape; + RootedShape newashape; + RootedShape newbshape; HeapSlot *newaslots; HeapSlot *newbslots; TradeGutsReserved(JSContext *cx) : avals(cx), bvals(cx), newafixed(0), newbfixed(0), - newashape(NULL), newbshape(NULL), + newashape(cx), newbshape(cx), newaslots(NULL), newbslots(NULL) {} ~TradeGutsReserved() { if (newaslots) js_free(newaslots); if (newbslots) js_free(newbslots); } }; bool JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved) { + AssertCanGC(); JS_ASSERT(a->compartment() == b->compartment()); AutoCompartment ac(cx, a); /* * When performing multiple swaps between objects which may have different * numbers of fixed slots, we reserve all space ahead of time so that the * swaps can be performed infallibly. */ @@ -2777,16 +2785,17 @@ JSObject::ReserveForTradeGuts(JSContext } return true; } void JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved) { + AutoAssertNoGC nogc; JS_ASSERT(a->compartment() == b->compartment()); JS_ASSERT(a->isFunction() == b->isFunction()); /* Don't try to swap a JSFunction for a plain function JSObject. */ JS_ASSERT_IF(a->isFunction(), a->sizeOfThis() == b->sizeOfThis()); /* * Regexp guts are more complicated -- we would need to migrate the @@ -2951,21 +2960,20 @@ DefineStandardSlot(JSContext *cx, Handle /* * Initializing an actual standard class on a global object. If the * property is not yet present, force it into a new one bound to a * reserved slot. Otherwise, go through the normal property path. */ JS_ASSERT(obj->isGlobal()); JS_ASSERT(obj->isNative()); - Shape *shape = obj->nativeLookup(cx, id); - if (!shape) { + if (!obj->nativeLookup(cx, id)) { uint32_t slot = 2 * JSProto_LIMIT + key; obj->setReservedSlot(slot, v); - if (!obj->addProperty(cx, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0, 0)) + if (!JSObject::addProperty(cx, obj, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0, 0)) return false; AddTypePropertyId(cx, obj, id, v); named = true; return true; } } @@ -3229,17 +3237,17 @@ JSObject::updateSlotsForSpan(JSContext * if (oldCount > newCount) JSObject::shrinkSlots(cx, obj, oldCount, newCount); } return true; } /* static */ bool -JSObject::setLastProperty(JSContext *cx, HandleObject obj, js::Shape *shape) +JSObject::setLastProperty(JSContext *cx, HandleObject obj, HandleShape shape) { JS_ASSERT(!obj->inDictionaryMode()); JS_ASSERT(!shape->inDictionary()); JS_ASSERT(shape->compartment() == obj->compartment()); JS_ASSERT(shape->numFixedSlots() == obj->numFixedSlots()); size_t oldSpan = obj->lastProperty()->slotSpan(); size_t newSpan = shape->slotSpan(); @@ -3684,54 +3692,54 @@ js_FindClassObject(JSContext *cx, JSProt if (v.get().isPrimitive()) v.get().setUndefined(); } } vp.set(v); return true; } -bool -JSObject::allocSlot(JSContext *cx, uint32_t *slotp) +/* static */ bool +JSObject::allocSlot(JSContext *cx, HandleObject obj, uint32_t *slotp) { - uint32_t slot = slotSpan(); - JS_ASSERT(slot >= JSSLOT_FREE(getClass())); + AssertCanGC(); + uint32_t slot = obj->slotSpan(); + JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass())); /* * If this object is in dictionary mode, try to pull a free slot from the * shape table's slot-number freelist. */ - if (inDictionaryMode()) { - ShapeTable &table = lastProperty()->table(); + if (obj->inDictionaryMode()) { + ShapeTable &table = obj->lastProperty()->table(); uint32_t last = table.freelist; if (last != SHAPE_INVALID_SLOT) { #ifdef DEBUG JS_ASSERT(last < slot); - uint32_t next = getSlot(last).toPrivateUint32(); + uint32_t next = obj->getSlot(last).toPrivateUint32(); JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot); #endif *slotp = last; - const Value &vref = getSlot(last); + const Value &vref = obj->getSlot(last); table.freelist = vref.toPrivateUint32(); - setSlot(last, UndefinedValue()); + obj->setSlot(last, UndefinedValue()); return true; } } if (slot >= SHAPE_MAXIMUM_SLOT) { js_ReportOutOfMemory(cx); return false; } *slotp = slot; - RootedObject self(cx, this); - if (inDictionaryMode() && !setSlotSpan(cx, self, slot + 1)) + if (obj->inDictionaryMode() && !setSlotSpan(cx, obj, slot + 1)) return false; return true; } void JSObject::freeSlot(uint32_t slot) { @@ -3753,23 +3761,22 @@ JSObject::freeSlot(uint32_t slot) last = slot; return; } } setSlot(slot, UndefinedValue()); } static bool -PurgeProtoChain(JSContext *cx, JSObject *obj_, jsid id_) +PurgeProtoChain(JSContext *cx, RawObject objArg, HandleId id) { - Shape *shape; - - RootedObject obj(cx, obj_); - RootedId id(cx, id_); - + /* Root locally so we can re-assign. */ + RootedObject obj(cx, objArg); + + RootedShape shape(cx); while (obj) { /* Lookups will not be cached through non-native protos. */ if (!obj->isNative()) break; shape = obj->nativeLookup(cx, id); if (shape) { if (!obj->shadowingShapeChange(cx, *shape)) @@ -3780,20 +3787,20 @@ PurgeProtoChain(JSContext *cx, JSObject } obj = obj->getProto(); } return true; } bool -js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj_, jsid id_) +js_PurgeScopeChainHelper(JSContext *cx, HandleObject objArg, HandleId id) { - RootedObject obj(cx, obj_); - RootedId id(cx, id_); + /* Re-root locally so we can re-assign. */ + RootedObject obj(cx, objArg); JS_ASSERT(obj->isNative()); JS_ASSERT(obj->isDelegate()); PurgeProtoChain(cx, obj->getProto(), id); /* * We must purge the scope chain only for Call objects as they are the only * kind of cacheable non-global object that can gain properties after outer @@ -3805,57 +3812,55 @@ js_PurgeScopeChainHelper(JSContext *cx, if (!PurgeProtoChain(cx, obj, id)) return false; } } return true; } -Shape * -js_AddNativeProperty(JSContext *cx, HandleObject obj, jsid id_, +UnrootedShape +js_AddNativeProperty(JSContext *cx, HandleObject obj, HandleId id, PropertyOp getter, StrictPropertyOp setter, uint32_t slot, unsigned attrs, unsigned flags, int shortid) { - RootedId id(cx, id_); - /* * Purge the property cache of now-shadowed id in obj's scope chain. Do * this optimistically (assuming no failure below) before locking obj, so * we can lock the shadowed scope. */ if (!js_PurgeScopeChain(cx, obj, id)) - return NULL; - - return obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid); + return UnrootedShape(NULL); + + return JSObject::putProperty(cx, obj, id, getter, setter, slot, attrs, flags, shortid); } JSBool baseops::DefineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs) { - return !!DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0); + return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0); } JSBool baseops::DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs) { Rooted<jsid> id(cx); if (index <= JSID_INT_MAX) { id = INT_TO_JSID(index); - return !!DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0); + return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0); } AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); if (!IndexToId(cx, index, id.address())) return false; - return !!DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0); + return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0); } /* * Backward compatibility requires allowing addProperty hooks to mutate the * nominal initial value of a slotful property, while GC safety wants that * value to be stored before the call-out through the hook. Optimize to do * both while saving cycles for classes that stub their addProperty hook. */ @@ -3873,17 +3878,17 @@ CallAddPropertyHook(JSContext *cx, Class if (value.get() != nominal) { if (shape->hasSlot()) JSObject::nativeSetSlotWithType(cx, obj, shape, value); } } return true; } -Shape * +bool js::DefineNativeProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs, unsigned flags, int shortid, unsigned defineHow /* = 0 */) { JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_DONT_PURGE | DNP_SKIP_TYPE)) == 0); JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS)); @@ -3903,44 +3908,44 @@ js::DefineNativeProperty(JSContext *cx, /* * If we are defining a getter whose setter was already defined, or * vice versa, finish the job via obj->changeProperty, and refresh the * property cache line for (obj, id) to map shape. */ RootedObject pobj(cx); RootedShape prop(cx); if (!baseops::LookupProperty(cx, obj, id, &pobj, &prop)) - return NULL; + return false; if (prop && pobj == obj) { shape = prop; if (shape->isAccessorDescriptor()) { shape = JSObject::changeProperty(cx, obj, shape, attrs, JSPROP_GETTER | JSPROP_SETTER, (attrs & JSPROP_GETTER) ? getter : shape->getter(), (attrs & JSPROP_SETTER) ? setter : shape->setter()); if (!shape) - return NULL; + return false; } else { shape = NULL; } } } /* * Purge the property cache of any properties named by id that are about * to be shadowed in obj's scope chain unless it is known a priori that it * is not possible. We do this before locking obj to avoid nesting locks. */ if (!(defineHow & DNP_DONT_PURGE)) { if (!js_PurgeScopeChain(cx, obj, id)) - return NULL; + return false; } /* Use the object's class getter and setter by default. */ Class *clasp = obj->getClass(); if (!getter && !(attrs & JSPROP_GETTER)) getter = clasp->getProperty; if (!setter && !(attrs & JSPROP_SETTER)) setter = clasp->setProperty; @@ -3951,29 +3956,29 @@ js::DefineNativeProperty(JSContext *cx, * initial value of the property. */ AddTypePropertyId(cx, obj, id, value); if (attrs & JSPROP_READONLY) MarkTypePropertyConfigured(cx, obj, id); } if (!shape) { - shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT, - attrs, flags, shortid); + shape = JSObject::putProperty(cx, obj, id, getter, setter, SHAPE_INVALID_SLOT, + attrs, flags, shortid); if (!shape) - return NULL; + return false; } /* Store valueCopy before calling addProperty, in case the latter GC's. */ if (shape->hasSlot()) obj->nativeSetSlot(shape->slot(), value); if (!CallAddPropertyHook(cx, clasp, obj, shape, value)) { obj->removeProperty(cx, id); - return NULL; + return false; } return shape; } /* * Call obj's resolve hook. * @@ -4046,37 +4051,39 @@ CallResolveOp(JSContext *cx, HandleObjec objp.set(obj2); } else { if (!resolve(cx, obj, id)) return false; objp.set(obj); } - Shape *shape; + UnrootedShape shape; if (!objp->nativeEmpty() && (shape = objp->nativeLookup(cx, id))) propp.set(shape); else objp.set(NULL); return true; } static JS_ALWAYS_INLINE bool LookupPropertyWithFlagsInline(JSContext *cx, HandleObject obj, HandleId id, unsigned flags, MutableHandleObject objp, MutableHandleShape propp) { /* Search scopes starting with obj and following the prototype link. */ RootedObject current(cx, obj); while (true) { - Shape *shape = current->nativeLookup(cx, id); - if (shape) { - objp.set(current); - propp.set(shape); - return true; + { + UnrootedShape shape = current->nativeLookup(cx, id); + if (shape) { + objp.set(current); + propp.set(shape); + return true; + } } /* Try obj's class resolve hook if id was not found in obj's scope. */ if (current->getClass()->resolve != JS_ResolveStub) { bool recursed; if (!CallResolveOp(cx, current, id, flags, objp, propp, &recursed)) return false; if (recursed) @@ -4172,58 +4179,54 @@ js::LookupNameWithGlobalDefault(JSContex break; } objp.set(scope); return true; } static JS_ALWAYS_INLINE JSBool -js_NativeGetInline(JSContext *cx, Handle<JSObject*> receiver, JSObject *obj, JSObject *pobj, - Shape *shape, unsigned getHow, Value *vp) +js_NativeGetInline(JSContext *cx, Handle<JSObject*> receiver, Handle<JSObject*> obj, + Handle<JSObject*> pobj, Handle<Shape*> shape, unsigned getHow, + MutableHandle<Value> vp) { JS_ASSERT(pobj->isNative()); if (shape->hasSlot()) { - *vp = pobj->nativeGetSlot(shape->slot()); - JS_ASSERT(!vp->isMagic()); + vp.set(pobj->nativeGetSlot(shape->slot())); + JS_ASSERT(!vp.isMagic()); JS_ASSERT_IF(!pobj->hasSingletonType() && shape->hasDefaultGetter(), - js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), *vp)); + js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), vp)); } else { - vp->setUndefined(); + vp.setUndefined(); } if (shape->hasDefaultGetter()) return true; jsbytecode *pc; JSScript *script = cx->stack.currentScript(&pc); if (script && script->hasAnalysis()) { analyze::Bytecode *code = script->analysis()->maybeCode(pc); if (code) code->accessGetter = true; } - Rooted<Shape*> shapeRoot(cx, shape); - RootedObject pobjRoot(cx, pobj); - RootedValue nvp(cx, *vp); - - if (!shape->get(cx, receiver, obj, pobj, &nvp)) + if (!shape->get(cx, receiver, obj, pobj, vp)) return false; /* Update slotful shapes according to the value produced by the getter. */ - if (shapeRoot->hasSlot() && pobjRoot->nativeContains(cx, shapeRoot)) - pobjRoot->nativeSetSlot(shapeRoot->slot(), nvp); - - *vp = nvp; + if (shape->hasSlot() && pobj->nativeContains(cx, shape)) + pobj->nativeSetSlot(shape->slot(), vp); + return true; } JSBool -js_NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Shape *shape, - unsigned getHow, Value *vp) +js_NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Handle<Shape*> shape, + unsigned getHow, MutableHandle<Value> vp) { return js_NativeGetInline(cx, obj, obj, pobj, shape, getHow, vp); } JSBool js_NativeSet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> receiver, HandleShape shape, bool added, bool strict, Value *vp) { @@ -4353,17 +4356,17 @@ js_GetPropertyHelperInline(JSContext *cx ? Proxy::get(cx, obj2, receiver, id, vp) : JSObject::getGeneric(cx, obj2, obj2, id, vp); } if (getHow & JSGET_CACHE_RESULT) cx->propertyCache().fill(cx, obj, obj2, shape); /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */ - if (!js_NativeGetInline(cx, receiver, obj, obj2, shape, getHow, vp.address())) + if (!js_NativeGetInline(cx, receiver, obj, obj2, shape, getHow, vp)) return JS_FALSE; return JS_TRUE; } bool js::GetPropertyHelper(JSContext *cx, HandleObject obj, HandleId id, uint32_t getHow, MutableHandleValue vp) { @@ -4633,18 +4636,18 @@ baseops::SetPropertyHelper(JSContext *cx /* * Purge the property cache of now-shadowed id in obj's scope chain. * Do this early, before locking obj to avoid nesting locks. */ if (!js_PurgeScopeChain(cx, obj, id)) return JS_FALSE; - shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT, - attrs, flags, shortid); + shape = JSObject::putProperty(cx, obj, id, getter, setter, SHAPE_INVALID_SLOT, + attrs, flags, shortid); if (!shape) return JS_FALSE; /* * Initialize the new property value (passed to setter) to undefined. * Note that we store before calling addProperty, to match the order * in DefineNativeProperty. */ @@ -4804,17 +4807,17 @@ baseops::DeleteSpecial(JSContext *cx, Ha { Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid)); return baseops::DeleteGeneric(cx, obj, id, rval, strict); } bool js::HasDataProperty(JSContext *cx, HandleObject obj, jsid id, Value *vp) { - if (Shape *shape = obj->nativeLookup(cx, id)) { + if (UnrootedShape shape = obj->nativeLookup(cx, id)) { if (shape->hasDefaultGetter() && shape->hasSlot()) { *vp = obj->nativeGetSlot(shape->slot()); return true; } } return false; } @@ -5153,17 +5156,17 @@ js_ValueToNonNullObject(JSContext *cx, c void js_GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) { JS_ASSERT(trc->debugPrinter == js_GetObjectSlotName); JSObject *obj = (JSObject *)trc->debugPrintArg; uint32_t slot = uint32_t(trc->debugPrintIndex); - Shape *shape; + UnrootedShape shape; if (obj->isNative()) { shape = obj->lastProperty(); while (shape && (!shape->hasSlot() || shape->slot() != slot)) shape = shape->previous(); } else { shape = NULL; } @@ -5290,17 +5293,17 @@ js_DumpId(jsid id) } static void DumpProperty(JSObject *obj, Shape &shape) { jsid id = shape.propid(); uint8_t attrs = shape.attributes(); - fprintf(stderr, " ((Shape *) %p) ", (void *) &shape); + fprintf(stderr, " ((JSShape *) %p) ", (void *) &shape); if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate "); if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly "); if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent "); if (attrs & JSPROP_SHARED) fprintf(stderr, "shared "); if (shape.hasGetterValue()) fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject()); else if (!shape.hasDefaultGetter()) @@ -5391,17 +5394,17 @@ JSObject::dump() fprintf(stderr, "(reserved) "); fprintf(stderr, "= "); dumpValue(obj->getSlot(i)); fputc('\n', stderr); } if (obj->isNative()) { fprintf(stderr, "properties:\n"); - Vector<Shape *, 8, SystemAllocPolicy> props; + Vector<RawShape, 8, SystemAllocPolicy> props; for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) props.append(&r.front()); for (size_t i = props.length(); i-- != 0;) DumpProperty(obj, *props[i]); } fputc('\n', stderr); }
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -16,33 +16,32 @@ * values, called slots. The map/slot pointer pair is GC'ed, while the map * is reference counted and the slot vector is malloc'ed. */ #include "jsapi.h" #include "jsatom.h" #include "jsclass.h" #include "jsfriendapi.h" #include "jsinfer.h" -#include "jspubtd.h" -#include "jsprvtd.h" -#include "jslock.h" #include "gc/Barrier.h" #include "gc/Heap.h" #include "vm/ObjectImpl.h" #include "vm/String.h" namespace js { class AutoPropDescArrayRooter; class BaseProxyHandler; class CallObject; struct GCMarker; struct NativeIterator; +ForwardDeclare(Shape); +struct StackShape; namespace mjit { class Compiler; } inline JSObject * CastAsObject(PropertyOp op) { return JS_FUNC_TO_DATA_PTR(JSObject *, op); } @@ -275,20 +274,20 @@ struct JSObject : public js::ObjectImpl /* Make the type object to use for LAZY_TYPE objects. */ js::types::TypeObject *makeLazyType(JSContext *cx); public: /* * Update the last property, keeping the number of allocated slots in sync * with the object's new slot span. */ - static bool setLastProperty(JSContext *cx, js::HandleObject obj, js::Shape *shape); + static bool setLastProperty(JSContext *cx, JS::HandleObject obj, js::HandleShape shape); /* As above, but does not change the slot span. */ - inline void setLastPropertyInfallible(js::Shape *shape); + inline void setLastPropertyInfallible(js::UnrootedShape shape); /* Make a non-array object with the specified initial state. */ static inline JSObject *create(JSContext *cx, js::gc::AllocKind kind, js::HandleShape shape, js::HandleTypeObject type, js::HeapSlot *slots); @@ -721,17 +720,17 @@ struct JSObject : public js::ObjectImpl /* * Allocate and free an object slot. * * FIXME: bug 593129 -- slot allocation should be done by object methods * after calling object-parameter-free shape methods, avoiding coupling * logic across the object vs. shape module wall. */ - bool allocSlot(JSContext *cx, uint32_t *slotp); + static bool allocSlot(JSContext *cx, JS::HandleObject obj, uint32_t *slotp); void freeSlot(uint32_t slot); public: static bool reportReadOnly(JSContext *cx, jsid id, unsigned report = JSREPORT_ERROR); bool reportNotConfigurable(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR); bool reportNotExtensible(JSContext *cx, unsigned report = JSREPORT_ERROR); /* @@ -739,71 +738,78 @@ struct JSObject : public js::ObjectImpl * given arguments, providing this object as |this|. If the property isn't * callable a TypeError will be thrown. On success the value returned by * the call is stored in *vp. */ bool callMethod(JSContext *cx, js::HandleId id, unsigned argc, js::Value *argv, js::MutableHandleValue vp); private: - js::Shape *getChildProperty(JSContext *cx, js::Shape *parent, js::StackShape &child); + static js::UnrootedShape getChildProperty(JSContext *cx, JS::HandleObject obj, + js::HandleShape parent, js::StackShape &child); protected: /* * Internal helper that adds a shape not yet mapped by this object. * * Notes: * 1. getter and setter must be normalized based on flags (see jsscope.cpp). * 2. !isExtensible() checking must be done by callers. */ - js::Shape *addPropertyInternal(JSContext *cx, jsid id, - JSPropertyOp getter, JSStrictPropertyOp setter, - uint32_t slot, unsigned attrs, - unsigned flags, int shortid, js::Shape **spp, - bool allowDictionary); + static js::UnrootedShape addPropertyInternal(JSContext *cx, + JS::HandleObject obj, JS::HandleId id, + JSPropertyOp getter, JSStrictPropertyOp setter, + uint32_t slot, unsigned attrs, + unsigned flags, int shortid, js::Shape **spp, + bool allowDictionary); private: bool toDictionaryMode(JSContext *cx); struct TradeGutsReserved; static bool ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved); static void TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved); public: /* Add a property whose id is not yet in this scope. */ - js::Shape *addProperty(JSContext *cx, jsid id, - JSPropertyOp getter, JSStrictPropertyOp setter, - uint32_t slot, unsigned attrs, - unsigned flags, int shortid, bool allowDictionary = true); + static js::UnrootedShape addProperty(JSContext *cx, JS::HandleObject, JS::HandleId id, + JSPropertyOp getter, JSStrictPropertyOp setter, + uint32_t slot, unsigned attrs, unsigned flags, + int shortid, bool allowDictionary = true); /* Add a data property whose id is not yet in this scope. */ - js::Shape *addDataProperty(JSContext *cx, jsid id, uint32_t slot, unsigned attrs) { + js::UnrootedShape addDataProperty(JSContext *cx, jsid id_, uint32_t slot, unsigned attrs) { JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); - return addProperty(cx, id, NULL, NULL, slot, attrs, 0, 0); + js::RootedObject self(cx, this); + js::RootedId id(cx, id_); + return addProperty(cx, self, id, NULL, NULL, slot, attrs, 0, 0); } /* Add or overwrite a property for id in this scope. */ - js::Shape *putProperty(JSContext *cx, jsid id, - JSPropertyOp getter, JSStrictPropertyOp setter, - uint32_t slot, unsigned attrs, - unsigned flags, int shortid); - inline js::Shape * - putProperty(JSContext *cx, js::PropertyName *name, - JSPropertyOp getter, JSStrictPropertyOp setter, - uint32_t slot, unsigned attrs, unsigned flags, int shortid) { - return putProperty(cx, js::NameToId(name), getter, setter, slot, attrs, flags, shortid); + static js::UnrootedShape putProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, + JSPropertyOp getter, JSStrictPropertyOp setter, + uint32_t slot, unsigned attrs, + unsigned flags, int shortid); + static js::UnrootedShape putProperty(JSContext *cx, JS::HandleObject obj, + js::PropertyName *name, + JSPropertyOp getter, JSStrictPropertyOp setter, + uint32_t slot, unsigned attrs, + unsigned flags, int shortid) + { + js::RootedId id(cx, js::NameToId(name)); + return putProperty(cx, obj, id, getter, setter, slot, attrs, flags, shortid); } /* Change the given property into a sibling with the same id in this scope. */ - static js::Shape *changeProperty(JSContext *cx, js::HandleObject obj, - js::Shape *shape, unsigned attrs, unsigned mask, - JSPropertyOp getter, JSStrictPropertyOp setter); + static js::UnrootedShape changeProperty(JSContext *cx, js::HandleObject obj, + js::RawShape shape, unsigned attrs, unsigned mask, + JSPropertyOp getter, JSStrictPropertyOp setter); static inline bool changePropertyAttributes(JSContext *cx, js::HandleObject obj, js::Shape *shape, unsigned attrs); /* Remove the property named by id from this object. */ bool removeProperty(JSContext *cx, jsid id); /* Clear the scope, making it empty. */ @@ -1171,24 +1177,24 @@ js_CreateThisForFunction(JSContext *cx, // Generic call for constructing |this|. extern JSObject * js_CreateThis(JSContext *cx, js::Class *clasp, js::HandleObject callee); /* * Find or create a property named by id in obj's scope, with the given getter * and setter, slot, attributes, and other members. */ -extern js::Shape * -js_AddNativeProperty(JSContext *cx, js::HandleObject obj, jsid id, +extern js::UnrootedShape +js_AddNativeProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JSPropertyOp getter, JSStrictPropertyOp setter, uint32_t slot, unsigned attrs, unsigned flags, int shortid); extern JSBool -js_DefineOwnProperty(JSContext *cx, js::HandleObject obj, js::HandleId id, - const js::Value &descriptor, JSBool *bp); +js_DefineOwnProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, + const JS::Value &descriptor, JSBool *bp); namespace js { JSObject * CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, HandleObject parent); /* * Flags for the defineHow parameter of js_DefineNativeProperty. @@ -1198,22 +1204,22 @@ const unsigned DNP_DONT_PURGE = 2; / const unsigned DNP_UNQUALIFIED = 4; /* Unqualified property set. Only used in the defineHow argument of js_SetPropertyHelper. */ const unsigned DNP_SKIP_TYPE = 8; /* Don't update type information */ /* * Return successfully added or changed shape or NULL on error. */ -extern Shape * +extern bool DefineNativeProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs, unsigned flags, int shortid, unsigned defineHow = 0); -inline Shape * +inline bool DefineNativeProperty(JSContext *cx, HandleObject obj, PropertyName *name, HandleValue value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs, unsigned flags, int shortid, unsigned defineHow = 0) { Rooted<jsid> id(cx, NameToId(name)); return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, flags, shortid, defineHow); } @@ -1287,17 +1293,17 @@ const unsigned JSGET_CACHE_RESULT = 1; / /* * NB: js_NativeGet and js_NativeSet are called with the scope containing shape * (pobj's scope for Get, obj's for Set) locked, and on successful return, that * scope is again locked. But on failure, both functions return false with the * scope containing shape unlocked. */ extern JSBool js_NativeGet(JSContext *cx, js::Handle<JSObject*> obj, js::Handle<JSObject*> pobj, - js::Shape *shape, unsigned getHow, js::Value *vp); + js::Handle<js::Shape*> shape, unsigned getHow, js::MutableHandle<js::Value> vp); extern JSBool js_NativeSet(JSContext *cx, js::Handle<JSObject*> obj, js::Handle<JSObject*> receiver, js::Handle<js::Shape*> shape, bool added, bool strict, js::Value *vp); namespace js { bool
--- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -267,47 +267,48 @@ JSObject::isFixedSlot(size_t slot) inline size_t JSObject::dynamicSlotIndex(size_t slot) { JS_ASSERT(!isDenseArray() && slot >= numFixedSlots()); return slot - numFixedSlots(); } inline void -JSObject::setLastPropertyInfallible(js::Shape *shape) +JSObject::setLastPropertyInfallible(js::UnrootedShape shape) { JS_ASSERT(!shape->inDictionary()); JS_ASSERT(shape->compartment() == compartment()); JS_ASSERT(!inDictionaryMode()); JS_ASSERT(slotSpan() == shape->slotSpan()); JS_ASSERT(numFixedSlots() == shape->numFixedSlots()); shape_ = shape; } inline void JSObject::removeLastProperty(JSContext *cx) { JS_ASSERT(canRemoveLastProperty()); js::RootedObject self(cx, this); - JS_ALWAYS_TRUE(setLastProperty(cx, self, lastProperty()->previous())); + js::RootedShape prev(cx, lastProperty()->previous()); + JS_ALWAYS_TRUE(setLastProperty(cx, self, prev)); } inline bool JSObject::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 */ JS_ASSERT(!inDictionaryMode()); - js::Shape *previous = lastProperty()->previous(); + js::UnrootedShape previous = lastProperty()->previous().get(); return previous->getObjectParent() == lastProperty()->getObjectParent() && previous->getObjectFlags() == lastProperty()->getObjectFlags(); } inline const js::HeapSlot * JSObject::getRawSlots() { JS_ASSERT(isGlobal()); @@ -1517,17 +1518,18 @@ CopyInitializerObject(JSContext *cx, Han gc::AllocKind kind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots()); kind = gc::GetBackgroundAllocKind(kind); JS_ASSERT(kind == baseobj->getAllocKind()); RootedObject obj(cx, NewBuiltinClassInstance(cx, &ObjectClass, kind)); if (!obj) return NULL; - if (!JSObject::setLastProperty(cx, obj, baseobj->lastProperty())) + RootedShape lastProp(cx, baseobj->lastProperty()); + if (!JSObject::setLastProperty(cx, obj, lastProp)) return NULL; return obj; } JSObject * NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind kind, HandleShape shape); @@ -1553,17 +1555,17 @@ GuessArrayGCKind(size_t numSlots) return gc::FINALIZE_OBJECT8; } /* * Fill slots with the initial slot array to use for a newborn object which * may or may not need dynamic slots. */ inline bool -PreallocateObjectDynamicSlots(JSContext *cx, Shape *shape, HeapSlot **slots) +PreallocateObjectDynamicSlots(JSContext *cx, UnrootedShape shape, HeapSlot **slots) { if (size_t count = JSObject::dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan())) { *slots = cx->pod_malloc<HeapSlot>(count); if (!*slots) return false; Debug_SetSlotRangeToCrashOnTouch(*slots, count); return true; } @@ -1668,20 +1670,20 @@ js_InitClass(JSContext *cx, js::HandleOb /* * js_PurgeScopeChain does nothing if obj is not itself a prototype or parent * scope, else it reshapes the scope and prototype chains it links. It calls * js_PurgeScopeChainHelper, which asserts that obj is flagged as a delegate * (i.e., obj has ever been on a prototype or parent chain). */ extern bool -js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id); +js_PurgeScopeChainHelper(JSContext *cx, JS::HandleObject obj, JS::HandleId id); inline bool -js_PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id) +js_PurgeScopeChain(JSContext *cx, JS::HandleObject obj, JS::HandleId id) { if (obj->isDelegate()) return js_PurgeScopeChainHelper(cx, obj, id); return true; } inline void js::DestroyIdArray(FreeOp *fop, JSIdArray *ida)
--- a/js/src/jspropertycacheinlines.h +++ b/js/src/jspropertycacheinlines.h @@ -30,17 +30,17 @@ JS_ALWAYS_INLINE void js::PropertyCache::test(JSContext *cx, jsbytecode *pc, JSObject *&obj, JSObject *&pobj, PropertyCacheEntry *&entry, PropertyName *&name) { AutoAssertNoGC nogc; JS_ASSERT(this == &cx->propertyCache()); - Shape *kshape = obj->lastProperty(); + UnrootedShape kshape = obj->lastProperty(); entry = &table[hash(pc, kshape)]; PCMETER(pctestentry = entry); PCMETER(tests++); JS_ASSERT(&obj != &pobj); if (entry->kpc == pc && entry->kshape == kshape) { JSObject *tmp; pobj = obj; if (entry->isPrototypePropertyHit() && @@ -59,19 +59,21 @@ js::PropertyCache::test(JSContext *cx, j if (name) PCMETER(misses++); } JS_ALWAYS_INLINE bool js::PropertyCache::testForSet(JSContext *cx, jsbytecode *pc, JSObject *obj, PropertyCacheEntry **entryp, JSObject **obj2p, PropertyName **namep) { + AutoAssertNoGC nogc; + JS_ASSERT(this == &cx->propertyCache()); - Shape *kshape = obj->lastProperty(); + UnrootedShape kshape = obj->lastProperty(); PropertyCacheEntry *entry = &table[hash(pc, kshape)]; *entryp = entry; PCMETER(pctestentry = entry); PCMETER(tests++); PCMETER(settests++); if (entry->kpc == pc && entry->kshape == kshape) return true;
--- a/js/src/jspropertytree.cpp +++ b/js/src/jspropertytree.cpp @@ -25,60 +25,58 @@ ShapeHasher::hash(const Lookup &l) } inline bool ShapeHasher::match(const Key k, const Lookup &l) { return k->matches(l); } -Shape * +UnrootedShape PropertyTree::newShape(JSContext *cx) { - Shape *shape = js_NewGCShape(cx); - if (!shape) { + UnrootedShape shape = js_NewGCShape(cx); + if (!shape) JS_ReportOutOfMemory(cx); - return NULL; - } return shape; } static KidsHash * -HashChildren(Shape *kid1, Shape *kid2) +HashChildren(UnrootedShape kid1, UnrootedShape kid2) { KidsHash *hash = js_new<KidsHash>(); if (!hash || !hash->init(2)) { js_delete(hash); return NULL; } JS_ALWAYS_TRUE(hash->putNew(kid1, kid1)); JS_ALWAYS_TRUE(hash->putNew(kid2, kid2)); return hash; } bool -PropertyTree::insertChild(JSContext *cx, Shape *parent, Shape *child) +PropertyTree::insertChild(JSContext *cx, UnrootedShape parent, UnrootedShape child) { JS_ASSERT(!parent->inDictionary()); JS_ASSERT(!child->parent); JS_ASSERT(!child->inDictionary()); JS_ASSERT(cx->compartment == compartment); JS_ASSERT(child->compartment() == parent->compartment()); KidsPointer *kidp = &parent->kids; if (kidp->isNull()) { child->setParent(parent); kidp->setShape(child); return true; } if (kidp->isShape()) { - Shape *shape = kidp->toShape(); + UnrootedShape shape = kidp->toShape(); JS_ASSERT(shape != child); JS_ASSERT(!shape->matches(child)); KidsHash *hash = HashChildren(shape, child); if (!hash) { JS_ReportOutOfMemory(cx); return false; } @@ -92,17 +90,17 @@ PropertyTree::insertChild(JSContext *cx, return false; } child->setParent(parent); return true; } void -Shape::removeChild(Shape *child) +Shape::removeChild(UnrootedShape child) { JS_ASSERT(!child->inDictionary()); JS_ASSERT(child->parent == this); KidsPointer *kidp = &kids; if (kidp->isShape()) { JS_ASSERT(kidp->toShape() == child); @@ -122,84 +120,86 @@ Shape::removeChild(Shape *child) KidsHash::Range r = hash->all(); Shape *otherChild = r.front(); JS_ASSERT((r.popFront(), r.empty())); /* No more elements! */ kidp->setShape(otherChild); js_delete(hash); } } -Shape * +UnrootedShape PropertyTree::getChild(JSContext *cx, Shape *parent_, uint32_t nfixed, const StackShape &child) { AssertCanGC(); - Shape *shape = NULL; + { + UnrootedShape shape = NULL; - JS_ASSERT(parent_); + JS_ASSERT(parent_); - /* - * The property tree has extremely low fan-out below its root in - * popular embeddings with real-world workloads. Patterns such as - * defining closures that capture a constructor's environment as - * getters or setters on the new object that is passed in as - * |this| can significantly increase fan-out below the property - * tree root -- see bug 335700 for details. - */ - KidsPointer *kidp = &parent_->kids; - if (kidp->isShape()) { - Shape *kid = kidp->toShape(); - if (kid->matches(child)) - shape = kid; - } else if (kidp->isHash()) { - if (KidsHash::Ptr p = kidp->toHash()->lookup(child)) - shape = *p; - } else { - /* If kidp->isNull(), we always insert. */ - } + /* + * The property tree has extremely low fan-out below its root in + * popular embeddings with real-world workloads. Patterns such as + * defining closures that capture a constructor's environment as + * getters or setters on the new object that is passed in as + * |this| can significantly increase fan-out below the property + * tree root -- see bug 335700 for details. + */ + KidsPointer *kidp = &parent_->kids; + if (kidp->isShape()) { + UnrootedShape kid = kidp->toShape(); + if (kid->matches(child)) + shape = kid; + } else if (kidp->isHash()) { + if (KidsHash::Ptr p = kidp->toHash()->lookup(child)) + shape = *p; + } else { + /* If kidp->isNull(), we always insert. */ + } #ifdef JSGC_INCREMENTAL - if (shape) { - JSCompartment *comp = shape->compartment(); - if (comp->needsBarrier()) { - /* - * We need a read barrier for the shape tree, since these are weak - * pointers. - */ - Shape *tmp = shape; - MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier"); - JS_ASSERT(tmp == shape); - } else if (comp->isGCSweeping() && !shape->isMarked() && - !shape->arenaHeader()->allocatedDuringIncremental) - { - /* - * The shape we've found is unreachable and due to be finalized, so - * remove our weak reference to it and don't use it. - */ - JS_ASSERT(parent_->isMarked()); - parent_->removeChild(shape); - shape = NULL; + if (shape) { + JSCompartment *comp = shape->compartment(); + if (comp->needsBarrier()) { + /* + * We need a read barrier for the shape tree, since these are weak + * pointers. + */ + Shape *tmp = shape; + MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier"); + JS_ASSERT(tmp == shape); + } else if (comp->isGCSweeping() && !shape->isMarked() && + !shape->arenaHeader()->allocatedDuringIncremental) + { + /* + * The shape we've found is unreachable and due to be finalized, so + * remove our weak reference to it and don't use it. + */ + JS_ASSERT(parent_->isMarked()); + parent_->removeChild(shape); + shape = NULL; + } } - } #endif - if (shape) - return shape; + if (shape) + return shape; + } StackShape::AutoRooter childRoot(cx, &child); RootedShape parent(cx, parent_); - shape = newShape(cx); + UnrootedShape shape = newShape(cx); if (!shape) - return NULL; + return UnrootedShape(NULL); new (shape) Shape(child, nfixed); if (!insertChild(cx, parent, shape)) - return NULL; + return UnrootedShape(NULL); return shape; } void Shape::finalize(FreeOp *fop) { if (!inDictionary()) { @@ -231,17 +231,17 @@ Shape::finalize(FreeOp *fop) if (kids.isHash()) fop->delete_(kids.toHash()); } } #ifdef DEBUG void -KidsPointer::checkConsistency(Shape *aKid) const +KidsPointer::checkConsistency(UnrootedShape aKid) const { if (isShape()) { JS_ASSERT(toShape() == aKid); } else { JS_ASSERT(isHash()); KidsHash *hash = toHash(); KidsHash::Ptr ptr = hash->lookup(aKid); JS_ASSERT(*ptr == aKid); @@ -318,23 +318,23 @@ Shape::dumpSubtree(JSContext *cx, int le } else { fprintf(fp, "%*sid ", level, ""); dump(cx, fp); } if (!kids.isNull()) { ++level; if (kids.isShape()) { - Shape *kid = kids.toShape(); + UnrootedShape kid = kids.toShape(); JS_ASSERT(kid->parent == this); kid->dumpSubtree(cx, level, fp); } else { const KidsHash &hash = *kids.toHash(); for (KidsHash::Range range = hash.all(); !range.empty(); range.popFront()) { - Shape *kid = range.front(); + RawShape kid = range.front(); JS_ASSERT(kid->parent == this); kid->dumpSubtree(cx, level, fp); } } } } @@ -358,16 +358,16 @@ js::PropertyTree::dumpShapes(JSRuntime * for (gc::GCCompartmentsIter c(rt); !c.done(); c.next()) { fprintf(dumpfp, "*** Compartment %p ***\n", (void *)c.get()); /* typedef JSCompartment::EmptyShapeSet HS; HS &h = c->emptyShapes; for (HS::Range r = h.all(); !r.empty(); r.popFront()) { - Shape *empty = r.front(); + RawShape empty = r.front(); empty->dumpSubtree(rt, 0, dumpfp); putc('\n', dumpfp); } */ } } #endif
--- a/js/src/jspropertytree.h +++ b/js/src/jspropertytree.h @@ -2,95 +2,95 @@ /* vim: set ts=4 sw=4 et tw=99: */ /* 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 jspropertytree_h___ #define jspropertytree_h___ -#include "jsprvtd.h" - #include "js/HashTable.h" namespace js { +ForwardDeclare(Shape); +struct StackShape; + struct ShapeHasher { - typedef js::Shape *Key; - typedef js::StackShape Lookup; + typedef RawShape Key; + typedef StackShape Lookup; static inline HashNumber hash(const Lookup &l); static inline bool match(Key k, const Lookup &l); }; -typedef HashSet<js::Shape *, ShapeHasher, SystemAllocPolicy> KidsHash; +typedef HashSet<RawShape, ShapeHasher, SystemAllocPolicy> KidsHash; class KidsPointer { private: enum { SHAPE = 0, HASH = 1, TAG = 1 }; uintptr_t w; public: bool isNull() const { return !w; } void setNull() { w = 0; } bool isShape() const { return (w & TAG) == SHAPE && !isNull(); } - js::Shape *toShape() const { + UnrootedShape toShape() const { JS_ASSERT(isShape()); - return reinterpret_cast<js::Shape *>(w & ~uintptr_t(TAG)); + return reinterpret_cast<RawShape>(w & ~uintptr_t(TAG)); } - void setShape(js::Shape *shape) { + void setShape(UnrootedShape shape) { JS_ASSERT(shape); - JS_ASSERT((reinterpret_cast<uintptr_t>(shape) & TAG) == 0); - w = reinterpret_cast<uintptr_t>(shape) | SHAPE; + JS_ASSERT((reinterpret_cast<uintptr_t>(static_cast<RawShape>(shape)) & TAG) == 0); + w = reinterpret_cast<uintptr_t>(static_cast<RawShape>(shape)) | SHAPE; } bool isHash() const { return (w & TAG) == HASH; } KidsHash *toHash() const { JS_ASSERT(isHash()); return reinterpret_cast<KidsHash *>(w & ~uintptr_t(TAG)); } void setHash(KidsHash *hash) { JS_ASSERT(hash); JS_ASSERT((reinterpret_cast<uintptr_t>(hash) & TAG) == 0); w = reinterpret_cast<uintptr_t>(hash) | HASH; } #ifdef DEBUG - void checkConsistency(js::Shape *aKid) const; + void checkConsistency(UnrootedShape aKid) const; #endif }; class PropertyTree { friend struct ::JSFunction; JSCompartment *compartment; - bool insertChild(JSContext *cx, js::Shape *parent, js::Shape *child); + bool insertChild(JSContext *cx, UnrootedShape parent, UnrootedShape child); PropertyTree(); public: enum { MAX_HEIGHT = 128 }; PropertyTree(JSCompartment *comp) : compartment(comp) { } - js::Shape *newShape(JSContext *cx); - js::Shape *getChild(JSContext *cx, Shape *parent, uint32_t nfixed, const StackShape &child); + UnrootedShape newShape(JSContext *cx); + UnrootedShape getChild(JSContext *cx, Shape *parent, uint32_t nfixed, const StackShape &child); #ifdef DEBUG static void dumpShapes(JSRuntime *rt); - static void meter(JSBasicStats *bs, js::Shape *node); #endif }; } /* namespace js */ #endif /* jspropertytree_h___ */
--- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -137,25 +137,17 @@ class GlobalObject; template <typename K, typename V, size_t InlineElems> class InlineMap; class LifoAlloc; -class BaseShape; -class UnownedBaseShape; struct Shape; -struct EmptyShape; -class ShapeKindArray; -class Bindings; - -struct StackBaseShape; -struct StackShape; class Breakpoint; class BreakpointSite; class Debugger; class WatchpointMap; /* * Env is the type of what ES5 calls "lexical environments" (runtime @@ -202,28 +194,26 @@ namespace types { class TypeSet; struct TypeCallsite; struct TypeObject; struct TypeCompartment; } /* namespace types */ typedef JS::Handle<Shape*> HandleShape; -typedef JS::Handle<BaseShape*> HandleBaseShape; typedef JS::Handle<types::TypeObject*> HandleTypeObject; typedef JS::Handle<JSAtom*> HandleAtom; typedef JS::Handle<PropertyName*> HandlePropertyName; typedef JS::MutableHandle<Shape*> MutableHandleShape; typedef JS::MutableHandle<JSAtom*> MutableHandleAtom; typedef JSAtom * RawAtom; typedef js::Rooted<Shape*> RootedShape; -typedef js::Rooted<BaseShape*> RootedBaseShape; typedef js::Rooted<types::TypeObject*> RootedTypeObject; typedef js::Rooted<JSAtom*> RootedAtom; typedef js::Rooted<PropertyName*> RootedPropertyName; enum XDRMode { XDR_ENCODE, XDR_DECODE };
--- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -32,17 +32,17 @@ #include "jsscopeinlines.h" using namespace js; using namespace js::gc; using mozilla::DebugOnly; bool -ShapeTable::init(JSRuntime *rt, Shape *lastProp) +ShapeTable::init(JSRuntime *rt, UnrootedShape lastProp) { /* * Either we're creating a table for a large scope that was populated * via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or * JSOP_SETPROP; or else calloc failed at least once already. In any * event, let's try to grow, overallocating to hold at least twice the * current population. */ @@ -87,17 +87,17 @@ Shape::makeOwnBaseShape(JSContext *cx, H nbase->setOwned(shape->base()->toUnowned()); shape->base_ = nbase; return true; } void -Shape::handoffTableTo(Shape *shape) +Shape::handoffTableTo(UnrootedShape shape) { AutoAssertNoGC nogc; JS_ASSERT(inDictionary() && shape->inDictionary()); if (this == shape) return; JS_ASSERT(base()->isOwned() && !shape->base()->isOwned()); @@ -268,110 +268,106 @@ ShapeTable::grow(JSContext *cx) if (!change(delta, cx) && entryCount + removedCount == size - 1) { JS_ReportOutOfMemory(cx); return false; } return true; } -Shape * +UnrootedShape Shape::getChildBinding(JSContext *cx, const StackShape &child) { AssertCanGC(); JS_ASSERT(!inDictionary()); /* Try to allocate all slots inline. */ uint32_t slots = child.slotSpan(); gc::AllocKind kind = gc::GetGCObjectKind(slots); uint32_t nfixed = gc::GetGCKindSlots(kind); return cx->propertyTree().getChild(cx, this, nfixed, child); } -/* static */ Shape * +/* static */ UnrootedShape Shape::replaceLastProperty(JSContext *cx, const StackBaseShape &base, - TaggedProto proto, Shape *shape_) + TaggedProto proto, HandleShape shape) { - RootedShape shape(cx, shape_); - JS_ASSERT(!shape->inDictionary()); if (!shape->parent) { /* Treat as resetting the initial property of the shape hierarchy. */ AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); return EmptyShape::getInitialShape(cx, base.clasp, proto, base.parent, kind, base.flags & BaseShape::OBJECT_FLAG_MASK); } StackShape child(shape); { UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base); if (!nbase) - return NULL; + return UnrootedShape(NULL); child.base = nbase; } return cx->propertyTree().getChild(cx, shape->parent, shape->numFixedSlots(), child); } /* * Get or create a property-tree or dictionary child property of |parent|, * which must be lastProperty() if inDictionaryMode(), else parent must be * one of lastProperty() or lastProperty()->parent. */ -Shape * -JSObject::getChildProperty(JSContext *cx, Shape *parent, StackShape &child) +/* static */ UnrootedShape +JSObject::getChildProperty(JSContext *cx, HandleObject obj, HandleShape parent, StackShape &child) { /* * Shared properties have no slot, but slot_ will reflect that of parent. * Unshared properties allocate a slot here but may lose it due to a * JS_ClearScope call. */ if (!child.hasSlot()) { child.setSlot(parent->maybeSlot()); } else { if (child.hasMissingSlot()) { uint32_t slot; - if (!allocSlot(cx, &slot)) - return NULL; + if (!allocSlot(cx, obj, &slot)) + return UnrootedShape(NULL); child.setSlot(slot); } else { /* Slots can only be allocated out of order on objects in dictionary mode. */ - JS_ASSERT(inDictionaryMode() || + JS_ASSERT(obj->inDictionaryMode() || parent->hasMissingSlot() || child.slot() == parent->maybeSlot() + 1); } } - Shape *shape; + RootedShape shape(cx); - RootedObject self(cx, this); - - if (inDictionaryMode()) { - JS_ASSERT(parent == lastProperty()); + if (obj->inDictionaryMode()) { + JS_ASSERT(parent == obj->lastProperty()); StackShape::AutoRooter childRoot(cx, &child); shape = js_NewGCShape(cx); if (!shape) - return NULL; - if (child.hasSlot() && child.slot() >= self->lastProperty()->base()->slotSpan()) { - if (!JSObject::setSlotSpan(cx, self, child.slot() + 1)) - return NULL; + return UnrootedShape(NULL); + if (child.hasSlot() && child.slot() >= obj->lastProperty()->base()->slotSpan()) { + if (!JSObject::setSlotSpan(cx, obj, child.slot() + 1)) + return UnrootedShape(NULL); } - shape->initDictionaryShape(child, self->numFixedSlots(), &self->shape_); + shape->initDictionaryShape(child, obj->numFixedSlots(), &obj->shape_); } else { - shape = cx->propertyTree().getChild(cx, parent, self->numFixedSlots(), child); + shape = cx->propertyTree().getChild(cx, parent, obj->numFixedSlots(), child); if (!shape) - return NULL; + return UnrootedShape(NULL); //JS_ASSERT(shape->parent == parent); //JS_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent); - if (!JSObject::setLastProperty(cx, self, shape)) - return NULL; + if (!JSObject::setLastProperty(cx, obj, shape)) + return UnrootedShape(NULL); } return shape; } bool JSObject::toDictionaryMode(JSContext *cx) { @@ -392,17 +388,17 @@ JSObject::toDictionaryMode(JSContext *cx */ RootedShape root(cx); RootedShape dictionaryShape(cx); RootedShape shape(cx, lastProperty()); while (shape) { JS_ASSERT(!shape->inDictionary()); - Shape *dprop = js_NewGCShape(cx); + UnrootedShape dprop = js_NewGCShape(cx); if (!dprop) { js_ReportOutOfMemory(cx); return false; } HeapPtrShape *listp = dictionaryShape ? &dictionaryShape->parent : (HeapPtrShape *) root.address(); @@ -447,127 +443,121 @@ NormalizeGetterAndSetter(JSObject *obj, if (getter == JS_PropertyStub) { JS_ASSERT(!(attrs & JSPROP_GETTER)); getter = NULL; } return true; } -Shape * -JSObject::addProperty(JSContext *cx, jsid id, +/* static */ UnrootedShape +JSObject::addProperty(JSContext *cx, HandleObject obj, HandleId id, PropertyOp getter, StrictPropertyOp setter, uint32_t slot, unsigned attrs, unsigned flags, int shortid, bool allowDictionary) { JS_ASSERT(!JSID_IS_VOID(id)); - if (!isExtensible()) { - reportNotExtensible(cx); - return NULL; + if (!obj->isExtensible()) { + obj->reportNotExtensible(cx); + return UnrootedShape(NULL); } - NormalizeGetterAndSetter(this, id, attrs, flags, getter, setter); - - RootedObject self(cx, this); + NormalizeGetterAndSetter(obj, id, attrs, flags, getter, setter); Shape **spp = NULL; - if (inDictionaryMode()) - spp = lastProperty()->table().search(id, true); + if (obj->inDictionaryMode()) + spp = obj->lastProperty()->table().search(id, true); - return self->addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, - spp, allowDictionary); + return addPropertyInternal(cx, obj, id, getter, setter, slot, attrs, flags, shortid, + spp, allowDictionary); } -Shape * -JSObject::addPropertyInternal(JSContext *cx, jsid id_, +/* static */ UnrootedShape +JSObject::addPropertyInternal(JSContext *cx, HandleObject obj, HandleId id, PropertyOp getter, StrictPropertyOp setter, uint32_t slot, unsigned attrs, unsigned flags, int shortid, Shape **spp, bool allowDictionary) { AssertCanGC(); - JS_ASSERT_IF(!allowDictionary, !inDictionaryMode()); - - RootedId id(cx, id_); - RootedObject self(cx, this); + JS_ASSERT_IF(!allowDictionary, !obj->inDictionaryMode()); AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); ShapeTable *table = NULL; - if (!inDictionaryMode()) { + if (!obj->inDictionaryMode()) { bool stableSlot = (slot == SHAPE_INVALID_SLOT) || - lastProperty()->hasMissingSlot() || - (slot == lastProperty()->maybeSlot() + 1); + obj->lastProperty()->hasMissingSlot() || + (slot == obj->lastProperty()->maybeSlot() + 1); JS_ASSERT_IF(!allowDictionary, stableSlot); if (allowDictionary && - (!stableSlot || lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT)) { - if (!toDictionaryMode(cx)) - return NULL; - table = &self->lastProperty()->table(); + (!stableSlot || obj->lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT)) { + if (!obj->toDictionaryMode(cx)) + return UnrootedShape(NULL); + table = &obj->lastProperty()->table(); spp = table->search(id, true); } } else { - table = &lastProperty()->table(); + table = &obj->lastProperty()->table(); if (table->needsToGrow()) { if (!table->grow(cx)) - return NULL; + return UnrootedShape(NULL); spp = table->search(id, true); JS_ASSERT(!SHAPE_FETCH(spp)); } } JS_ASSERT(!!table == !!spp); /* Find or create a property tree node labeled by our arguments. */ - Shape *shape; + UnrootedShape shape; { - shape = self->lastProperty(); + RootedShape last(cx, obj->lastProperty()); uint32_t index; bool indexed = js_IdIsIndex(id, &index); UnrootedUnownedBaseShape nbase; - if (shape->base()->matchesGetterSetter(getter, setter) && !indexed) { - nbase = shape->base()->unowned(); + if (last->base()->matchesGetterSetter(getter, setter) && !indexed) { + nbase = last->base()->unowned(); } else { - StackBaseShape base(shape->base()); + StackBaseShape base(last->base()); base.updateGetterSetter(attrs, getter, setter); if (indexed) base.flags |= BaseShape::INDEXED; nbase = BaseShape::getUnowned(cx, base); if (!nbase) - return NULL; + return UnrootedShape(NULL); } - StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid); - DropUnrooted(nbase); - shape = self->getChildProperty(cx, self->lastProperty(), child); + StackShape child(DropUnrooted(nbase), id, slot, obj->numFixedSlots(), attrs, flags, shortid); + shape = getChildProperty(cx, obj, last, child); } if (shape) { - JS_ASSERT(shape == self->lastProperty()); + JS_ASSERT(shape == obj->lastProperty()); if (table) { /* Store the tree node pointer in the table entry for id. */ - SHAPE_STORE_PRESERVING_COLLISION(spp, shape); + SHAPE_STORE_PRESERVING_COLLISION(spp, static_cast<RawShape>(shape)); ++table->entryCount; /* Pass the table along to the new last property, namely shape. */ JS_ASSERT(&shape->parent->table() == table); shape->parent->handoffTableTo(shape); } - self->checkShapeConsistency(); + obj->checkShapeConsistency(); return shape; } - self->checkShapeConsistency(); - return NULL; + obj->checkShapeConsistency(); + return UnrootedShape(NULL); } /* * Check and adjust the new attributes for the shape to make sure that our * slot access optimizations are sound. It is responsibility of the callers to * enforce all restrictions from ECMA-262 v5 8.12.9 [[DefineOwnProperty]]. */ inline bool @@ -584,171 +574,170 @@ CheckCanChangeAttrs(JSContext *cx, JSObj (*attrsp & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED))) { obj->reportNotConfigurable(cx, shape->propid()); return false; } return true; } -Shape * -JSObject::putProperty(JSContext *cx, jsid id_, +/* static */ UnrootedShape +JSObject::putProperty(JSContext *cx, HandleObject obj, HandleId id, PropertyOp getter, StrictPropertyOp setter, uint32_t slot, unsigned attrs, unsigned flags, int shortid) { - RootedId id(cx, id_); JS_ASSERT(!JSID_IS_VOID(id)); - NormalizeGetterAndSetter(this, id, attrs, flags, getter, setter); + NormalizeGetterAndSetter(obj, id, attrs, flags, getter, setter); - RootedObject self(cx, this); AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); /* Search for id in order to claim its entry if table has been allocated. */ Shape **spp; - RootedShape shape(cx, Shape::search(cx, lastProperty(), id, &spp, true)); + RootedShape shape(cx, Shape::search(cx, obj->lastProperty(), id, &spp, true)); if (!shape) { /* * You can't add properties to a non-extensible object, but you can change * attributes of properties in such objects. */ - if (!self->isExtensible()) { - self->reportNotExtensible(cx); - return NULL; + if (!obj->isExtensible()) { + obj->reportNotExtensible(cx); + return UnrootedShape(NULL); } - return self->addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp, true); + return addPropertyInternal(cx, obj, id, getter, setter, slot, attrs, flags, shortid, spp, true); } /* Property exists: search must have returned a valid *spp. */ JS_ASSERT_IF(spp, !SHAPE_IS_REMOVED(*spp)); - if (!CheckCanChangeAttrs(cx, self, shape, &attrs)) - return NULL; + if (!CheckCanChangeAttrs(cx, obj, shape, &attrs)) + return UnrootedShape(NULL); /* * If the caller wants to allocate a slot, but doesn't care which slot, * copy the existing shape's slot into slot so we can match shape, if all * other members match. */ bool hadSlot = shape->hasSlot(); uint32_t oldSlot = shape->maybeSlot(); if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot) slot = oldSlot; Rooted<UnownedBaseShape*> nbase(cx); { uint32_t index; bool indexed = js_IdIsIndex(id, &index); - StackBaseShape base(self->lastProperty()->base()); + StackBaseShape base(obj->lastProperty()->base()); base.updateGetterSetter(attrs, getter, setter); if (indexed) base.flags |= BaseShape::INDEXED; nbase = BaseShape::getUnowned(cx, base); if (!nbase) - return NULL; + return UnrootedShape(NULL); } /* * Now that we've possibly preserved slot, check whether all members match. * If so, this is a redundant "put" and we can return without more work. */ if (shape->matchesParamsAfterId(nbase, slot, attrs, flags, shortid)) return shape; /* * Overwriting a non-last property requires switching to dictionary mode. * The shape tree is shared immutable, and we can't removeProperty and then * addPropertyInternal because a failure under add would lose data. */ - if (shape != self->lastProperty() && !self->inDictionaryMode()) { - if (!self->toDictionaryMode(cx)) - return NULL; - spp = self->lastProperty()->table().search(shape->propid(), false); + if (shape != obj->lastProperty() && !obj->inDictionaryMode()) { + if (!obj->toDictionaryMode(cx)) + return UnrootedShape(NULL); + spp = obj->lastProperty()->table().search(shape->propid(), false); shape = SHAPE_FETCH(spp); } JS_ASSERT_IF(shape->hasSlot() && !(attrs & JSPROP_SHARED), shape->slot() == slot); - if (self->inDictionaryMode()) { + if (obj->inDictionaryMode()) { /* * Updating some property in a dictionary-mode object. Create a new * shape for the existing property, and also generate a new shape for * the last property of the dictionary (unless the modified property * is also the last property). */ - bool updateLast = (shape == self->lastProperty()); - shape = self->replaceWithNewEquivalentShape(cx, shape); + bool updateLast = (shape == obj->lastProperty()); + shape = obj->replaceWithNewEquivalentShape(cx, shape); if (!shape) - return NULL; - if (!updateLast && !self->generateOwnShape(cx)) - return NULL; + return UnrootedShape(NULL); + if (!updateLast && !obj->generateOwnShape(cx)) + return UnrootedShape(NULL); /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */ if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED)) { - if (!self->allocSlot(cx, &slot)) - return NULL; + if (!allocSlot(cx, obj, &slot)) + return UnrootedShape(NULL); } if (updateLast) shape->base()->adoptUnowned(nbase); else shape->base_ = nbase; shape->setSlot(slot); shape->attrs = uint8_t(attrs); shape->flags = flags | Shape::IN_DICTIONARY; shape->shortid_ = int16_t(shortid); } else { /* * Updating the last property in a non-dictionary-mode object. Find an * alternate shared child of the last property's previous shape. */ - StackBaseShape base(self->lastProperty()->base()); + StackBaseShape base(obj->lastProperty()->base()); base.updateGetterSetter(attrs, getter, setter); UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base); if (!nbase) - return NULL; + return UnrootedShape(NULL); - JS_ASSERT(shape == self->lastProperty()); + JS_ASSERT(shape == obj->lastProperty()); /* Find or create a property tree node labeled by our arguments. */ - StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid); + StackShape child(nbase, id, slot, obj->numFixedSlots(), attrs, flags, shortid); DropUnrooted(nbase); - Shape *newShape = self->getChildProperty(cx, shape->parent, child); + RootedShape parent(cx, shape->parent); + UnrootedShape newShape = JSObject::getChildProperty(cx, obj, parent, child); if (!newShape) { - self->checkShapeConsistency(); - return NULL; + obj->checkShapeConsistency(); + return UnrootedShape(NULL); } shape = newShape; } /* * Can't fail now, so free the previous incarnation's slot if the new shape * has no slot. But we do not need to free oldSlot (and must not, as trying * to will botch an assertion in JSObject::freeSlot) if the new last * property (shape here) has a slotSpan that does not cover it. */ if (hadSlot && !shape->hasSlot()) { - if (oldSlot < self->slotSpan()) - self->freeSlot(oldSlot); + if (oldSlot < obj->slotSpan()) + obj->freeSlot(oldSlot); ++cx->runtime->propertyRemovals; } - self->checkShapeConsistency(); + obj->checkShapeConsistency(); return shape; } -/* static */ Shape * -JSObject::changeProperty(JSContext *cx, HandleObject obj, Shape *shape, unsigned attrs, unsigned mask, +/* static */ UnrootedShape +JSObject::changeProperty(JSContext *cx, HandleObject obj, RawShape shape, unsigned attrs, unsigned mask, PropertyOp getter, StrictPropertyOp setter) { JS_ASSERT(obj->nativeContainsNoAllocation(*shape)); attrs |= shape->attrs & mask; /* Allow only shared (slotless) => unshared (slotful) transition. */ JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) || @@ -759,29 +748,30 @@ JSObject::changeProperty(JSContext *cx, types::AddTypePropertyId(cx, obj, shape->propid(), types::Type::UnknownType()); if (getter == JS_PropertyStub) getter = NULL; if (setter == JS_StrictPropertyStub) setter = NULL; if (!CheckCanChangeAttrs(cx, obj, shape, &attrs)) - return NULL; + return UnrootedShape(NULL); if (shape->attrs == attrs && shape->getter() == getter && shape->setter() == setter) return shape; /* * Let JSObject::putProperty handle this |overwriting| case, including * the conservation of shape->slot (if it's valid). We must not call * removeProperty because it will free an allocated shape->slot, and * putProperty won't re-allocate it. */ - Shape *newShape = obj->putProperty(cx, shape->propid(), getter, setter, shape->maybeSlot(), - attrs, shape->flags, shape->maybeShortid()); + RootedId propid(cx, shape->propid()); + UnrootedShape newShape = putProperty(cx, obj, propid, getter, setter, shape->maybeSlot(), + attrs, shape->flags, shape->maybeShortid()); obj->checkShapeConsistency(); return newShape; } bool JSObject::removeProperty(JSContext *cx, jsid id_) { @@ -857,28 +847,30 @@ JSObject::removeProperty(JSContext *cx, --table.entryCount; #ifdef DEBUG /* * Check the consistency of the table but limit the number of * checks not to alter significantly the complexity of the * delete in debug builds, see bug 534493. */ - Shape *aprop = self->lastProperty(); + UnrootedShape aprop = self->lastProperty(); for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent) JS_ASSERT_IF(aprop != shape, self->nativeContainsNoAllocation(*aprop)); #endif } - /* Remove shape from its non-circular doubly linked list. */ - Shape *oldLastProp = self->lastProperty(); - shape->removeFromDictionary(self); + { + /* Remove shape from its non-circular doubly linked list. */ + UnrootedShape oldLastProp = self->lastProperty(); + shape->removeFromDictionary(self); - /* Hand off table from the old to new last property. */ - oldLastProp->handoffTableTo(self->lastProperty()); + /* Hand off table from the old to new last property. */ + oldLastProp->handoffTableTo(self->lastProperty()); + } /* Generate a new shape for the object, infallibly. */ JS_ALWAYS_TRUE(self->generateOwnShape(cx, spare)); /* Consider shrinking table if its load factor is <= .25. */ uint32_t size = table.capacity(); if (size > ShapeTable::MIN_SIZE && table.entryCount <= size >> 2) (void) table.change(-1, cx); @@ -895,17 +887,17 @@ JSObject::removeProperty(JSContext *cx, self->checkShapeConsistency(); return true; } /* static */ void JSObject::clear(JSContext *cx, HandleObject obj) { - Shape *shape = obj->lastProperty(); + RootedShape shape(cx, obj->lastProperty()); JS_ASSERT(obj->inDictionaryMode() == shape->inDictionary()); while (shape->parent) { shape = shape->parent; JS_ASSERT(obj->inDictionaryMode() == shape->inDictionary()); } JS_ASSERT(shape->isEmptyShape()); @@ -931,16 +923,17 @@ JSObject::rollbackProperties(JSContext * JS_ASSERT(lastProperty()->hasSlot() && getSlot(lastProperty()->slot()).isUndefined()); removeLastProperty(cx); } } Shape * JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *newShape) { + AssertCanGC(); JS_ASSERT(cx->compartment == oldShape->compartment()); JS_ASSERT_IF(oldShape != lastProperty(), inDictionaryMode() && nativeLookupNoAllocation(oldShape->propidRef()) == oldShape); JSObject *self = this; if (!inDictionaryMode()) { @@ -1011,34 +1004,35 @@ JSObject::setParent(JSContext *cx, Handl UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base); if (!nbase) return false; obj->lastProperty()->base()->adoptUnowned(nbase); return true; } - Shape *newShape = Shape::setObjectParent(cx, parent, obj->getTaggedProto(), obj->shape_); + UnrootedShape newShape = Shape::setObjectParent(cx, parent, obj->getTaggedProto(), obj->shape_); if (!newShape) return false; obj->shape_ = newShape; return true; } -/* static */ Shape * +/* static */ UnrootedShape Shape::setObjectParent(JSContext *cx, JSObject *parent, TaggedProto proto, Shape *last) { if (last->getObjectParent() == parent) return last; StackBaseShape base(last); base.parent = parent; - return replaceLastProperty(cx, base, proto, last); + RootedShape lastRoot(cx, last); + return replaceLastProperty(cx, base, proto, lastRoot); } bool JSObject::preventExtensions(JSContext *cx) { JS_ASSERT(isExtensible()); RootedObject self(cx, this); @@ -1075,34 +1069,35 @@ JSObject::setFlag(JSContext *cx, /*BaseS UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base); if (!nbase) return false; self->lastProperty()->base()->adoptUnowned(nbase); return true; } - Shape *newShape = Shape::setObjectFlag(cx, flag, getTaggedProto(), lastProperty()); + UnrootedShape newShape = Shape::setObjectFlag(cx, flag, getTaggedProto(), lastProperty()); if (!newShape) return false; self->shape_ = newShape; return true; } -/* static */ Shape * +/* static */ UnrootedShape Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, TaggedProto proto, Shape *last) { if (last->getObjectFlags() & flag) return last; StackBaseShape base(last); base.flags |= flag; - return replaceLastProperty(cx, base, proto, last); + RootedShape lastRoot(cx, last); + return replaceLastProperty(cx, base, proto, lastRoot); } /* static */ inline HashNumber StackBaseShape::hash(const StackBaseShape *base) { HashNumber hash = base->flags; hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->clasp) >> 3); hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->parent) >> 3); @@ -1233,73 +1228,74 @@ EmptyShape::getInitialShape(JSContext *c Rooted<TaggedProto> protoRoot(cx, lookup.proto); RootedObject parentRoot(cx, lookup.parent); StackBaseShape base(clasp, parent, objectFlags); Rooted<UnownedBaseShape*> nbase(cx, BaseShape::getUnowned(cx, base)); if (!nbase) return NULL; - Shape *shape = cx->propertyTree().newShape(cx); + UnrootedShape shape = cx->propertyTree().newShape(cx); if (!shape) return NULL; new (shape) EmptyShape(nbase, nfixed); lookup.proto = protoRoot; lookup.parent = parentRoot; if (!table.relookupOrAdd(p, lookup, InitialShapeEntry(shape, lookup.proto))) return NULL; return shape; } void -NewObjectCache::invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto_) +NewObjectCache::invalidateEntriesForShape(JSContext *cx, HandleShape shape, HandleObject proto) { + AssertCanGC(); Class *clasp = shape->getObjectClass(); gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); if (CanBeFinalizedInBackground(kind, clasp)) kind = GetBackgroundAllocKind(kind); Rooted<GlobalObject *> global(cx, &shape->getObjectParent()->global()); - RootedObject proto(cx, proto_); - types::TypeObject *type = proto->getNewType(cx); + Rooted<types::TypeObject *> type(cx, proto->getNewType(cx)); EntryIndex entry; if (lookupGlobal(clasp, global, kind, &entry)) PodZero(&entries[entry]); if (!proto->isGlobal() && lookupProto(clasp, proto, kind, &entry)) PodZero(&entries[entry]); if (lookupType(clasp, type, kind, &entry)) PodZero(&entries[entry]); } /* static */ void -EmptyShape::insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto) +EmptyShape::insertInitialShape(JSContext *cx, HandleShape shape, HandleObject proto) { - InitialShapeEntry::Lookup lookup(shape->getObjectClass(), proto, shape->getObjectParent(), - shape->numFixedSlots(), shape->getObjectFlags()); + InitialShapeEntry::Lookup lookup(shape->getObjectClass(), TaggedProto(proto), + shape->getObjectParent(), shape->numFixedSlots(), + shape->getObjectFlags()); InitialShapeSet::Ptr p = cx->compartment->initialShapes.lookup(lookup); JS_ASSERT(p); InitialShapeEntry &entry = const_cast<InitialShapeEntry &>(*p); JS_ASSERT(entry.shape->isEmptyShape()); /* The new shape had better be rooted at the old one. */ #ifdef DEBUG - Shape *nshape = shape; + RawShape nshape = shape; while (!nshape->isEmptyShape()) nshape = nshape->previous(); JS_ASSERT(nshape == entry.shape); #endif - entry.shape = shape; + entry.shape = shape.get(); /* * This affects the shape that will be produced by the various NewObject * methods, so clear any cache entry referring to the old shape. This is * not required for correctness (though it may bust on the above asserts): * the NewObject must always check for a nativeEmpty() result and generate * the appropriate properties if found. Clearing the cache entry avoids * this duplicate regeneration. @@ -1310,17 +1306,17 @@ EmptyShape::insertInitialShape(JSContext void JSCompartment::sweepInitialShapeTable() { gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_TABLES_INITIAL_SHAPE); if (initialShapes.initialized()) { for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) { const InitialShapeEntry &entry = e.front(); - Shape *shape = entry.shape; + RawShape shape = entry.shape; JSObject *proto = entry.proto.raw(); if (IsShapeAboutToBeFinalized(&shape) || (entry.proto.isObject() && IsObjectAboutToBeFinalized(&proto))) { e.removeFront(); } else { #ifdef DEBUG DebugOnly<JSObject *> parent = shape->getObjectParent(); JS_ASSERT(!parent || !IsObjectAboutToBeFinalized(&parent)); JS_ASSERT(parent == shape->getObjectParent());
--- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -85,18 +85,22 @@ * trees are more space-efficient than alternatives. This was removed in bug * 631138; see that bug for the full details. * * Because many Shapes have similar data, there is actually a secondary type * called a BaseShape that holds some of a Shape's data. Many shapes can share * a single BaseShape. */ +struct JSObject; + namespace js { +class Bindings; + /* Limit on the number of slotful properties in an object. */ static const uint32_t SHAPE_INVALID_SLOT = JS_BIT(24) - 1; static const uint32_t SHAPE_MAXIMUM_SLOT = JS_BIT(24) - 2; /* * Shapes use multiplicative hashing, but specialized to * minimize footprint. */ @@ -127,17 +131,17 @@ struct ShapeTable { ~ShapeTable() { js_free(entries); } /* By definition, hashShift = HASH_BITS - log2(capacity). */ uint32_t capacity() const { return JS_BIT(HASH_BITS - hashShift); } /* Computes the size of the entries array for a given capacity. */ - static size_t sizeOfEntries(size_t cap) { return cap * sizeof(Shape *); } + static size_t sizeOfEntries(size_t cap) { return cap * sizeof(RawShape); } /* * This counts the ShapeTable object itself (which must be * heap-allocated) and its |entries| array. */ size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { return mallocSizeOf(this) + mallocSizeOf(entries); } @@ -155,29 +159,21 @@ struct ShapeTable { */ bool grow(JSContext *cx); /* * NB: init and change are fallible but do not report OOM, so callers can * cope or ignore. They do however use JSRuntime's calloc_ method in order * to update the malloc counter on success. */ - bool init(JSRuntime *rt, js::Shape *lastProp); + bool init(JSRuntime *rt, UnrootedShape lastProp); bool change(int log2Delta, JSContext *cx); - js::Shape **search(jsid id, bool adding); + Shape **search(jsid id, bool adding); }; -} /* namespace js */ - -struct JSObject; - -namespace js { - -class PropertyTree; - /* * Reuse the API-only JSPROP_INDEX attribute to mean shadowability. */ #define JSPROP_SHADOWABLE JSPROP_INDEX /* * Shapes encode information about both a property lineage *and* a particular * property. This information is split across the Shape and the BaseShape @@ -224,16 +220,17 @@ class PropertyTree; * last property is used to query information about the object. Care must be * taken to call JSObject::canRemoveLastProperty when unwinding an object to * an earlier property, however. */ ForwardDeclare(UnownedBaseShape); ForwardDeclare(BaseShape); ForwardDeclare(Shape); +struct StackBaseShape; class BaseShape : public js::gc::Cell { public: friend struct Shape; friend struct StackBaseShape; friend struct StackShape; @@ -267,23 +264,23 @@ class BaseShape : public js::gc::Cell private: Class *clasp; /* Class of referring object. */ HeapPtrObject parent; /* Parent of referring object. */ uint32_t flags; /* Vector of above flags. */ uint32_t slotSpan_; /* Object slot span for BaseShapes at * dictionary last properties. */ union { - js::PropertyOp rawGetter; /* getter hook for shape */ + PropertyOp rawGetter; /* getter hook for shape */ JSObject *getterObj; /* user-defined callable "get" object or null if shape->hasGetterValue() */ }; union { - js::StrictPropertyOp rawSetter; /* setter hook for shape */ + StrictPropertyOp rawSetter; /* setter hook for shape */ JSObject *setterObj; /* user-defined callable "set" object or null if shape->hasSetterValue() */ }; /* For owned BaseShapes, the canonical unowned BaseShape. */ HeapPtr<UnownedBaseShape> unowned_; /* For owned BaseShapes, the shape's shape table. */ @@ -388,33 +385,33 @@ struct StackBaseShape typedef const StackBaseShape *Lookup; uint32_t flags; Class *clasp; JSObject *parent; PropertyOp rawGetter; StrictPropertyOp rawSetter; - StackBaseShape(UnrootedBaseShape base) + explicit StackBaseShape(UnrootedBaseShape base) : flags(base->flags & BaseShape::OBJECT_FLAG_MASK), clasp(base->clasp), parent(base->parent), rawGetter(NULL), rawSetter(NULL) {} StackBaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags) : flags(objectFlags), clasp(clasp), parent(parent), rawGetter(NULL), rawSetter(NULL) {} - inline StackBaseShape(Shape *shape); + inline StackBaseShape(UnrootedShape shape); inline void updateGetterSetter(uint8_t attrs, PropertyOp rawGetter, StrictPropertyOp rawSetter); static inline HashNumber hash(const StackBaseShape *lookup); static inline bool match(RawUnownedBaseShape key, const StackBaseShape *lookup); @@ -493,50 +490,50 @@ struct Shape : public js::gc::Cell KidsPointer kids; /* null, single child, or a tagged ptr to many-kids data structure */ HeapPtrShape *listp; /* dictionary list starting at shape_ has a double-indirect back pointer, either to the next shape's parent if not last, else to obj->shape_ */ }; - static inline Shape *search(JSContext *cx, Shape *start, jsid id, - Shape ***pspp, bool adding = false); + static inline UnrootedShape search(JSContext *cx, Shape *start, jsid id, + Shape ***pspp, bool adding = false); - static inline Shape *searchNoAllocation(Shape *start, jsid id); + static inline UnrootedShape searchNoAllocation(UnrootedShape start, jsid id); inline void removeFromDictionary(JSObject *obj); inline void insertIntoDictionary(HeapPtrShape *dictp); inline void initDictionaryShape(const StackShape &child, uint32_t nfixed, HeapPtrShape *dictp); - Shape *getChildBinding(JSContext *cx, const StackShape &child); + UnrootedShape getChildBinding(JSContext *cx, const StackShape &child); /* Replace the base shape of the last shape in a non-dictionary lineage with base. */ - static Shape *replaceLastProperty(JSContext *cx, const StackBaseShape &base, - TaggedProto proto, Shape *shape); + static UnrootedShape replaceLastProperty(JSContext *cx, const StackBaseShape &base, + TaggedProto proto, HandleShape shape); static bool hashify(JSContext *cx, HandleShape shape); - void handoffTableTo(Shape *newShape); + void handoffTableTo(UnrootedShape newShape); - inline void setParent(js::Shape *p); + inline void setParent(UnrootedShape p); bool ensureOwnBaseShape(JSContext *cx) { if (base()->isOwned()) return true; RootedShape self(cx, this); return makeOwnBaseShape(cx, self); } static bool makeOwnBaseShape(JSContext *cx, HandleShape shape); public: bool hasTable() const { return base()->hasTable(); } - js::ShapeTable &table() const { return base()->table(); } + ShapeTable &table() const { return base()->table(); } void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *propTableSize, size_t *kidsSize) const { *propTableSize = hasTable() ? table().sizeOfIncludingThis(mallocSizeOf) : 0; *kidsSize = !inDictionary() && kids.isHash() ? kids.toHash()->sizeOfIncludingThis(mallocSizeOf) : 0; } @@ -548,20 +545,22 @@ struct Shape : public js::gc::Cell const HeapPtrShape &previous() const { return parent; } class Range { protected: friend struct Shape; - Shape *cursor; + + /* |cursor| is rooted manually when necessary using Range::AutoRooter. */ + RawShape cursor; public: - Range(Shape *shape) : cursor(shape) { } + Range(UnrootedShape shape) : cursor(shape) { } bool empty() const { return !cursor || cursor->isEmptyShape(); } Shape &front() const { JS_ASSERT(!empty()); return *cursor; @@ -594,18 +593,18 @@ struct Shape : public js::gc::Cell Range all() { return Range(this); } Class *getObjectClass() const { return base()->clasp; } JSObject *getObjectParent() const { return base()->parent; } - static Shape *setObjectParent(JSContext *cx, JSObject *obj, TaggedProto proto, Shape *last); - static Shape *setObjectFlag(JSContext *cx, BaseShape::Flag flag, TaggedProto proto, Shape *last); + static UnrootedShape setObjectParent(JSContext *cx, JSObject *obj, TaggedProto proto, Shape *last); + static UnrootedShape setObjectFlag(JSContext *cx, BaseShape::Flag flag, TaggedProto proto, Shape *last); uint32_t getObjectFlags() const { return base()->getObjectFlags(); } bool hasObjectFlag(BaseShape::Flag flag) const { JS_ASSERT(!(flag & ~BaseShape::OBJECT_FLAG_MASK)); return !!(base()->flags & flag); } protected: @@ -655,54 +654,54 @@ struct Shape : public js::gc::Cell PropertyOp getter() const { return base()->rawGetter; } bool hasDefaultGetter() const { return !base()->rawGetter; } PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return base()->rawGetter; } JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return base()->getterObj; } // Per ES5, decode null getterObj as the undefined value, which encodes as null. Value getterValue() const { JS_ASSERT(hasGetterValue()); - return base()->getterObj ? js::ObjectValue(*base()->getterObj) : js::UndefinedValue(); + return base()->getterObj ? ObjectValue(*base()->getterObj) : UndefinedValue(); } Value getterOrUndefined() const { return (hasGetterValue() && base()->getterObj) ? ObjectValue(*base()->getterObj) : UndefinedValue(); } StrictPropertyOp setter() const { return base()->rawSetter; } bool hasDefaultSetter() const { return !base()->rawSetter; } StrictPropertyOp setterOp() const { JS_ASSERT(!hasSetterValue()); return base()->rawSetter; } JSObject *setterObject() const { JS_ASSERT(hasSetterValue()); return base()->setterObj; } // Per ES5, decode null setterObj as the undefined value, which encodes as null. Value setterValue() const { JS_ASSERT(hasSetterValue()); - return base()->setterObj ? js::ObjectValue(*base()->setterObj) : js::UndefinedValue(); + return base()->setterObj ? ObjectValue(*base()->setterObj) : UndefinedValue(); } Value setterOrUndefined() const { return (hasSetterValue() && base()->setterObj) ? ObjectValue(*base()->setterObj) : UndefinedValue(); } - void update(js::PropertyOp getter, js::StrictPropertyOp setter, uint8_t attrs); + void update(PropertyOp getter, StrictPropertyOp setter, uint8_t attrs); - inline bool matches(const Shape *other) const; + inline bool matches(const UnrootedShape other) const; inline bool matches(const StackShape &other) const; inline bool matchesParamsAfterId(UnrootedBaseShape base, uint32_t aslot, unsigned aattrs, unsigned aflags, int ashortid) const; bool get(JSContext* cx, HandleObject receiver, JSObject *obj, JSObject *pobj, MutableHandleValue vp); bool set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, MutableHandleValue vp); - BaseShape* base() const { return base_; } + RawBaseShape base() const { return base_.get(); } bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; } uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); } uint32_t maybeSlot() const { return slotInfo & SLOT_MASK; } bool isEmptyShape() const { JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot()); return JSID_IS_EMPTY(propid_); @@ -794,58 +793,58 @@ struct Shape : public js::gc::Cell JS_ASSERT_IF(isDataDescriptor(), writable()); return hasSlot() || (attrs & JSPROP_SHADOWABLE); } uint32_t entryCount() { if (hasTable()) return table().entryCount; - js::Shape *shape = this; + UnrootedShape shape = this; uint32_t count = 0; - for (js::Shape::Range r = shape->all(); !r.empty(); r.popFront()) + for (Shape::Range r = shape->all(); !r.empty(); r.popFront()) ++count; return count; } bool isBigEnoughForAShapeTable() { JS_ASSERT(!hasTable()); - js::Shape *shape = this; + UnrootedShape shape = this; uint32_t count = 0; - for (js::Shape::Range r = shape->all(); !r.empty(); r.popFront()) { + for (Shape::Range r = shape->all(); !r.empty(); r.popFront()) { ++count; if (count >= ShapeTable::MIN_ENTRIES) return true; } return false; } #ifdef DEBUG void dump(JSContext *cx, FILE *fp) const; void dumpSubtree(JSContext *cx, int level, FILE *fp) const; #endif void finalize(FreeOp *fop); - void removeChild(js::Shape *child); + void removeChild(UnrootedShape child); - static inline void writeBarrierPre(Shape *shape); - static inline void writeBarrierPost(Shape *shape, void *addr); + static inline void writeBarrierPre(UnrootedShape shape); + static inline void writeBarrierPost(UnrootedShape shape, void *addr); /* * All weak references need a read barrier for incremental GC. This getter * method implements the read barrier. It's used to obtain initial shapes * from the compartment. */ - static inline void readBarrier(Shape *shape); + static inline void readBarrier(UnrootedShape shape); static inline ThingRootKind rootKind() { return THING_ROOT_SHAPE; } inline void markChildren(JSTracer *trc); - inline Shape *search(JSContext *cx, jsid id) { + inline UnrootedShape search(JSContext *cx, jsid id) { Shape **_; return search(cx, this, id, &_); } /* For JIT usage */ static inline size_t offsetOfBase() { return offsetof(Shape, base_); } private: @@ -908,17 +907,17 @@ struct EmptyShape : public js::Shape static Shape *getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto, JSObject *parent, gc::AllocKind kind, uint32_t objectFlags = 0); /* * Reinsert an alternate initial shape, to be returned by future * getInitialShape calls, until the new shape becomes unreachable in a GC * and the table entry is purged. */ - static void insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto); + static void insertInitialShape(JSContext *cx, HandleShape shape, HandleObject proto); }; /* * Entries for the per-compartment initialShapes set indexing initial shapes * for objects in the compartment and the associated types. */ struct InitialShapeEntry { @@ -965,33 +964,33 @@ struct StackShape /* For performance, StackShape only roots when absolutely necessary. */ RawUnownedBaseShape base; RawId propid; uint32_t slot_; uint8_t attrs; uint8_t flags; int16_t shortid; - StackShape(UnrootedUnownedBaseShape base, jsid propid, uint32_t slot, - uint32_t nfixed, unsigned attrs, unsigned flags, int shortid) + explicit StackShape(UnrootedUnownedBaseShape base, jsid propid, uint32_t slot, + uint32_t nfixed, unsigned attrs, unsigned flags, int shortid) : base(base), propid(propid), slot_(slot), attrs(uint8_t(attrs)), flags(uint8_t(flags)), shortid(int16_t(shortid)) { JS_ASSERT(base); JS_ASSERT(!JSID_IS_VOID(propid)); JS_ASSERT(slot <= SHAPE_INVALID_SLOT); } - StackShape(const Shape *shape) + StackShape(const UnrootedShape &shape) : base(shape->base()->unowned()), - propid(const_cast<Shape *>(shape)->propidRef()), + propid(shape->propidRef()), slot_(shape->slotInfo & Shape::SLOT_MASK), attrs(shape->attrs), flags(shape->flags), shortid(shape->shortid_) {} bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; } bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; } @@ -1029,36 +1028,36 @@ struct StackShape JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; }; } /* namespace js */ /* js::Shape pointer tag bit indicating a collision. */ #define SHAPE_COLLISION (uintptr_t(1)) -#define SHAPE_REMOVED ((js::Shape *) SHAPE_COLLISION) +#define SHAPE_REMOVED ((RawShape) SHAPE_COLLISION) /* Macros to get and set shape pointer values and collision flags. */ #define SHAPE_IS_FREE(shape) ((shape) == NULL) #define SHAPE_IS_REMOVED(shape) ((shape) == SHAPE_REMOVED) #define SHAPE_IS_LIVE(shape) ((shape) > SHAPE_REMOVED) -#define SHAPE_FLAG_COLLISION(spp,shape) (*(spp) = (js::Shape *) \ +#define SHAPE_FLAG_COLLISION(spp,shape) (*(spp) = (RawShape) \ (uintptr_t(shape) | SHAPE_COLLISION)) #define SHAPE_HAD_COLLISION(shape) (uintptr_t(shape) & SHAPE_COLLISION) #define SHAPE_FETCH(spp) SHAPE_CLEAR_COLLISION(*(spp)) #define SHAPE_CLEAR_COLLISION(shape) \ - ((js::Shape *) (uintptr_t(shape) & ~SHAPE_COLLISION)) + ((RawShape) (uintptr_t(shape) & ~SHAPE_COLLISION)) #define SHAPE_STORE_PRESERVING_COLLISION(spp, shape) \ - (*(spp) = (js::Shape *) (uintptr_t(shape) | SHAPE_HAD_COLLISION(*(spp)))) + (*(spp) = (RawShape) (uintptr_t(shape) | SHAPE_HAD_COLLISION(*(spp)))) namespace js { -inline Shape * +inline UnrootedShape Shape::search(JSContext *cx, Shape *start, jsid id, Shape ***pspp, bool adding) { AssertCanGC(); #ifdef DEBUG { SkipRoot skip0(cx, &start); SkipRoot skip1(cx, &id); MaybeCheckStackRoots(cx); @@ -1092,38 +1091,38 @@ Shape::search(JSContext *cx, Shape *star * No table built -- there weren't enough entries, or OOM occurred. * Don't increment numLinearSearches, to keep hasTable() false. */ JS_ASSERT(!start->hasTable()); } else { start->incrementNumLinearSearches(); } - for (Shape *shape = start; shape; shape = shape->parent) { + for (UnrootedShape shape = start; shape; shape = shape->parent) { if (shape->propidRef() == id) return shape; } - return NULL; + return UnrootedShape(NULL); } -/* static */ inline Shape * -Shape::searchNoAllocation(Shape *start, jsid id) +/* static */ inline UnrootedShape +Shape::searchNoAllocation(UnrootedShape start, jsid id) { if (start->hasTable()) { Shape **spp = start->table().search(id, false); return SHAPE_FETCH(spp); } - for (Shape *shape = start; shape; shape = shape->parent) { + for (UnrootedShape shape = start; shape; shape = shape->parent) { if (shape->propidRef() == id) return shape; } - return NULL; + return UnrootedShape(NULL); } void MarkNonNativePropertyFound(HandleObject obj, MutableHandleShape propp); template<> struct RootKind<Shape *> : SpecificRootKind<Shape *, THING_ROOT_SHAPE> {}; template<> struct RootKind<BaseShape *> : SpecificRootKind<BaseShape *, THING_ROOT_BASE_SHAPE> {};
--- a/js/src/jsscopeinlines.h +++ b/js/src/jsscopeinlines.h @@ -119,17 +119,17 @@ BaseShape::operator=(const BaseShape &ot inline bool BaseShape::matchesGetterSetter(PropertyOp rawGetter, StrictPropertyOp rawSetter) const { return rawGetter == this->rawGetter && rawSetter == this->rawSetter; } inline -StackBaseShape::StackBaseShape(Shape *shape) +StackBaseShape::StackBaseShape(UnrootedShape shape) : flags(shape->getObjectFlags()), clasp(shape->getObjectClass()), parent(shape->getObjectParent()) { updateGetterSetter(shape->attrs, shape->getter(), shape->setter()); } inline void @@ -233,17 +233,17 @@ StackShape::hash() const hash = JS_ROTATE_LEFT32(hash, 4) ^ attrs; hash = JS_ROTATE_LEFT32(hash, 4) ^ shortid; hash = JS_ROTATE_LEFT32(hash, 4) ^ slot_; hash = JS_ROTATE_LEFT32(hash, 4) ^ JSID_BITS(propid); return hash; } inline bool -Shape::matches(const js::Shape *other) const +Shape::matches(const UnrootedShape other) const { return propid_.get() == other->propid_.get() && matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs, other->flags, other->shortid_); } inline bool Shape::matches(const StackShape &other) const @@ -329,17 +329,17 @@ Shape::set(JSContext* cx, HandleObject o RootedObject nobj(cx, &obj->asWith().object()); return CallJSPropertyOpSetter(cx, self->setterOp(), nobj, id, strict, vp); } return CallJSPropertyOpSetter(cx, self->setterOp(), obj, id, strict, vp); } inline void -Shape::setParent(js::Shape *p) +Shape::setParent(UnrootedShape p) { JS_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(), p->maybeSlot() <= maybeSlot()); JS_ASSERT_IF(p && !inDictionary(), hasSlot() == (p->maybeSlot() != maybeSlot())); parent = p; } @@ -368,17 +368,17 @@ Shape::insertIntoDictionary(HeapPtrShape */ JS_ASSERT(inDictionary()); JS_ASSERT(!listp); JS_ASSERT_IF(*dictp, (*dictp)->inDictionary()); JS_ASSERT_IF(*dictp, (*dictp)->listp == dictp); JS_ASSERT_IF(*dictp, compartment() == (*dictp)->compartment()); - setParent(*dictp); + setParent(dictp->get()); if (parent) parent->listp = &parent; listp = (HeapPtrShape *) dictp; *dictp = this; } void Shape::initDictionaryShape(const StackShape &child, uint32_t nfixed, HeapPtrShape *dictp) @@ -395,43 +395,43 @@ EmptyShape::EmptyShape(UnrootedUnownedBa : js::Shape(base, nfixed) { /* Only empty shapes can be NON_NATIVE. */ if (!getObjectClass()->isNative()) flags |= NON_NATIVE; } inline void -Shape::writeBarrierPre(Shape *shape) +Shape::writeBarrierPre(UnrootedShape shape) { #ifdef JSGC_INCREMENTAL if (!shape) return; JSCompartment *comp = shape->compartment(); if (comp->needsBarrier()) { - Shape *tmp = const_cast<Shape *>(shape); + UnrootedShape tmp = shape; MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "write barrier"); JS_ASSERT(tmp == shape); } #endif } inline void -Shape::writeBarrierPost(Shape *shape, void *addr) +Shape::writeBarrierPost(UnrootedShape shape, void *addr) { } inline void -Shape::readBarrier(Shape *shape) +Shape::readBarrier(UnrootedShape shape) { #ifdef JSGC_INCREMENTAL JSCompartment *comp = shape->compartment(); if (comp->needsBarrier()) { - Shape *tmp = const_cast<Shape *>(shape); + UnrootedShape tmp = shape; MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier"); JS_ASSERT(tmp == shape); } #endif } inline void Shape::markChildren(JSTracer *trc)
--- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -127,19 +127,21 @@ Bindings::initWithTemporaryStorage(JSCon RootedId id(cx, NameToId(bi->name())); unsigned attrs = JSPROP_PERMANENT | JSPROP_ENUMERATE | (bi->kind() == CONSTANT ? JSPROP_READONLY : 0); unsigned frameIndex = bi.frameIndex(); StackShape child(nbase, id, slot++, 0, attrs, Shape::HAS_SHORTID, frameIndex); DropUnrooted(nbase); - self->callObjShape_ = self->callObjShape_->getChildBinding(cx, child); - if (!self->callObjShape_) + UnrootedShape shape = self->callObjShape_->getChildBinding(cx, child); + if (!shape) return false; + + self->callObjShape_ = shape; } JS_ASSERT(!bi); return true; } uint8_t * Bindings::switchToScriptStorage(Binding *newBindingArray)
--- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -186,17 +186,17 @@ class Bindings static bool clone(JSContext *cx, InternalBindingsHandle self, uint8_t *dstScriptData, HandleScript srcScript); unsigned numArgs() const { return numArgs_; } unsigned numVars() const { return numVars_; } unsigned count() const { return numArgs() + numVars(); } /* Return the initial shape of call objects created for this scope. */ - Shape *callObjShape() const { return callObjShape_; } + UnrootedShape callObjShape() const { return callObjShape_.get(); } /* Convenience method to get the var index of 'arguments'. */ static unsigned argumentsVarIndex(JSContext *cx, InternalBindingsHandle); /* Return whether the binding at bindingIndex is aliased. */ bool bindingIsAliased(unsigned bindingIndex); /* Return whether this scope has any aliased bindings. */ @@ -205,17 +205,17 @@ class Bindings void trace(JSTracer *trc); }; template <> struct RootMethods<Bindings> { static Bindings initial(); static ThingRootKind kind() { return THING_ROOT_BINDINGS; } static bool poisoned(const Bindings &bindings) { - return IsPoisonedPtr(bindings.callObjShape()); + return IsPoisonedPtr(static_cast<RawShape>(bindings.callObjShape())); } }; class ScriptCounts { friend struct ::JSScript; friend struct ScriptAndCounts;
--- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3236,23 +3236,23 @@ js::str_fromCharCode(JSContext *cx, unsi return JS_TRUE; } static JSFunctionSpec string_static_methods[] = { JS_FN("fromCharCode", js::str_fromCharCode, 1, 0), JS_FS_END }; -Shape * +UnrootedShape StringObject::assignInitialShape(JSContext *cx) { JS_ASSERT(nativeEmpty()); - return addDataProperty(cx, NameToId(cx->names().length), - LENGTH_SLOT, JSPROP_PERMANENT | JSPROP_READONLY); + RootedId lengthid(cx, NameToId(cx->names().length)); + return addDataProperty(cx, lengthid, LENGTH_SLOT, JSPROP_PERMANENT | JSPROP_READONLY); } JSObject * js_InitStringClass(JSContext *cx, HandleObject obj) { JS_ASSERT(obj->isNative()); Rooted<GlobalObject*> global(cx, &obj->asGlobal());
--- a/js/src/jswatchpoint.cpp +++ b/js/src/jswatchpoint.cpp @@ -127,17 +127,17 @@ WatchpointMap::triggerWatchpoint(JSConte /* Copy the entry, since GC would invalidate p. */ JSWatchPointHandler handler = p->value.handler; RootedObject closure(cx, p->value.closure); /* Determine the property's old value. */ Value old; old.setUndefined(); if (obj->isNative()) { - if (Shape *shape = obj->nativeLookup(cx, id)) { + if (UnrootedShape shape = obj->nativeLookup(cx, id)) { if (shape->hasSlot()) old = obj->nativeGetSlot(shape->slot()); } } /* Call the handler. */ return handler(cx, obj, id, old, vp.address(), closure); }
--- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -4719,20 +4719,19 @@ xml_lookupGeneric(JSContext *cx, HandleO if (!JSID_IS_VOID(funid)) return baseops::LookupProperty(cx, obj, funid, objp, propp); found = HasNamedProperty(xml, qn); } if (!found) { objp.set(NULL); propp.set(NULL); } else { - Shape *shape = - js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, - SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, - 0, 0); + RootedShape shape(cx, js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, + SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, + 0, 0)); if (!shape) return JS_FALSE; objp.set(obj); propp.set(shape); } return JS_TRUE; } @@ -4751,23 +4750,22 @@ xml_lookupElement(JSContext *cx, HandleO { JSXML *xml = reinterpret_cast<JSXML *>(obj->getPrivate()); if (!HasIndexedProperty(xml, index)) { objp.set(NULL); propp.set(NULL); return true; } - jsid id; - if (!IndexToId(cx, index, &id)) + RootedId id(cx); + if (!IndexToId(cx, index, id.address())) return false; - Shape *shape = - js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, SHAPE_INVALID_SLOT, - JSPROP_ENUMERATE, 0, 0); + RootedShape shape(cx, js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, + SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, 0, 0)); if (!shape) return false; objp.set(obj); propp.set(shape); return true; }
--- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -192,17 +192,17 @@ static const JSC::MacroAssembler::Regist void loadPtrFromImm(void *ptr, RegisterID reg) { loadPtr(ptr, reg); } void loadShape(RegisterID obj, RegisterID shape) { loadPtr(Address(obj, JSObject::offsetOfShape()), shape); } - Jump guardShape(RegisterID objReg, Shape *shape) { + Jump guardShape(RegisterID objReg, UnrootedShape shape) { return branchPtr(NotEqual, Address(objReg, JSObject::offsetOfShape()), ImmPtr(shape)); } Jump guardShape(RegisterID objReg, JSObject *obj) { return guardShape(objReg, obj->lastProperty()); } /* @@ -976,17 +976,17 @@ static const JSC::MacroAssembler::Regist void loadDynamicSlot(RegisterID objReg, uint32_t index, RegisterID typeReg, RegisterID dataReg) { loadPtr(Address(objReg, JSObject::offsetOfSlots()), dataReg); loadValueAsComponents(Address(dataReg, index * sizeof(Value)), typeReg, dataReg); } void loadObjProp(JSObject *obj, RegisterID objReg, - js::Shape *shape, + js::UnrootedShape shape, RegisterID typeReg, RegisterID dataReg) { if (obj->isFixedSlot(shape->slot())) loadInlineSlot(objReg, shape->slot(), typeReg, dataReg); else loadDynamicSlot(objReg, obj->dynamicSlotIndex(shape->slot()), typeReg, dataReg); } @@ -1337,16 +1337,17 @@ static const JSC::MacroAssembler::Regist /* * Get a free object for the specified GC kind in compartment, writing it * to result and filling it in according to templateObject. Returns a jump * taken if a free thing was not retrieved. Note: don't call this directly, * use Compiler::getNewObject instead. */ Jump getNewObject(JSContext *cx, RegisterID result, JSObject *templateObject) { + AutoAssertNoGC nogc; gc::AllocKind allocKind = templateObject->getAllocKind(); JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST); int thingSize = (int)gc::Arena::thingSize(allocKind); JS_ASSERT(cx->typeInferenceEnabled()); JS_ASSERT(!templateObject->hasDynamicSlots()); JS_ASSERT(!templateObject->hasDynamicElements());
--- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -6074,17 +6074,17 @@ mjit::Compiler::jsop_aliasedArg(unsigned void mjit::Compiler::jsop_aliasedVar(ScopeCoordinate sc, bool get, bool poppedAfter) { RegisterID reg = frame.allocReg(Registers::SavedRegs).reg(); masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), reg); for (unsigned i = 0; i < sc.hops; i++) masm.loadPayload(Address(reg, ScopeObject::offsetOfEnclosingScope()), reg); - Shape *shape = ScopeCoordinateToStaticScope(script_, PC).scopeShape(); + UnrootedShape shape = ScopeCoordinateToStaticScope(script_, PC).scopeShape(); Address addr; if (shape->numFixedSlots() <= sc.slot) { masm.loadPtr(Address(reg, JSObject::offsetOfSlots()), reg); addr = Address(reg, (sc.slot - shape->numFixedSlots()) * sizeof(Value)); } else { addr = Address(reg, JSObject::getFixedSlotOffset(sc.slot)); } @@ -6222,17 +6222,17 @@ mjit::Compiler::iter(unsigned flags) Jump mismatchedObject = masm.branchPtr(Assembler::NotEqual, T1, T2); stubcc.linkExit(mismatchedObject, Uses(1)); /* Compare shape of object's prototype with iterator. */ masm.loadPtr(Address(reg, JSObject::offsetOfType()), T1); masm.loadPtr(Address(T1, offsetof(types::TypeObject, proto)), T1); masm.loadShape(T1, T1); masm.loadPtr(Address(nireg, offsetof(NativeIterator, shapes_array)), T2); - masm.loadPtr(Address(T2, sizeof(Shape *)), T2); + masm.loadPtr(Address(T2, sizeof(RawShape)), T2); Jump mismatchedProto = masm.branchPtr(Assembler::NotEqual, T1, T2); stubcc.linkExit(mismatchedProto, Uses(1)); /* * Compare object's prototype's prototype with NULL. The last native * iterator will always have a prototype chain length of one * (i.e. it must be a plain object), so we do not need to generate * a loop here. @@ -6457,16 +6457,18 @@ mjit::Compiler::jsop_bindgname() INLINE_STUBCALL(stubs::BindGlobalName, REJOIN_NONE); frame.takeReg(Registers::ReturnReg); frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg); } bool mjit::Compiler::jsop_getgname(uint32_t index) { + AssertCanGC(); + /* Optimize undefined, NaN and Infinity. */ PropertyName *name = script_->getName(index); if (name == cx->names().undefined) { frame.push(UndefinedValue()); return true; } if (name == cx->names().NaN) { frame.push(cx->runtime->NaNValue); @@ -6495,19 +6497,19 @@ mjit::Compiler::jsop_getgname(uint32_t i if (!propertyTypes) return false; /* * If we are accessing a defined global which is a normal data property * then bake its address into the jitcode and guard against future * reallocation of the global object's slots. */ - js::Shape *shape = globalObj->nativeLookup(cx, NameToId(name)); + UnrootedShape shape = globalObj->nativeLookup(cx, NameToId(name)); if (shape && shape->hasDefaultGetter() && shape->hasSlot()) { - HeapSlot *value = &globalObj->getSlotRef(shape->slot()); + HeapSlot *value = &globalObj->getSlotRef(DropUnrooted(shape)->slot()); if (!value->isUndefined() && !propertyTypes->isOwnProperty(cx, globalObj->getType(cx), true)) { watchGlobalReallocation(); RegisterID reg = frame.allocReg(); masm.move(ImmPtr(value), reg); BarrierState barrier = pushAddressMaybeBarrier(Address(reg), type, true); finishBarrier(barrier, REJOIN_GETTER, 0); @@ -6621,17 +6623,17 @@ mjit::Compiler::jsop_setgname(PropertyNa * Note: object branding is disabled when inference is enabled. With * branding there is no way to ensure that a non-function property * can't get a function later and cause the global object to become * branded, requiring a shape change if it changes again. */ types::HeapTypeSet *types = globalObj->getType(cx)->getProperty(cx, id, false); if (!types) return false; - js::Shape *shape = globalObj->nativeLookup(cx, NameToId(name)); + UnrootedShape shape = globalObj->nativeLookup(cx, NameToId(name)); if (shape && shape->hasDefaultSetter() && shape->writable() && shape->hasSlot() && !types->isOwnProperty(cx, globalObj->getType(cx), true)) { watchGlobalReallocation(); HeapSlot *value = &globalObj->getSlotRef(shape->slot()); RegisterID reg = frame.allocReg(); #ifdef JSGC_INCREMENTAL_MJ /* Write barrier. */ @@ -7906,16 +7908,18 @@ mjit::Compiler::pushedSingleton(unsigned * pushRegs to reflect the final state of the op, which is OK as no inline * code has been emitted since the barrier jump. */ mjit::Compiler::BarrierState mjit::Compiler::pushAddressMaybeBarrier(Address address, JSValueType type, bool reuseBase, bool testUndefined) { + AssertCanGC(); + if (!hasTypeBarriers(PC) && !testUndefined) { frame.push(address, type, reuseBase); return BarrierState(); } RegisterID typeReg, dataReg; frame.loadIntoRegisters(address, reuseBase, &typeReg, &dataReg);
--- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -1510,17 +1510,17 @@ mjit::Compiler::jsop_setelem(bool popGua ic.fastPathStart = masm.label(); // Create the common out-of-line sync block, taking care to link previous // guards here after. RESERVE_OOL_SPACE(stubcc.masm); ic.slowPathStart = stubcc.syncExit(Uses(3)); // Guard obj is a dense array. - Shape *shape = GetDenseArrayShape(cx, globalObj); + UnrootedShape shape = GetDenseArrayShape(cx, globalObj); if (!shape) return false; ic.shapeGuard = masm.guardShape(ic.objReg, shape); stubcc.linkExitDirect(ic.shapeGuard, ic.slowPathStart); // Load the dynamic elements vector. masm.loadPtr(Address(ic.objReg, JSObject::offsetOfElements()), ic.objReg); @@ -2088,17 +2088,17 @@ mjit::Compiler::jsop_getelem() if (id->mightBeType(JSVAL_TYPE_INT32)) { // Always test the type first (see comment in PolyIC.h). if (!id->isTypeKnown()) { ic.typeGuard = masm.testInt32(Assembler::NotEqual, ic.typeReg); stubcc.linkExitDirect(ic.typeGuard.get(), ic.slowPathStart); } // Guard obj is a dense array. - Shape *shape = GetDenseArrayShape(cx, globalObj); + UnrootedShape shape = GetDenseArrayShape(cx, globalObj); if (!shape) return false; ic.shapeGuard = masm.guardShape(ic.objReg, shape); stubcc.linkExitDirect(ic.shapeGuard, ic.slowPathStart); Int32Key key = id->isConstant() ? Int32Key::FromConstant(id->getValue().toInt32()) : Int32Key::FromRegister(ic.id.dataReg());
--- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -1395,22 +1395,22 @@ GetPIC(JSContext *cx, JSScript *script, for (uint32_t i = 0; i < chunk->nPICs; i++) { if (pics[i].pc == pc) return &pics[i]; } return NULL; } -Shape * +UnrootedShape mjit::GetPICSingleShape(JSContext *cx, JSScript *script, jsbytecode *pc, bool constructing) { ic::PICInfo *pic = GetPIC(cx, script, pc, constructing); if (!pic) - return NULL; + return UnrootedShape(NULL); return pic->getSingleShape(); } void JITScript::purgeCaches() { for (unsigned i = 0; i < nchunks; i++) { ChunkDescriptor &desc = chunkDescriptor(i);
--- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -1030,17 +1030,17 @@ IsLowerableFunCallOrApply(jsbytecode *pc #ifdef JS_MONOIC return (*pc == JSOP_FUNCALL && GET_ARGC(pc) >= 1) || (*pc == JSOP_FUNAPPLY && GET_ARGC(pc) == 2); #else return false; #endif } -Shape * +UnrootedShape GetPICSingleShape(JSContext *cx, JSScript *script, jsbytecode *pc, bool constructing); static inline void PurgeCaches(JSScript *script) { for (int constructing = 0; constructing <= 1; constructing++) { for (int barriers = 0; barriers <= 1; barriers++) { mjit::JITScript *jit = script->getJIT((bool) constructing, (bool) barriers);
--- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -63,42 +63,45 @@ ic::GetGlobalName(VMFrame &f, ic::GetGlo { AssertCanGC(); RootedObject obj(f.cx, &f.fp()->global()); PropertyName *name = f.script()->getName(GET_UINT32_INDEX(f.pc())); RecompilationMonitor monitor(f.cx); - Shape *shape = obj->nativeLookup(f.cx, NameToId(name)); - - if (monitor.recompiled()) { - stubs::Name(f); - return; - } - - if (!shape || - !shape->hasDefaultGetter() || - !shape->hasSlot()) + uint32_t slot; { - if (shape) - PatchGetFallback(f, ic); - stubs::Name(f); - return; - } - uint32_t slot = shape->slot(); + RootedShape shape(f.cx, obj->nativeLookup(f.cx, NameToId(name))); + + if (monitor.recompiled()) { + stubs::Name(f); + return; + } - /* Patch shape guard. */ - Repatcher repatcher(f.chunk()); - repatcher.repatch(ic->fastPathStart.dataLabelPtrAtOffset(ic->shapeOffset), obj->lastProperty()); + if (!shape || + !shape->hasDefaultGetter() || + !shape->hasSlot()) + { + if (shape) + PatchGetFallback(f, ic); + stubs::Name(f); + return; + } + slot = shape->slot(); - /* Patch loads. */ - uint32_t index = obj->dynamicSlotIndex(slot); - JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset); - repatcher.patchAddressOffsetForValueLoad(label, index * sizeof(Value)); + /* Patch shape guard. */ + Repatcher repatcher(f.chunk()); + repatcher.repatch(ic->fastPathStart.dataLabelPtrAtOffset(ic->shapeOffset), obj->lastProperty()); + + /* Patch loads. */ + uint32_t index = obj->dynamicSlotIndex(slot); + JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset); + repatcher.patchAddressOffsetForValueLoad(label, index * sizeof(Value)); + } /* Do load anyway... this time. */ stubs::Name(f); } static void JS_FASTCALL DisabledSetGlobal(VMFrame &f, ic::SetGlobalNameIC *ic) { @@ -112,24 +115,24 @@ PatchSetFallback(VMFrame &f, ic::SetGlob { Repatcher repatch(f.chunk()); VoidStubSetGlobal stub = DisabledSetGlobal; JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stub)); repatch.relink(ic->slowPathCall, fptr); } void -SetGlobalNameIC::patchInlineShapeGuard(Repatcher &repatcher, Shape *shape) +SetGlobalNameIC::patchInlineShapeGuard(Repatcher &repatcher, UnrootedShape shape) { JSC::CodeLocationDataLabelPtr label = fastPathStart.dataLabelPtrAtOffset(shapeOffset); repatcher.repatch(label, shape); } static LookupStatus -UpdateSetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, Shape *shape) +UpdateSetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, UnrootedShape shape) { /* Give globals a chance to appear. */ if (!shape) return Lookup_Uncacheable; if (!shape->hasDefaultSetter() || !shape->writable() || !shape->hasSlot() || @@ -157,22 +160,24 @@ ic::SetGlobalName(VMFrame &f, ic::SetGlo { AssertCanGC(); RootedObject obj(f.cx, &f.fp()->global()); RootedPropertyName name(f.cx, f.script()->getName(GET_UINT32_INDEX(f.pc()))); RecompilationMonitor monitor(f.cx); - Shape *shape = obj->nativeLookup(f.cx, NameToId(name)); + { + UnrootedShape shape = obj->nativeLookup(f.cx, NameToId(name)); - if (!monitor.recompiled()) { - LookupStatus status = UpdateSetGlobalName(f, ic, obj, shape); - if (status == Lookup_Error) - THROW(); + if (!monitor.recompiled()) { + LookupStatus status = UpdateSetGlobalName(f, ic, obj, shape); + if (status == Lookup_Error) + THROW(); + } } stubs::SetName(f, name); } class EqualityICLinker : public LinkerHelper { VMFrame &f;
--- a/js/src/methodjit/MonoIC.h +++ b/js/src/methodjit/MonoIC.h @@ -111,17 +111,17 @@ struct SetGlobalNameIC : public GlobalNa RegisterID objReg : 5; /* Register for object, if objConst is false. */ RegisterID shapeReg : 5; /* Register for shape; volatile. */ int32_t fastRejoinOffset : 16; /* Offset from fastPathStart to rejoin. */ /* SET only. */ ValueRemat vr; /* RHS value. */ - void patchInlineShapeGuard(Repatcher &repatcher, Shape *shape); + void patchInlineShapeGuard(Repatcher &repatcher, UnrootedShape shape); }; void JS_FASTCALL GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic); void JS_FASTCALL SetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic); struct EqualityICInfo { typedef JSC::MacroAssembler::RegisterID RegisterID;
--- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -184,17 +184,17 @@ class SetPropCompiler : public PICStubCo NULL); repatcher.relink(labels.getInlineShapeJump(pic.fastPathStart.labelAtOffset(pic.shapeGuard)), pic.slowPathStart); FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, ic::SetPropOrName)); repatcher.relink(pic.slowPathCall, target); } - LookupStatus patchInline(Shape *shape) + LookupStatus patchInline(UnrootedShape shape) { JS_ASSERT(!pic.inlinePathPatched); JaegerSpew(JSpew_PICs, "patch setprop inline at %p\n", pic.fastPathStart.executableAddress()); Repatcher repatcher(f.chunk()); SetPropLabels &labels = pic.setPropLabels(); int32_t offset; @@ -244,17 +244,17 @@ class SetPropCompiler : public PICStubCo } else { CodeLocationLabel shapeGuard = label.labelAtOffset(pic.shapeGuard); repatcher.relink(pic.setPropLabels().getInlineShapeJump(shapeGuard), cs); } if (int secondGuardOffset = getLastStubSecondShapeGuard()) repatcher.relink(label.jumpAtOffset(secondGuardOffset), cs); } - LookupStatus generateStub(Shape *initialShape, Shape *shape, bool adding) + LookupStatus generateStub(UnrootedShape initialShape, UnrootedShape shape, bool adding) { if (hadGC()) return Lookup_Uncacheable; /* Exits to the slow path. */ Vector<Jump, 8> slowExits(cx); Vector<Jump, 8> otherGuards(cx); @@ -505,30 +505,29 @@ class SetPropCompiler : public PICStubCo */ JSObject *proto = obj; while (proto) { if (!proto->isNative()) return disable("non-native proto"); proto = proto->getProto(); } - Shape *initialShape = obj->lastProperty(); + RootedShape initialShape(cx, obj->lastProperty()); uint32_t slots = obj->numDynamicSlots(); unsigned flags = 0; PropertyOp getter = clasp->getProperty; /* * Define the property but do not set it yet. For setmethod, * populate the slot to satisfy the method invariant (in case we * hit an early return below). */ - shape = - obj->putProperty(cx, name, getter, clasp->setProperty, - SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, flags, 0); + shape = JSObject::putProperty(cx, obj, name, getter, clasp->setProperty, + SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, flags, 0); if (!shape) return error(); if (monitor.recompiled()) return Lookup_Uncacheable; /* * Test after calling putProperty since it can switch obj into @@ -1011,17 +1010,17 @@ class GetPropCompiler : public PICStubCo repatcher.relink(pic.getPropLabels().getInlineTypeJump(pic.fastPathStart), start); } disable("generated string length stub"); return Lookup_Cacheable; } - LookupStatus patchInline(JSObject *holder, Shape *shape) + LookupStatus patchInline(JSObject *holder, UnrootedShape shape) { spew("patch", "inline"); Repatcher repatcher(f.chunk()); GetPropLabels &labels = pic.getPropLabels(); int32_t offset; if (holder->isFixedSlot(shape->slot())) { CodeLocationInstruction istr = labels.getDslotsLoad(pic.fastPathRejoin); @@ -1046,17 +1045,17 @@ class GetPropCompiler : public PICStubCo repatcher.patchAddressOffsetForValueLoad(labels.getValueLoad(pic.fastPathRejoin), offset); pic.inlinePathPatched = true; return Lookup_Cacheable; } /* For JSPropertyOp getters. */ - void generateGetterStub(Assembler &masm, Shape *shape, jsid userid, + void generateGetterStub(Assembler &masm, UnrootedShape shape, jsid userid, Label start, Vector<Jump, 8> &shapeMismatches) { AutoAssertNoGC nogc; /* * Getter hook needs to be called from the stub. The state is fully * synced and no registers are live except the result registers. */ @@ -1158,17 +1157,17 @@ class GetPropCompiler : public PICStubCo } linker.patchJump(pic.fastPathRejoin); linkerEpilogue(linker, start, shapeMismatches); } /* For getters backed by a JSNative. */ - void generateNativeGetterStub(Assembler &masm, Shape *shape, + void generateNativeGetterStub(Assembler &masm, UnrootedShape shape, Label start, Vector<Jump, 8> &shapeMismatches) { AutoAssertNoGC nogc; /* * Getter hook needs to be called from the stub. The state is fully * synced and no registers are live except the result registers. */ @@ -1699,17 +1698,17 @@ class ScopeNameCompiler : public PICStub /* For GETXPROP, the object is already in objReg. */ if (pic.kind == ic::PICInfo::NAME) masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg); JS_ASSERT(obj == getprop.holder); JS_ASSERT(getprop.holder != &scopeChain->global()); - Shape *shape = getprop.shape; + UnrootedShape shape = getprop.shape; if (!shape->hasDefaultGetter()) return disable("unhandled callobj sprop getter"); LookupStatus status = walkScopeChain(masm, fails); if (status != Lookup_Cacheable) return status; /* If a scope chain walk was required, the final object needs a NULL test. */ @@ -1828,17 +1827,27 @@ class ScopeNameCompiler : public PICStub return false; return true; } RootedShape shape(cx, getprop.shape); Rooted<JSObject*> normalized(cx, obj); if (obj->isWith() && !shape->hasDefaultGetter()) normalized = &obj->asWith().object(); - NATIVE_GET(cx, normalized, holder, shape, 0, vp.address(), return false); + if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { + /* Fast path for Object instance properties. */ + JS_ASSERT(shape->slot() != SHAPE_INVALID_SLOT || !shape->hasDefaultSetter()); + if (shape->slot() != SHAPE_INVALID_SLOT) + vp.set(holder->nativeGetSlot(shape->slot())); + else + vp.setUndefined(); + } else { + if (!js_NativeGet(cx, normalized, holder, shape, 0, vp)) + return false; + } return true; } }; class BindNameCompiler : public PICStubCompiler { RootedObject scopeChain; RootedPropertyName name; @@ -2397,17 +2406,17 @@ GetElementIC::attachGetProp(VMFrame &f, masm.move(ImmPtr(holder), holderReg); typeRegHasBaseShape = false; // Guard on the holder's shape. protoGuard = masm.guardShape(holderReg, holder); } // Load the value. - Shape *shape = getprop.shape; + RootedShape shape(cx, getprop.shape); masm.loadObjProp(holder, holderReg, shape, typeReg, objReg); Jump done = masm.jump(); updatePCCounters(f, masm); PICLinker buffer(masm, *this); if (!buffer.init(cx))
--- a/js/src/methodjit/PolyIC.h +++ b/js/src/methodjit/PolyIC.h @@ -484,24 +484,24 @@ struct PICInfo : public BasePolyIC { PropertyName *name; private: Shape *inlinePathShape_; public: void purge(Repatcher &repatcher); - void setInlinePathShape(Shape *shape) { + void setInlinePathShape(UnrootedShape shape) { JS_ASSERT(!inlinePathShape_); inlinePathShape_ = shape; } - Shape *getSingleShape() { + UnrootedShape getSingleShape() { if (disabled || hadUncacheable || stubsGenerated > 0) - return NULL; + return UnrootedShape(NULL); return inlinePathShape_; } protected: // Reset the data members to the state of a fresh PIC before any patching // or stub generation was done. void reset() { BasePolyIC::reset();
--- a/js/src/methodjit/StubCalls-inl.h +++ b/js/src/methodjit/StubCalls-inl.h @@ -24,28 +24,13 @@ ThrowException(VMFrame &f) static inline void ReportAtomNotDefined(JSContext *cx, JSAtom *atom) { JSAutoByteString printable; if (js_AtomToPrintableString(cx, atom, &printable)) js_ReportIsNotDefined(cx, printable.ptr()); } -#define NATIVE_GET(cx,obj,pobj,shape,getHow,vp,onerr) \ - JS_BEGIN_MACRO \ - if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { \ - /* Fast path for Object instance properties. */ \ - JS_ASSERT((shape)->slot() != SHAPE_INVALID_SLOT || \ - !shape->hasDefaultSetter()); \ - if (((shape)->slot() != SHAPE_INVALID_SLOT)) \ - *(vp) = (pobj)->nativeGetSlot((shape)->slot()); \ - else \ - (vp)->setUndefined(); \ - } else { \ - if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp)) \ - onerr; \ - } \ - JS_END_MACRO - -}} +} /* namespace mjit */ +} /* namespace js */ #endif /* jslogic_h__ */
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -2771,18 +2771,18 @@ CopyProperty(JSContext *cx, HandleObject desc.attrs &= JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT; desc.getter = JS_PropertyStub; desc.setter = JS_StrictPropertyStub; desc.shortid = 0; } RootedValue value(cx, desc.value); objp.set(obj); - return !!DefineNativeProperty(cx, obj, id, value, desc.getter, desc.setter, - desc.attrs, propFlags, desc.shortid); + return DefineNativeProperty(cx, obj, id, value, desc.getter, desc.setter, + desc.attrs, propFlags, desc.shortid); } static JSBool resolver_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags, MutableHandleObject objp) { jsval v = JS_GetReservedSlot(obj, 0); Rooted<JSObject*> vobj(cx, &v.toObject());
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -844,17 +844,17 @@ Debugger::parseResumptionValue(Maybe<Aut if (rv.isNull()) { ac.destroy(); return JSTRAP_ERROR; } /* Check that rv is {return: val} or {throw: val}. */ JSContext *cx = ac.ref().context(); Rooted<JSObject*> obj(cx); - Shape *shape; + RootedShape shape(cx); jsid returnId = NameToId(cx->names().return_); jsid throwId = NameToId(cx->names().throw_); bool okResumption = rv.isObject(); if (okResumption) { obj = &rv.toObject(); okResumption = obj->isObject(); } if (okResumption) { @@ -864,18 +864,20 @@ Debugger::parseResumptionValue(Maybe<Aut (shape->propid() == returnId || shape->propid() == throwId) && shape->isDataDescriptor(); } if (!okResumption) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_BAD_RESUMPTION); return handleUncaughtException(ac, vp, callHook); } - if (!js_NativeGet(cx, obj, obj, shape, 0, vp) || !unwrapDebuggeeValue(cx, vp)) - return handleUncaughtException(ac, vp, callHook); + RootedValue v(cx, *vp); + if (!js_NativeGet(cx, obj, obj, shape, 0, &v) || !unwrapDebuggeeValue(cx, v.address())) + return handleUncaughtException(ac, v.address(), callHook); + *vp = v; ac.destroy(); if (!cx->compartment->wrap(cx, vp)) { vp->setUndefined(); return JSTRAP_ERROR; } return shape->propid() == returnId ? JSTRAP_RETURN : JSTRAP_THROW; }
--- a/js/src/vm/ObjectImpl-inl.h +++ b/js/src/vm/ObjectImpl-inl.h @@ -35,35 +35,35 @@ Debug_SetSlotRangeToCrashOnTouch(HeapSlo { #ifdef DEBUG Debug_SetValueRangeToCrashOnTouch((Value *) begin, end - begin); #endif } } // namespace js -inline js::Shape * +inline js::UnrootedShape js::ObjectImpl::nativeLookup(JSContext *cx, PropertyId pid) { return nativeLookup(cx, pid.asId()); } -inline js::Shape * +inline js::UnrootedShape js::ObjectImpl::nativeLookup(JSContext *cx, PropertyName *name) { return nativeLookup(cx, PropertyId(name)); } -inline js::Shape * +inline js::UnrootedShape js::ObjectImpl::nativeLookupNoAllocation(PropertyId pid) { return nativeLookupNoAllocation(pid.asId()); } -inline js::Shape * +inline js::UnrootedShape js::ObjectImpl::nativeLookupNoAllocation(PropertyName *name) { return nativeLookupNoAllocation(PropertyId(name)); } inline bool js::ObjectImpl::nativeContains(JSContext *cx, JS::Handle<jsid> id) {
--- a/js/src/vm/ObjectImpl.cpp +++ b/js/src/vm/ObjectImpl.cpp @@ -159,40 +159,40 @@ js::ObjectImpl::checkShapeConsistency() if (throttle < 0) throttle = 0; } if (throttle == 0) return; MOZ_ASSERT(isNative()); - Shape *shape = lastProperty(); - Shape *prev = NULL; + UnrootedShape shape = lastProperty(); + UnrootedShape prev = NULL; if (inDictionaryMode()) { MOZ_ASSERT(shape->hasTable()); ShapeTable &table = shape->table(); for (uint32_t fslot = table.freelist; fslot != SHAPE_INVALID_SLOT; fslot = getSlot(fslot).toPrivateUint32()) { MOZ_ASSERT(fslot < slotSpan()); } for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) { - MOZ_ASSERT_IF(shape != lastProperty(), !shape->hasTable()); + MOZ_ASSERT_IF(lastProperty() != shape, !shape->hasTable()); Shape **spp = table.search(shape->propid(), false); MOZ_ASSERT(SHAPE_FETCH(spp) == shape); } shape = lastProperty(); for (int n = throttle; --n >= 0 && shape; shape = shape->parent) { MOZ_ASSERT_IF(shape->slot() != SHAPE_INVALID_SLOT, shape->slot() < slotSpan()); if (!prev) { - MOZ_ASSERT(shape == lastProperty()); + MOZ_ASSERT(lastProperty() == shape); MOZ_ASSERT(shape->listp == &shape_); } else { MOZ_ASSERT(shape->listp == &prev->parent); } prev = shape; } } else { for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) { @@ -252,25 +252,26 @@ js::ObjectImpl::slotInRange(uint32_t slo #if defined(_MSC_VER) && _MSC_VER >= 1500 /* * Work around a compiler bug in MSVC9 and above, where inlining this function * causes stack pointer offsets to go awry and spp to refer to something higher * up the stack. */ MOZ_NEVER_INLINE #endif -Shape * -js::ObjectImpl::nativeLookup(JSContext *cx, jsid id) +UnrootedShape +js::ObjectImpl::nativeLookup(JSContext *cx, jsid idArg) { MOZ_ASSERT(isNative()); Shape **spp; + RootedId id(cx, idArg); return Shape::search(cx, lastProperty(), id, &spp); } -Shape * +UnrootedShape js::ObjectImpl::nativeLookupNoAllocation(jsid id) { MOZ_ASSERT(isNative()); return Shape::searchNoAllocation(lastProperty(), id); } void js::ObjectImpl::markChildren(JSTracer *trc) @@ -498,18 +499,21 @@ js::GetOwnProperty(JSContext *cx, Handle Rooted<PropertyId> pid(cx, pid_); if (static_cast<JSObject *>(obj.get())->isProxy()) { MOZ_NOT_REACHED("NYI: proxy [[GetOwnProperty]]"); return false; } - Shape *shape = obj->nativeLookup(cx, pid); + /* |shape| is always set /after/ a GC. */ + UnrootedShape shape = obj->nativeLookup(cx, pid); if (!shape) { + DropUnrooted(shape); + /* Not found: attempt to resolve it. */ Class *clasp = obj->getClass(); JSResolveOp resolve = clasp->resolve; if (resolve != JS_ResolveStub) { Rooted<jsid> id(cx, pid.get().asId()); Rooted<JSObject*> robj(cx, static_cast<JSObject*>(obj.get())); if (clasp->flags & JSCLASS_NEW_RESOLVE) { Rooted<JSObject*> obj2(cx, NULL);
--- a/js/src/vm/ObjectImpl.h +++ b/js/src/vm/ObjectImpl.h @@ -19,16 +19,17 @@ #include "gc/Heap.h" #include "vm/NumericConversions.h" #include "vm/String.h" namespace js { class Debugger; class ObjectImpl; +ForwardDeclare(Shape); class AutoPropDescArrayRooter; static inline PropertyOp CastAsPropertyOp(JSObject *object) { return JS_DATA_TO_FUNC_PTR(PropertyOp, object); } @@ -353,17 +354,17 @@ class ElementsHeader union { class { friend class DenseElementsHeader; uint32_t initializedLength; uint32_t capacity; } dense; class { friend class SparseElementsHeader; - Shape * shape; + RawShape shape; } sparse; class { friend class ArrayBufferElementsHeader; JSObject * views; } buffer; }; void staticAsserts() { @@ -444,17 +445,17 @@ class DenseElementsHeader : public Eleme DenseElementsHeader(const DenseElementsHeader &other) MOZ_DELETE; void operator=(const DenseElementsHeader &other) MOZ_DELETE; }; class SparseElementsHeader : public ElementsHeader { public: - Shape * shape() { + UnrootedShape shape() { MOZ_ASSERT(ElementsHeader::isSparseElements()); return sparse.shape; } uint32_t length() const { MOZ_ASSERT(ElementsHeader::isSparseElements()); return ElementsHeader::length; } @@ -1150,23 +1151,23 @@ class ObjectImpl : public gc::Cell */ bool hasLazyType() const { return type_->lazy(); } inline uint32_t slotSpan() const; /* Compute dynamicSlotsCount() for this object. */ inline uint32_t numDynamicSlots() const; - Shape * nativeLookup(JSContext *cx, jsid id); - inline Shape * nativeLookup(JSContext *cx, PropertyId pid); - inline Shape * nativeLookup(JSContext *cx, PropertyName *name); + UnrootedShape nativeLookup(JSContext *cx, jsid id); + inline UnrootedShape nativeLookup(JSContext *cx, PropertyId pid); + inline UnrootedShape nativeLookup(JSContext *cx, PropertyName *name); - Shape * nativeLookupNoAllocation(jsid id); - inline Shape * nativeLookupNoAllocation(PropertyId pid); - inline Shape * nativeLookupNoAllocation(PropertyName *name); + UnrootedShape nativeLookupNoAllocation(jsid id); + inline UnrootedShape nativeLookupNoAllocation(PropertyId pid); + inline UnrootedShape nativeLookupNoAllocation(PropertyName *name); inline bool nativeContains(JSContext *cx, Handle<jsid> id); inline bool nativeContains(JSContext *cx, Handle<PropertyName*> name); inline bool nativeContains(JSContext *cx, Handle<Shape*> shape); inline bool nativeContainsNoAllocation(jsid id); inline bool nativeContainsNoAllocation(PropertyName *name); inline bool nativeContainsNoAllocation(Shape &shape);
--- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -317,69 +317,63 @@ RegExpObject::createShared(JSContext *cx JS_ASSERT(!maybeShared()); if (!cx->compartment->regExps.get(cx, getSource(), getFlags(), g)) return false; self->setShared(cx, **g); return true; } -Shape * +UnrootedShape RegExpObject::assignInitialShape(JSContext *cx) { JS_ASSERT(isRegExp()); JS_ASSERT(nativeEmpty()); JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0); JS_STATIC_ASSERT(SOURCE_SLOT == LAST_INDEX_SLOT + 1); JS_STATIC_ASSERT(GLOBAL_FLAG_SLOT == SOURCE_SLOT + 1); JS_STATIC_ASSERT(IGNORE_CASE_FLAG_SLOT == GLOBAL_FLAG_SLOT + 1); JS_STATIC_ASSERT(MULTILINE_FLAG_SLOT == IGNORE_CASE_FLAG_SLOT + 1); JS_STATIC_ASSERT(STICKY_FLAG_SLOT == MULTILINE_FLAG_SLOT + 1); RootedObject self(cx, this); /* The lastIndex property alone is writable but non-configurable. */ - if (!addDataProperty(cx, NameToId(cx->names().lastIndex), - LAST_INDEX_SLOT, JSPROP_PERMANENT)) - { - return NULL; - } + if (!addDataProperty(cx, NameToId(cx->names().lastIndex), LAST_INDEX_SLOT, JSPROP_PERMANENT)) + return UnrootedShape(NULL); /* Remaining instance properties are non-writable and non-configurable. */ - if (!self->addDataProperty(cx, NameToId(cx->names().source), - SOURCE_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) || - !self->addDataProperty(cx, NameToId(cx->names().global), - GLOBAL_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) || - !self->addDataProperty(cx, NameToId(cx->names().ignoreCase), - IGNORE_CASE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) || - !self->addDataProperty(cx, NameToId(cx->names().multiline), - MULTILINE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY)) - { - return NULL; - } - - return self->addDataProperty(cx, NameToId(cx->names().sticky), - STICKY_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY); + unsigned attrs = JSPROP_PERMANENT | JSPROP_READONLY; + if (!self->addDataProperty(cx, NameToId(cx->names().source), SOURCE_SLOT, attrs)) + return UnrootedShape(NULL); + if (!self->addDataProperty(cx, NameToId(cx->names().global), GLOBAL_FLAG_SLOT, attrs)) + return UnrootedShape(NULL); + if (!self->addDataProperty(cx, NameToId(cx->names().ignoreCase), IGNORE_CASE_FLAG_SLOT, attrs)) + return UnrootedShape(NULL); + if (!self->addDataProperty(cx, NameToId(cx->names().multiline), MULTILINE_FLAG_SLOT, attrs)) + return UnrootedShape(NULL); + return self->addDataProperty(cx, NameToId(cx->names().sticky), STICKY_FLAG_SLOT, attrs); } inline bool RegExpObject::init(JSContext *cx, HandleAtom source, RegExpFlag flags) { Rooted<RegExpObject *> self(cx, this); if (nativeEmpty()) { if (isDelegate()) { if (!assignInitialShape(cx)) return false; } else { - Shape *shape = assignInitialShape(cx); + RootedShape shape(cx, assignInitialShape(cx)); if (!shape) return false; - EmptyShape::insertInitialShape(cx, shape, self->getProto()); + RootedObject proto(cx, self->getProto()); + EmptyShape::insertInitialShape(cx, shape, proto); } JS_ASSERT(!self->nativeEmpty()); } JS_ASSERT(self->nativeLookupNoAllocation(NameToId(cx->names().lastIndex))->slot() == LAST_INDEX_SLOT); JS_ASSERT(self->nativeLookupNoAllocation(NameToId(cx->names().source))->slot() == SOURCE_SLOT);
--- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -400,17 +400,17 @@ class RegExpObject : public JSObject private: friend class RegExpObjectBuilder; /* * Compute the initial shape to associate with fresh RegExp objects, * encoding their initial properties. Return the shape after * changing this regular expression object's last property to it. */ - Shape *assignInitialShape(JSContext *cx); + UnrootedShape assignInitialShape(JSContext *cx); inline bool init(JSContext *cx, HandleAtom source, RegExpFlag flags); /* * Precondition: the syntax for |source| has already been validated. * Side effect: sets the private field. */ bool createShared(JSContext *cx, RegExpGuard *g);
--- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -53,22 +53,24 @@ StaticScopeIter::operator++(int) bool StaticScopeIter::hasDynamicScopeObject() const { return obj->isStaticBlock() ? obj->asStaticBlock().needsClone() : obj->toFunction()->isHeavyweight(); } -Shape * +UnrootedShape StaticScopeIter::scopeShape() const { JS_ASSERT(hasDynamicScopeObject()); JS_ASSERT(type() != NAMED_LAMBDA); - return type() == BLOCK ? block().lastProperty() : funScript()->bindings.callObjShape(); + return type() == BLOCK + ? UnrootedShape(block().lastProperty()) + : funScript()->bindings.callObjShape(); } StaticScopeIter::Type StaticScopeIter::type() const { if (onNamedLambda) return NAMED_LAMBDA; return obj->isStaticBlock() ? BLOCK : FUNCTION; @@ -298,18 +300,18 @@ DeclEnvObject::createTemplateObject(JSCo RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, emptyDeclEnvShape, type, NULL)); if (!obj) return NULL; // Assign a fixed slot to a property with the same name as the lambda. Rooted<jsid> id(cx, AtomToId(fun->atom())); Class *clasp = obj->getClass(); unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY; - if (!obj->putProperty(cx, id, clasp->getProperty, clasp->setProperty, - lambdaSlot(), attrs, 0, 0)) + if (!JSObject::putProperty(cx, obj, id, clasp->getProperty, clasp->setProperty, + lambdaSlot(), attrs, 0, 0)) { return NULL; } JS_ASSERT(!obj->hasDynamicSlots()); return &obj->asDeclEnv(); } @@ -684,40 +686,40 @@ StaticBlockObject::create(JSContext *cx) JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyBlockShape, type, NULL); if (!obj) return NULL; return &obj->asStaticBlock(); } -/* static */ Shape * +/* static */ UnrootedShape StaticBlockObject::addVar(JSContext *cx, Handle<StaticBlockObject*> block, HandleId id, int index, bool *redeclared) { JS_ASSERT(JSID_IS_ATOM(id) || (JSID_IS_INT(id) && JSID_TO_INT(id) == index)); *redeclared = false; /* Inline JSObject::addProperty in order to trap the redefinition case. */ Shape **spp; if (Shape::search(cx, block->lastProperty(), id, &spp, true)) { *redeclared = true; - return NULL; + return UnrootedShape(NULL); } /* * Don't convert this object to dictionary mode so that we can clone the * block's shape later. */ uint32_t slot = JSSLOT_FREE(&BlockClass) + index; - return block->addPropertyInternal(cx, id, /* getter = */ NULL, /* setter = */ NULL, - slot, JSPROP_ENUMERATE | JSPROP_PERMANENT, - Shape::HAS_SHORTID, index, spp, - /* allowDictionary = */ false); + return JSObject::addPropertyInternal(cx, block, id, /* getter = */ NULL, /* setter = */ NULL, + slot, JSPROP_ENUMERATE | JSPROP_PERMANENT, + Shape::HAS_SHORTID, index, spp, + /* allowDictionary = */ false); } Class js::BlockClass = { "Block", JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(BlockObject::RESERVED_SLOTS) | JSCLASS_IS_ANONYMOUS, JS_PropertyStub, /* addProperty */ @@ -795,36 +797,39 @@ js::XDRStaticBlockObject(XDRState<mode> obj->setAliased(i, !!aliased); } } else { AutoShapeVector shapes(cx); if (!shapes.growBy(count)) return false; for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) { - Shape *shape = &r.front(); + UnrootedShape shape = &r.front(); shapes[shape->shortid()] = shape; } /* * XDR the block object's properties. We know that there are 'count' * properties to XDR, stored as id/shortid pairs. */ + RootedShape shape(cx); + RootedId propid(cx); + RootedAtom atom(cx); for (unsigned i = 0; i < count; i++) { - Shape *shape = shapes[i]; + shape = shapes[i]; JS_ASSERT(shape->hasDefaultGetter()); JS_ASSERT(unsigned(shape->shortid()) == i); - jsid propid = shape->propid(); + propid = shape->propid(); JS_ASSERT(JSID_IS_ATOM(propid) || JSID_IS_INT(propid)); /* The empty string indicates an int id. */ - RootedAtom atom(cx, JSID_IS_ATOM(propid) - ? JSID_TO_ATOM(propid) - : cx->runtime->emptyString); + atom = JSID_IS_ATOM(propid) + ? JSID_TO_ATOM(propid) + : cx->runtime->emptyString; if (!XDRAtom(xdr, &atom)) return false; uint32_t aliased = obj->isAliased(i); if (!xdr->codeUint32(&aliased)) return false; } } @@ -1217,17 +1222,17 @@ class DebugScopeProxy : public BaseProxy } return true; } /* Handle unaliased let and catch bindings at block scope. */ if (scope->isClonedBlock()) { ClonedBlockObject &block = scope->asClonedBlock(); - Shape *shape = block.lastProperty()->search(cx, id); + UnrootedShape shape = block.lastProperty()->search(cx, id); if (!shape) return false; AutoAssertNoGC nogc; unsigned i = shape->shortid(); if (block.staticBlock().isAliased(i)) return false;
--- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -60,17 +60,17 @@ class StaticScopeIter public: explicit StaticScopeIter(JSObject *obj); bool done() const; void operator++(int); /* Return whether this static scope will be on the dynamic scope chain. */ bool hasDynamicScopeObject() const; - Shape *scopeShape() const; + UnrootedShape scopeShape() const; enum Type { BLOCK, FUNCTION, NAMED_LAMBDA }; Type type() const; StaticBlockObject &block() const; UnrootedScript funScript() const; }; @@ -341,18 +341,18 @@ class StaticBlockObject : public BlockOb * The parser uses 'enclosingBlock' as the prev-link in the pc->blockChain * stack. Note: in the case of hoisting, this prev-link will not ultimately * be the same as enclosingBlock, initEnclosingStaticScope must be called * separately in the emitter. 'reset' is just for asserting stackiness. */ void initPrevBlockChainFromParser(StaticBlockObject *prev); void resetPrevBlockChainFromParser(); - static Shape *addVar(JSContext *cx, Handle<StaticBlockObject*> block, HandleId id, - int index, bool *redeclared); + static UnrootedShape addVar(JSContext *cx, Handle<StaticBlockObject*> block, HandleId id, + int index, bool *redeclared); }; class ClonedBlockObject : public BlockObject { public: static ClonedBlockObject *create(JSContext *cx, Handle<StaticBlockObject *> block, StackFrame *fp);
--- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -850,50 +850,55 @@ class AutoNameVector : public AutoVector } /* namespace js */ /* Avoid requiring vm/String-inl.h just to call getChars. */ JS_ALWAYS_INLINE const jschar * JSString::getChars(JSContext *cx) { + JS::AutoAssertNoGC nogc; if (JSLinearString *str = ensureLinear(cx)) return str->chars(); return NULL; } JS_ALWAYS_INLINE const jschar * JSString::getCharsZ(JSContext *cx) { + JS::AutoAssertNoGC nogc; if (JSFlatString *str = ensureFlat(cx)) return str->chars(); return NULL; } JS_ALWAYS_INLINE JSLinearString * JSString::ensureLinear(JSContext *cx) { + JS::AutoAssertNoGC nogc; return isLinear() ? &asLinear() : asRope().flatten(cx); } JS_ALWAYS_INLINE JSFlatString * JSString::ensureFlat(JSContext *cx) { + JS::AutoAssertNoGC nogc; return isFlat() ? &asFlat() : isDependent() ? asDependent().undepend(cx) : asRope().flatten(cx); } JS_ALWAYS_INLINE JSStableString * JSString::ensureStable(JSContext *maybecx) { + JS::AutoAssertNoGC nogc; if (isRope()) { JSFlatString *flat = asRope().flatten(maybecx); if (!flat) return NULL; JS_ASSERT(!flat->isInline()); return &flat->asStable(); }
--- a/js/src/vm/StringObject-inl.h +++ b/js/src/vm/StringObject-inl.h @@ -17,29 +17,31 @@ JSObject::asString() return *static_cast<js::StringObject *>(this); } namespace js { inline bool StringObject::init(JSContext *cx, HandleString str) { + AssertCanGC(); JS_ASSERT(gc::GetGCKindSlots(getAllocKind()) == 2); Rooted<StringObject *> self(cx, this); if (nativeEmpty()) { if (isDelegate()) { if (!assignInitialShape(cx)) return false; } else { - Shape *shape = assignInitialShape(cx); + RootedShape shape(cx, assignInitialShape(cx)); if (!shape) return false; - EmptyShape::insertInitialShape(cx, shape, self->getProto()); + RootedObject proto(cx, self->getProto()); + EmptyShape::insertInitialShape(cx, shape, proto); } } JS_ASSERT(self->nativeLookupNoAllocation(NameToId(cx->names().length))->slot() == LENGTH_SLOT); self->setStringThis(str); return true;
--- a/js/src/vm/StringObject.h +++ b/js/src/vm/StringObject.h @@ -61,14 +61,14 @@ class StringObject : public JSObject friend JSObject * ::js_InitStringClass(JSContext *cx, js::HandleObject global); /* * Compute the initial shape to associate with fresh String objects, which * encodes the initial length property. Return the shape after changing * this String object's last property to it. */ - Shape *assignInitialShape(JSContext *cx); + UnrootedShape assignInitialShape(JSContext *cx); }; } // namespace js #endif /* StringObject_h__ */