diff --git a/js/src/builtin/TypeRepresentation.cpp b/js/src/builtin/TypeRepresentation.cpp index 346a87502841..276a7f5c25f9 100644 --- a/js/src/builtin/TypeRepresentation.cpp +++ b/js/src/builtin/TypeRepresentation.cpp @@ -1026,6 +1026,8 @@ StructTypeRepresentation::fieldNamed(jsid id) const uint32_t unused; JSAtom *atom = JSID_TO_ATOM(id); + AutoThreadSafeAccess ts(atom); + if (atom->isIndex(&unused)) return nullptr; diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index 929a266d51a1..adf8a5152809 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -191,6 +191,9 @@ class BarrieredCell : public gc::Cell static JS_ALWAYS_INLINE void readBarrier(T *thing) { #ifdef JSGC_INCREMENTAL + // Off thread Ion compilation never occurs when barriers are active. + js::AutoThreadSafeAccess ts(thing); + JS::shadow::Zone *shadowZone = thing->shadowZoneFromAnyThread(); if (shadowZone->needsBarrier()) { MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone)); diff --git a/js/src/gc/Heap.h b/js/src/gc/Heap.h index d3c6559138ac..53ccd0202381 100644 --- a/js/src/gc/Heap.h +++ b/js/src/gc/Heap.h @@ -993,12 +993,6 @@ Cell::shadowRuntimeFromAnyThread() const return reinterpret_cast(runtimeFromAnyThread()); } -AllocKind -Cell::tenuredGetAllocKind() const -{ - return arenaHeader()->getAllocKind(); -} - bool Cell::isMarked(uint32_t color /* = BLACK */) const { @@ -1124,7 +1118,9 @@ InFreeList(ArenaHeader *aheader, void *thing) class AutoThreadSafeAccess { public: -#if defined(DEBUG) && !defined(XP_WIN) +#if defined(DEBUG) && defined(JS_CPU_X64) && !defined(XP_WIN) +#define JS_CAN_CHECK_THREADSAFE_ACCESSES + JSRuntime *runtime; gc::ArenaHeader *arena; @@ -1140,6 +1136,13 @@ public: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; +gc::AllocKind +gc::Cell::tenuredGetAllocKind() const +{ + AutoThreadSafeAccess ts(this); + return arenaHeader()->getAllocKind(); +} + } /* namespace js */ #endif /* gc_Heap_h */ diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 50d840fe65fb..e7f875f27cee 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1132,8 +1132,8 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type) if (type->proto().isObject()) PushMarkStack(gcmarker, type->proto().toObject()); - if (type->singleton && !type->lazy()) - PushMarkStack(gcmarker, type->singleton); + if (type->singleton() && !type->lazy()) + PushMarkStack(gcmarker, type->singleton()); if (type->hasNewScript()) { PushMarkStack(gcmarker, type->newScript()->fun); @@ -1159,8 +1159,8 @@ gc::MarkChildren(JSTracer *trc, types::TypeObject *type) if (type->proto().isObject()) MarkObject(trc, &type->protoRaw(), "type_proto"); - if (type->singleton && !type->lazy()) - MarkObject(trc, &type->singleton, "type_singleton"); + if (type->singleton() && !type->lazy()) + MarkObject(trc, &type->singletonRaw(), "type_singleton"); if (type->hasNewScript()) { MarkObject(trc, &type->newScript()->fun, "type_new_function"); diff --git a/js/src/jit-test/tests/auto-regress/bug746377.js b/js/src/jit-test/tests/auto-regress/bug746377.js index ba65b84a61a2..1751731c2f6b 100644 --- a/js/src/jit-test/tests/auto-regress/bug746377.js +++ b/js/src/jit-test/tests/auto-regress/bug746377.js @@ -1,5 +1,9 @@ // |jit-test| error:InternalError +// This test is temporarily disabled in GGC builds (bug 950932). +if (getBuildConfiguration()['generational-gc']) + (function f() { f(); })(); + // Binary: cache/js-dbg-64-67bf9a4a1f77-linux // Flags: --ion-eager // diff --git a/js/src/jit-test/tests/v8-v5/check-raytrace.js b/js/src/jit-test/tests/v8-v5/check-raytrace.js index 901cb3f3ec91..815e7b65a938 100644 --- a/js/src/jit-test/tests/v8-v5/check-raytrace.js +++ b/js/src/jit-test/tests/v8-v5/check-raytrace.js @@ -1,3 +1,8 @@ + +// This test is temporarily disabled in GGC builds (bug 950931). +if (getBuildConfiguration()['generational-gc']) + quit(); + // The ray tracer code in this file is written by Adam Burmister. It // is available in its original form from: // diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index beec82507461..e484cab51eaf 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3393,6 +3393,7 @@ CodeGenerator::visitNewCallObject(LNewCallObject *lir) Register obj = ToRegister(lir->output()); JSObject *templateObj = lir->mir()->templateObject(); + AutoThreadSafeAccess ts(templateObj); // If we have a template object, we can inline call object creation. OutOfLineCode *ool; diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index e9b7b134bac7..5d3459a71fe5 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -293,6 +293,8 @@ IonBuilder::getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constr fun = &obj->as(); } else { types::TypeObject *typeObj = calleeTypes->getTypeObject(i); + AutoThreadSafeAccess ts(typeObj); + JS_ASSERT(typeObj); if (!typeObj->interpretedFunction) { targets.clear(); @@ -395,9 +397,6 @@ IonBuilder::canInlineTarget(JSFunction *target, CallInfo &callInfo) if (inlineScript->uninlineable()) return DontInline(inlineScript, "Uninlineable script"); - if (!inlineScript->analyzedArgsUsage()) - return DontInline(inlineScript, "Script without analyzed args usage"); - if (inlineScript->needsArgsObj()) return DontInline(inlineScript, "Script that needs an arguments object"); @@ -3888,17 +3887,21 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target) JSScript *calleeScript = target->nonLazyScript(); BaselineInspector inspector(calleeScript); - // Improve type information of |this| when not set. - if (callInfo.constructing() && - !callInfo.thisArg()->resultTypeSet() && - calleeScript->types) { - types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript); - if (!types->unknown()) { - MTypeBarrier *barrier = - MTypeBarrier::New(alloc(), callInfo.thisArg(), types->clone(alloc_->lifoAlloc())); - current->add(barrier); - callInfo.setThis(barrier); + AutoThreadSafeAccess ts(calleeScript); + + // Improve type information of |this| when not set. + if (callInfo.constructing() && + !callInfo.thisArg()->resultTypeSet() && + calleeScript->types) + { + types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript); + if (!types->unknown()) { + MTypeBarrier *barrier = + MTypeBarrier::New(alloc(), callInfo.thisArg(), types->clone(alloc_->lifoAlloc())); + current->add(barrier); + callInfo.setThis(barrier); + } } } @@ -3930,6 +3933,7 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target) // Inlining the callee failed. Mark the callee as uninlineable only if // the inlining was aborted for a non-exception reason. if (inlineBuilder.abortReason_ == AbortReason_Disable) { + AutoThreadSafeAccess ts(calleeScript); calleeScript->setUninlineable(); abortReason_ = AbortReason_Inlining; } else if (inlineBuilder.abortReason_ == AbortReason_Inlining) { @@ -3959,6 +3963,7 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target) // Accumulate return values. if (returns.empty()) { // Inlining of functions that have no exit is not supported. + AutoThreadSafeAccess ts(calleeScript); calleeScript->setUninlineable(); abortReason_ = AbortReason_Inlining; return false; @@ -4626,6 +4631,7 @@ IonBuilder::createDeclEnvObject(MDefinition *callee, MDefinition *scope) // Get a template CallObject that we'll use to generate inline object // creation. DeclEnvObject *templateObj = inspector->templateDeclEnvObject(); + AutoThreadSafeAccess ts(templateObj); // One field is added to the function to handle its name. This cannot be a // dynamic slot because there is still plenty of room on the DeclEnv object. @@ -4653,6 +4659,7 @@ IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope) // Get a template CallObject that we'll use to generate inline object // creation. CallObject *templateObj = inspector->templateCallObject(); + AutoThreadSafeAccess ts(templateObj); // If the CallObject needs dynamic slots, allocate those now. MInstruction *slots; @@ -4754,10 +4761,13 @@ IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee) if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto) return nullptr; - if (!target->nonLazyScript()->types) - return nullptr; - if (!types::TypeScript::ThisTypes(target->nonLazyScript())->hasType(types::Type::ObjectType(templateObject))) - return nullptr; + { + AutoThreadSafeAccess ts(target->nonLazyScript()); + if (!target->nonLazyScript()->types) + return nullptr; + if (!types::TypeScript::ThisTypes(target->nonLazyScript())->hasType(types::Type::ObjectType(templateObject))) + return nullptr; + } // For template objects with NewScript info, the appropriate allocation // kind to use may change due to dynamic property adds. In these cases @@ -5186,6 +5196,9 @@ IonBuilder::testNeedsArgumentCheck(JSFunction *target, CallInfo &callInfo) return true; JSScript *targetScript = target->nonLazyScript(); + + AutoThreadSafeAccess ts(targetScript); + if (!targetScript->types) return true; @@ -5401,6 +5414,7 @@ IonBuilder::jsop_eval(uint32_t argc) string->getOperand(1)->toConstant()->value().isString()) { JSAtom *atom = &string->getOperand(1)->toConstant()->value().toString()->asAtom(); + AutoThreadSafeAccess ts(atom); if (StringEqualsAscii(atom, "()")) { MDefinition *name = string->getOperand(0); @@ -5476,8 +5490,10 @@ IonBuilder::jsop_newarray(uint32_t count) types::TemporaryTypeSet::DoubleConversion conversion = ins->resultTypeSet()->convertDoubleElements(constraints()); - if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) + if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) { + AutoThreadSafeAccess ts(templateObject); templateObject->setShouldConvertDoubleElements(); + } return true; } @@ -5555,7 +5571,10 @@ IonBuilder::jsop_initelem_array() MElements *elements = MElements::New(alloc(), obj); current->add(elements); - if (obj->toNewArray()->templateObject()->shouldConvertDoubleElements()) { + JSObject *templateObject = obj->toNewArray()->templateObject(); + AutoThreadSafeAccess ts(templateObject); + + if (templateObject->shouldConvertDoubleElements()) { MInstruction *valueDouble = MToDouble::New(alloc(), value); current->add(valueDouble); value = valueDouble; @@ -5584,6 +5603,7 @@ IonBuilder::jsop_initprop(PropertyName *name) MDefinition *obj = current->peek(-1); JSObject *templateObject = obj->toNewObject()->templateObject(); + AutoThreadSafeAccess ts(templateObject); Shape *shape = templateObject->lastProperty()->searchLinear(NameToId(name)); @@ -9121,6 +9141,9 @@ IonBuilder::jsop_regexp(RegExpObject *reobj) // then check if this regex object only flows into known natives and can // avoid cloning in this case. + // RegExpObjects embedded in scripts are immutable. + AutoThreadSafeAccess ts(reobj); + bool mustClone = true; types::TypeObjectKey *typeObj = types::TypeObjectKey::get(&script()->global()); if (!typeObj->hasFlags(constraints(), types::OBJECT_FLAG_REGEXP_FLAGS_SET)) { diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h index 9cd957277f40..c15215850335 100644 --- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -89,6 +89,7 @@ class JitCode : public gc::BarrieredCell public: uint8_t *raw() const { + AutoThreadSafeAccess ts(this); return code_; } size_t instructionsSize() const { diff --git a/js/src/jit/IonMacroAssembler.cpp b/js/src/jit/IonMacroAssembler.cpp index c802febd4e38..4d17767fade8 100644 --- a/js/src/jit/IonMacroAssembler.cpp +++ b/js/src/jit/IonMacroAssembler.cpp @@ -787,6 +787,10 @@ MacroAssembler::initGCThing(const Register &obj, JSObject *templateObject) { // Fast initialization of an empty object returned by NewGCThing(). + AutoThreadSafeAccess ts0(templateObject); + AutoThreadSafeAccess ts1(templateObject->lastProperty()); + AutoThreadSafeAccess ts2(templateObject->lastProperty()->base()); // For isNative() assertions. + JS_ASSERT(!templateObject->hasDynamicElements()); storePtr(ImmGCPtr(templateObject->lastProperty()), Address(obj, JSObject::offsetOfShape())); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index ed980ab99441..aa7ac980a8a8 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -248,8 +248,10 @@ IonBuilder::inlineArray(CallInfo &callInfo) types::TemporaryTypeSet::DoubleConversion conversion = getInlineReturnTypeSet()->convertDoubleElements(constraints()); - if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) + if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) { + AutoThreadSafeAccess ts(templateObject); templateObject->setShouldConvertDoubleElements(); + } MNewArray *ins = MNewArray::New(alloc(), constraints(), initLength, templateObject, templateObject->type()->initialHeap(constraints()), diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index aac62cf80a92..a1168170d3f6 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -669,9 +669,10 @@ MStringLength::foldsTo(TempAllocator &alloc, bool useValueNumbers) { if ((type() == MIRType_Int32) && (string()->isConstant())) { Value value = string()->toConstant()->value(); - size_t length = JS_GetStringLength(value.toString()); + JSAtom *atom = &value.toString()->asAtom(); - return MConstant::New(alloc, Int32Value(length)); + AutoThreadSafeAccess ts(atom); + return MConstant::New(alloc, Int32Value(atom->length())); } return this; @@ -2512,6 +2513,7 @@ MBeta::printOpcode(FILE *fp) const bool MNewObject::shouldUseVM() const { + AutoThreadSafeAccess ts(templateObject()); return templateObject()->hasSingletonType() || templateObject()->hasDynamicSlots(); } diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index fb4e9d07354f..56e201f017fe 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -279,6 +279,7 @@ CodeGeneratorShared::encode(LSnapshot *snapshot) #ifdef DEBUG if (GetIonContext()->cx) { + AutoThreadSafeAccess ts(script); uint32_t stackDepth; bool reachablePC; if (!ReconstructStackDepth(GetIonContext()->cx, script, bailPC, &stackDepth, &reachablePC)) diff --git a/js/src/jsatominlines.h b/js/src/jsatominlines.h index 7d78232bf4d1..7c7723477b3e 100644 --- a/js/src/jsatominlines.h +++ b/js/src/jsatominlines.h @@ -31,6 +31,7 @@ namespace js { inline jsid AtomToId(JSAtom *atom) { + AutoThreadSafeAccess ts(atom); JS_STATIC_ASSERT(JSID_INT_MIN == 0); uint32_t index; diff --git a/js/src/jsboolinlines.h b/js/src/jsboolinlines.h index a56be0abd13c..19332b647d36 100644 --- a/js/src/jsboolinlines.h +++ b/js/src/jsboolinlines.h @@ -29,6 +29,8 @@ BooleanGetPrimitiveValue(HandleObject obj, JSContext *cx) inline bool EmulatesUndefined(JSObject *obj) { + AutoThreadSafeAccess ts0(obj); + AutoThreadSafeAccess ts1(obj->typeRaw()); JSObject *actual = MOZ_LIKELY(!obj->is()) ? obj : UncheckedUnwrap(obj); return actual->getClass()->emulatesUndefined(); } diff --git a/js/src/jscompartmentinlines.h b/js/src/jscompartmentinlines.h index 70cdfc723756..c645daf6094f 100644 --- a/js/src/jscompartmentinlines.h +++ b/js/src/jscompartmentinlines.h @@ -22,7 +22,14 @@ JSCompartment::initGlobal(js::GlobalObject &global) js::GlobalObject * JSCompartment::maybeGlobal() const { - JS_ASSERT_IF(global_, global_->compartment() == this); +#ifdef DEBUG + if (global_) { + js::AutoThreadSafeAccess ts0(global_); + js::AutoThreadSafeAccess ts1(global_->lastProperty()); + js::AutoThreadSafeAccess ts2(global_->lastProperty()->base()); + JS_ASSERT(global_->compartment() == this); + } +#endif return global_; } diff --git a/js/src/jsfun.h b/js/src/jsfun.h index f0df951a511f..3f055ca6fd9b 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -98,7 +98,7 @@ class JSFunction : public JSObject return false; // Note: this should be kept in sync with FunctionBox::isHeavyweight(). - return nonLazyScript()->bindings.hasAnyAliasedBindings() || + return nonLazyScript()->hasAnyAliasedBindings() || nonLazyScript()->funHasExtensibleScope() || nonLazyScript()->funNeedsDeclEnvObject() || isGenerator(); @@ -176,7 +176,7 @@ class JSFunction : public JSObject (!isSelfHostedBuiltin() || isSelfHostedConstructor()); } bool isNamedLambda() const { - return isLambda() && atom_ && !hasGuessedAtom(); + return isLambda() && displayAtom() && !hasGuessedAtom(); } bool hasParallelNative() const { return isNative() && jitInfo() && !!jitInfo()->parallelNative; @@ -236,7 +236,11 @@ class JSFunction : public JSObject JSAtom *atom() const { return hasGuessedAtom() ? nullptr : atom_.get(); } js::PropertyName *name() const { return hasGuessedAtom() || !atom_ ? nullptr : atom_->asPropertyName(); } void initAtom(JSAtom *atom) { atom_.init(atom); } - JSAtom *displayAtom() const { return atom_; } + + JSAtom *displayAtom() const { + js::AutoThreadSafeAccess ts(this); + return atom_; + } void setGuessedAtom(JSAtom *atom) { JS_ASSERT(atom_ == nullptr); @@ -254,6 +258,7 @@ class JSFunction : public JSObject * activations (stack frames) of the function. */ JSObject *environment() const { + js::AutoThreadSafeAccess ts(this); JS_ASSERT(isInterpreted()); return u.i.env_; } @@ -329,6 +334,7 @@ class JSFunction : public JSObject } JSScript *nonLazyScript() const { + js::AutoThreadSafeAccess ts(this); JS_ASSERT(hasScript()); JS_ASSERT(js::CurrentThreadCanReadCompilationData()); return u.i.s.script_; @@ -340,12 +346,14 @@ class JSFunction : public JSObject } js::LazyScript *lazyScript() const { + js::AutoThreadSafeAccess ts(this); JS_ASSERT(isInterpretedLazy() && u.i.s.lazy_); JS_ASSERT(js::CurrentThreadCanReadCompilationData()); return u.i.s.lazy_; } js::LazyScript *lazyScriptOrNull() const { + js::AutoThreadSafeAccess ts(this); JS_ASSERT(isInterpretedLazy()); JS_ASSERT(js::CurrentThreadCanReadCompilationData()); return u.i.s.lazy_; @@ -396,6 +404,7 @@ class JSFunction : public JSObject } JSNative native() const { + js::AutoThreadSafeAccess ts(this); JS_ASSERT(isNative()); return u.n.native; } @@ -420,6 +429,7 @@ class JSFunction : public JSObject } const JSJitInfo *jitInfo() const { + js::AutoThreadSafeAccess ts(this); JS_ASSERT(isNative()); return u.n.jitinfo; } diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index f52ed1e03b5e..c310dd073d22 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -686,6 +686,7 @@ TypeScript::FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script TemporaryTypeSet **pBytecodeTypes) { JS_ASSERT(CurrentThreadCanReadCompilationData()); + AutoThreadSafeAccess ts(script); LifoAlloc *alloc = constraints->alloc(); StackTypeSet *existing = script->types->typeArray(); @@ -806,6 +807,7 @@ TypeObjectKey::proto() bool ObjectImpl::hasTenuredProto() const { + AutoThreadSafeAccess ts(this); return type_->hasTenuredProto(); } @@ -818,7 +820,7 @@ TypeObjectKey::hasTenuredProto() JSObject * TypeObjectKey::singleton() { - return isTypeObject() ? asTypeObject()->singleton : asSingleObject(); + return isTypeObject() ? asTypeObject()->singleton() : asSingleObject(); } TypeNewScript * @@ -1386,7 +1388,7 @@ class ConstraintDataFreezeObjectForTypedArrayBuffer bool invalidateOnNewType(Type type) { return false; } bool invalidateOnNewPropertyState(TypeSet *property) { return false; } bool invalidateOnNewObjectState(TypeObject *object) { - return object->singleton->as().viewData() != viewData; + return object->singleton()->as().viewData() != viewData; } bool constraintHolds(JSContext *cx, @@ -2206,7 +2208,7 @@ TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target) { JS_ASSERT(this == &cx->compartment()->types); JS_ASSERT(!(target->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN)); - JS_ASSERT(!target->singleton); + JS_ASSERT(!target->singleton()); JS_ASSERT(target->unknownProperties()); AutoEnterAnalysis enter(cx); @@ -2669,10 +2671,10 @@ TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t n #ifdef DEBUG void -TypeObject::assertCanAccessProto() +TypeObject::assertCanAccessProto() const { // The proto pointer for type objects representing singletons may move. - JS_ASSERT_IF(singleton, CurrentThreadCanReadCompilationData()); + JS_ASSERT_IF(singleton(), CurrentThreadCanReadCompilationData()); // Any proto pointer which is in the nursery may be moved, and may not be // accessed during off thread compilation. @@ -2689,7 +2691,7 @@ void TypeObject::setProto(JSContext *cx, TaggedProto proto) { JS_ASSERT(CurrentThreadCanWriteCompilationData()); - JS_ASSERT(singleton); + JS_ASSERT(singleton()); if (proto.isObject() && IsInsideNursery(cx->runtime(), proto.toObject())) addFlags(OBJECT_FLAG_NURSERY_PROTO); @@ -2737,7 +2739,7 @@ TypeObject::addProperty(ExclusiveContext *cx, jsid id, Property **pprop) return false; } - if (singleton && singleton->isNative()) { + if (singleton() && singleton()->isNative()) { /* * Fill the property in with any type the object already has in an own * property. We are only interested in plain native properties and @@ -2745,19 +2747,18 @@ TypeObject::addProperty(ExclusiveContext *cx, jsid id, Property **pprop) * or jitcode. */ - RootedObject rSingleton(cx, singleton); if (JSID_IS_VOID(id)) { /* Go through all shapes on the object to get integer-valued properties. */ - RootedShape shape(cx, singleton->lastProperty()); + RootedShape shape(cx, singleton()->lastProperty()); while (!shape->isEmptyShape()) { if (JSID_IS_VOID(IdToTypeId(shape->propid()))) - UpdatePropertyType(cx, &base->types, rSingleton, shape, true); + UpdatePropertyType(cx, &base->types, singleton(), shape, true); shape = shape->previous(); } /* Also get values of any dense elements in the object. */ - for (size_t i = 0; i < singleton->getDenseInitializedLength(); i++) { - const Value &value = singleton->getDenseElement(i); + for (size_t i = 0; i < singleton()->getDenseInitializedLength(); i++) { + const Value &value = singleton()->getDenseElement(i); if (!value.isMagic(JS_ELEMENTS_HOLE)) { Type type = GetValueType(value); if (!base->types.TypeSet::addType(type, &cx->typeLifoAlloc())) @@ -2766,12 +2767,12 @@ TypeObject::addProperty(ExclusiveContext *cx, jsid id, Property **pprop) } } else if (!JSID_IS_EMPTY(id)) { RootedId rootedId(cx, id); - Shape *shape = singleton->nativeLookup(cx, rootedId); + Shape *shape = singleton()->nativeLookup(cx, rootedId); if (shape) - UpdatePropertyType(cx, &base->types, rSingleton, shape, false); + UpdatePropertyType(cx, &base->types, singleton(), shape, false); } - if (singleton->watched()) { + if (singleton()->watched()) { /* * Mark the property as configured, to inhibit optimizations on it * and avoid bypassing the watchpoint handler. @@ -2941,10 +2942,10 @@ TypeObject::setFlags(ExclusiveContext *cx, TypeObjectFlags flags) AutoEnterAnalysis enter(cx); - if (singleton) { + if (singleton()) { /* Make sure flags are consistent with persistent object state. */ JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED, - singleton->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)); + singleton()->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)); } { @@ -3761,7 +3762,7 @@ JSObject::makeLazyType(JSContext *cx, HandleObject obj) /* Fill in the type according to the state of this object. */ - type->singleton = obj; + type->initSingleton(obj); if (obj->is() && obj->as().isInterpreted()) type->interpretedFunction = &obj->as(); @@ -3987,7 +3988,7 @@ ExclusiveContext::getLazyType(const Class *clasp, TaggedProto proto) if (!table.add(p, TypeObjectWithNewScriptEntry(type, nullptr))) return nullptr; - type->singleton = (JSObject *) TypeObject::LAZY_SINGLETON; + type->initSingleton((JSObject *) TypeObject::LAZY_SINGLETON); return type; } @@ -4087,7 +4088,7 @@ TypeObject::sweep(FreeOp *fop) for (unsigned i = 0; i < oldCapacity; i++) { Property *prop = oldArray[i]; if (prop) { - if (singleton && !prop->types.constraintList) { + if (singleton() && !prop->types.constraintList) { /* * Don't copy over properties of singleton objects which * don't have associated constraints. The contents of these @@ -4114,7 +4115,7 @@ TypeObject::sweep(FreeOp *fop) setBasePropertyCount(propertyCount); } else if (propertyCount == 1) { Property *prop = (Property *) propertySet; - if (singleton && !prop->types.constraintList) { + if (singleton() && !prop->types.constraintList) { // Skip, as above. clearProperties(); } else { @@ -4306,16 +4307,6 @@ TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { - if (singleton) { - /* - * Properties and associated type sets for singletons are cleared on - * every GC. The type object is normally destroyed too, but we don't - * charge this to 'temporary' as this is not for GC heap values. - */ - JS_ASSERT(!hasNewScript()); - return 0; - } - return mallocSizeOf(addendum); } diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index ff7c27970b00..6005be0a9818 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -889,51 +889,61 @@ struct TypeObject : gc::BarrieredCell HeapPtrObject proto_; #ifdef DEBUG - void assertCanAccessProto(); + void assertCanAccessProto() const; #else - void assertCanAccessProto() {} + void assertCanAccessProto() const {} #endif - public: - - const Class *clasp() { - return clasp_; - } - - void setClasp(const Class *clasp) { - JS_ASSERT(CurrentThreadCanWriteCompilationData()); - JS_ASSERT(singleton); - clasp_ = clasp; - } - - TaggedProto proto() { - assertCanAccessProto(); - return TaggedProto(proto_); - } - - HeapPtrObject &protoRaw() { - // For use during marking, don't call otherwise. - return proto_; - } - - void setProto(JSContext *cx, TaggedProto proto); - void setProtoUnchecked(TaggedProto proto) { - proto_ = proto.raw(); - } - /* * Whether there is a singleton JS object with this type. That JS object * must appear in type sets instead of this; we include the back reference * here to allow reverting the JS object to a lazy type. */ - HeapPtrObject singleton; + HeapPtrObject singleton_; + + public: + + const Class *clasp() const { + AutoThreadSafeAccess ts(this); + return clasp_; + } + + void setClasp(const Class *clasp) { + JS_ASSERT(CurrentThreadCanWriteCompilationData()); + JS_ASSERT(singleton()); + clasp_ = clasp; + } + + TaggedProto proto() const { + AutoThreadSafeAccess ts(this); + assertCanAccessProto(); + return TaggedProto(proto_); + } + + JSObject *singleton() const { + AutoThreadSafeAccess ts(this); + return singleton_; + } + + // For use during marking, don't call otherwise. + HeapPtrObject &protoRaw() { return proto_; } + HeapPtrObject &singletonRaw() { return singleton_; } + + void setProto(JSContext *cx, TaggedProto proto); + void setProtoUnchecked(TaggedProto proto) { + proto_ = proto.raw(); + } + + void initSingleton(JSObject *singleton) { + singleton_ = singleton; + } /* * Value held by singleton if this is a standin type for a singleton JS * object whose type has not been constructed yet. */ static const size_t LAZY_SINGLETON = 1; - bool lazy() const { return singleton == (JSObject *) LAZY_SINGLETON; } + bool lazy() const { return singleton() == (JSObject *) LAZY_SINGLETON; } private: /* Flags for this object. */ @@ -954,6 +964,7 @@ struct TypeObject : gc::BarrieredCell TypeObjectFlags flags() const { JS_ASSERT(CurrentThreadCanReadCompilationData()); + AutoThreadSafeAccess ts(this); return flags_; } @@ -969,21 +980,25 @@ struct TypeObject : gc::BarrieredCell bool hasNewScript() const { JS_ASSERT(CurrentThreadCanReadCompilationData()); + AutoThreadSafeAccess ts(this); return addendum && addendum->isNewScript(); } TypeNewScript *newScript() { JS_ASSERT(CurrentThreadCanReadCompilationData()); + AutoThreadSafeAccess ts(this); return addendum->asNewScript(); } bool hasTypedObject() { JS_ASSERT(CurrentThreadCanReadCompilationData()); + AutoThreadSafeAccess ts(this); return addendum && addendum->isTypedObject(); } TypeTypedObject *typedObject() { JS_ASSERT(CurrentThreadCanReadCompilationData()); + AutoThreadSafeAccess ts(this); return addendum->asTypedObject(); } diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index ab4186e83ef9..66aa26a7d493 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -88,8 +88,9 @@ Type::ObjectType(JSObject *obj) /* static */ inline Type Type::ObjectType(TypeObject *obj) { - if (obj->singleton) - return Type(uintptr_t(obj->singleton.get()) | 1); + AutoThreadSafeAccess ts(obj); + if (obj->singleton()) + return Type(uintptr_t(obj->singleton()) | 1); return Type(uintptr_t(obj)); } @@ -177,8 +178,9 @@ IdToTypeId(jsid id) * and overflowing integers. */ if (JSID_IS_STRING(id)) { - JSFlatString *str = JSID_TO_FLAT_STRING(id); - JS::TwoByteChars cp = str->range(); + JSAtom *atom = JSID_TO_ATOM(id); + js::AutoThreadSafeAccess ts(atom); + JS::TwoByteChars cp = atom->range(); if (cp.length() > 0 && (JS7_ISDEC(cp[0]) || cp[0] == '-')) { for (size_t i = 1; i < cp.length(); ++i) { if (!JS7_ISDEC(cp[i])) @@ -1085,7 +1087,7 @@ TypeSet::addType(Type type, LifoAlloc *alloc) if (type.isTypeObject()) { TypeObject *nobject = type.typeObject(); - JS_ASSERT(!nobject->singleton); + JS_ASSERT(!nobject->singleton()); if (nobject->unknownProperties()) goto unknownObject; } @@ -1317,6 +1319,8 @@ TypeObject::maybeGetProperty(jsid id) JS_ASSERT(!unknownProperties()); JS_ASSERT(CurrentThreadCanReadCompilationData()); + AutoThreadSafeAccess ts(this); + Property *prop = HashSetLookup (propertySet, basePropertyCount(), id); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 9d7dae3bc707..5decfbdef895 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -5506,6 +5506,8 @@ DumpProperty(JSObject *obj, Shape &shape) bool JSObject::uninlinedIsProxy() const { + AutoThreadSafeAccess ts0(this); + AutoThreadSafeAccess ts1(type_); return is(); } diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 0c4ffa3edd08..f87c0189941f 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -288,6 +288,10 @@ class JSObject : public js::ObjectImpl } bool isBoundFunction() const { + // Note: This function can race when it is called during off thread compilation. + js::AutoThreadSafeAccess ts0(this); + js::AutoThreadSafeAccess ts1(lastProperty()); + js::AutoThreadSafeAccess ts2(lastProperty()->base()); return lastProperty()->hasObjectFlag(js::BaseShape::BOUND_FUNCTION); } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index b48e057e1364..9ab8e3a18914 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -3114,12 +3114,14 @@ JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script) bool JSScript::varIsAliased(unsigned varSlot) { + AutoThreadSafeAccess ts(this); return bindings.bindingIsAliased(bindings.numArgs() + varSlot); } bool JSScript::formalIsAliased(unsigned argSlot) { + AutoThreadSafeAccess ts(this); return bindings.bindingIsAliased(argSlot); } diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 187d68e7f89f..6093dc8f7a17 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -203,11 +203,13 @@ class Bindings bool bindingArrayUsingTemporaryStorage() const { return bindingArrayAndFlag_ & TEMPORARY_STORAGE_BIT; } + + public: + Binding *bindingArray() const { return reinterpret_cast(bindingArrayAndFlag_ & ~TEMPORARY_STORAGE_BIT); } - public: inline Bindings(); /* @@ -243,7 +245,14 @@ class Bindings bool bindingIsAliased(unsigned bindingIndex); /* Return whether this scope has any aliased bindings. */ - bool hasAnyAliasedBindings() const { return callObjShape_ && !callObjShape_->isEmptyShape(); } + bool hasAnyAliasedBindings() const { + if (!callObjShape_) + return false; + + // Binding shapes are immutable once constructed. + AutoThreadSafeAccess ts(callObjShape_); + return !callObjShape_->isEmptyShape(); + } void trace(JSTracer *trc); }; @@ -478,6 +487,10 @@ class ScriptSourceObject : public JSObject const ReadOnlyCompileOptions &options); ScriptSource *source() { + // Script source objects are immutable. + AutoThreadSafeAccess ts0(this); + AutoThreadSafeAccess ts1(lastProperty()); + AutoThreadSafeAccess ts2(lastProperty()->base()); return static_cast(getReservedSlot(SOURCE_SLOT).toPrivate()); } @@ -556,6 +569,26 @@ class JSScript : public js::gc::BarrieredCell js::Bindings bindings; /* names of top-level variables in this script (and arguments if this is a function script) */ + bool hasAnyAliasedBindings() const { + js::AutoThreadSafeAccess ts(this); + return bindings.hasAnyAliasedBindings(); + } + + js::Binding *bindingArray() const { + js::AutoThreadSafeAccess ts(this); + return bindings.bindingArray(); + } + + unsigned numArgs() const { + js::AutoThreadSafeAccess ts(this); + return bindings.numArgs(); + } + + js::Shape *callObjShape() const { + js::AutoThreadSafeAccess ts(this); + return bindings.callObjShape(); + } + // Word-sized fields. private: @@ -889,12 +922,24 @@ class JSScript : public js::gc::BarrieredCell bool selfHosted() const { return selfHosted_; } bool bindingsAccessedDynamically() const { return bindingsAccessedDynamically_; } - bool funHasExtensibleScope() const { return funHasExtensibleScope_; } - bool funNeedsDeclEnvObject() const { return funNeedsDeclEnvObject_; } - bool funHasAnyAliasedFormal() const { return funHasAnyAliasedFormal_; } + bool funHasExtensibleScope() const { + js::AutoThreadSafeAccess ts(this); + return funHasExtensibleScope_; + } + bool funNeedsDeclEnvObject() const { + js::AutoThreadSafeAccess ts(this); + return funNeedsDeclEnvObject_; + } + bool funHasAnyAliasedFormal() const { + js::AutoThreadSafeAccess ts(this); + return funHasAnyAliasedFormal_; + } bool hasSingletons() const { return hasSingletons_; } - bool treatAsRunOnce() const { return treatAsRunOnce_; } + bool treatAsRunOnce() const { + js::AutoThreadSafeAccess ts(this); + return treatAsRunOnce_; + } bool hasRunOnce() const { return hasRunOnce_; } bool hasBeenCloned() const { return hasBeenCloned_; } @@ -921,23 +966,50 @@ class JSScript : public js::gc::BarrieredCell void setActiveEval() { isActiveEval_ = true; } void setDirectlyInsideEval() { directlyInsideEval_ = true; } - bool usesArgumentsAndApply() const { return usesArgumentsAndApply_; } + bool usesArgumentsAndApply() const { + js::AutoThreadSafeAccess ts(this); + return usesArgumentsAndApply_; + } void setUsesArgumentsAndApply() { usesArgumentsAndApply_ = true; } - bool shouldCloneAtCallsite() const { return shouldCloneAtCallsite_; } - bool shouldInline() const { return shouldInline_; } + bool shouldCloneAtCallsite() const { + js::AutoThreadSafeAccess ts(this); + return shouldCloneAtCallsite_; + } + bool shouldInline() const { + js::AutoThreadSafeAccess ts(this); + return shouldInline_; + } void setShouldCloneAtCallsite() { shouldCloneAtCallsite_ = true; } void setShouldInline() { shouldInline_ = true; } - bool isCallsiteClone() const { return isCallsiteClone_; } + bool isCallsiteClone() const { + js::AutoThreadSafeAccess ts(this); + return isCallsiteClone_; + } bool isGeneratorExp() const { return isGeneratorExp_; } - bool failedBoundsCheck() const { return failedBoundsCheck_; } - bool failedShapeGuard() const { return failedShapeGuard_; } - bool hadFrequentBailouts() const { return hadFrequentBailouts_; } - bool uninlineable() const { return uninlineable_; } - bool invalidatedIdempotentCache() const { return invalidatedIdempotentCache_; } + bool failedBoundsCheck() const { + js::AutoThreadSafeAccess ts(this); + return failedBoundsCheck_; + } + bool failedShapeGuard() const { + js::AutoThreadSafeAccess ts(this); + return failedShapeGuard_; + } + bool hadFrequentBailouts() const { + js::AutoThreadSafeAccess ts(this); + return hadFrequentBailouts_; + } + bool uninlineable() const { + js::AutoThreadSafeAccess ts(this); + return uninlineable_; + } + bool invalidatedIdempotentCache() const { + js::AutoThreadSafeAccess ts(this); + return invalidatedIdempotentCache_; + } void setFailedBoundsCheck() { failedBoundsCheck_ = true; } void setFailedShapeGuard() { failedShapeGuard_ = true; } @@ -955,7 +1027,10 @@ class JSScript : public js::gc::BarrieredCell void setWarnedAboutUndefinedProp() { warnedAboutUndefinedProp_ = true; } /* See ContextFlags::funArgumentsHasLocalBinding comment. */ - bool argumentsHasVarBinding() const { return argsHasVarBinding_; } + bool argumentsHasVarBinding() const { + js::AutoThreadSafeAccess ts(this); + return argsHasVarBinding_; + } jsbytecode *argumentsBytecode() const { JS_ASSERT(code()[0] == JSOP_ARGUMENTS); return code(); } void setArgumentsHasVarBinding(); bool argumentsAliasesFormals() const { @@ -963,6 +1038,7 @@ class JSScript : public js::gc::BarrieredCell } js::GeneratorKind generatorKind() const { + js::AutoThreadSafeAccess ts(this); return js::GeneratorKindFromBits(generatorKindBits_); } bool isGenerator() const { return generatorKind() != js::NotGenerator; } @@ -987,8 +1063,9 @@ class JSScript : public js::gc::BarrieredCell */ bool analyzedArgsUsage() const { return !needsArgsAnalysis_; } bool needsArgsObj() const { - JS_ASSERT(analyzedArgsUsage()); + js::AutoThreadSafeAccess ts(this); JS_ASSERT(js::CurrentThreadCanReadCompilationData()); + JS_ASSERT(analyzedArgsUsage()); return needsArgsObj_; } void setNeedsArgsObj(bool needsArgsObj); @@ -1012,9 +1089,11 @@ class JSScript : public js::gc::BarrieredCell } bool hasIonScript() const { + js::AutoThreadSafeAccess ts(this); return ion && ion != ION_DISABLED_SCRIPT && ion != ION_COMPILING_SCRIPT; } bool canIonCompile() const { + js::AutoThreadSafeAccess ts(this); return ion != ION_DISABLED_SCRIPT; } @@ -1041,6 +1120,7 @@ class JSScript : public js::gc::BarrieredCell bool hasBaselineScript() const { JS_ASSERT(js::CurrentThreadCanReadCompilationData()); + js::AutoThreadSafeAccess ts(this); return baseline && baseline != BASELINE_DISABLED_SCRIPT; } bool canBaselineCompile() const { @@ -1048,6 +1128,7 @@ class JSScript : public js::gc::BarrieredCell } js::jit::BaselineScript *baselineScript() const { JS_ASSERT(hasBaselineScript()); + js::AutoThreadSafeAccess ts(this); return baseline; } inline void setBaselineScript(JSContext *maybecx, js::jit::BaselineScript *baselineScript); @@ -1059,6 +1140,7 @@ class JSScript : public js::gc::BarrieredCell } bool canParallelIonCompile() const { + js::AutoThreadSafeAccess ts(this); return parallelIon != ION_DISABLED_SCRIPT; } @@ -1099,7 +1181,10 @@ class JSScript : public js::gc::BarrieredCell * Original compiled function for the script, if it has a function. * nullptr for global and eval scripts. */ - JSFunction *function() const { return function_; } + JSFunction *function() const { + js::AutoThreadSafeAccess ts(this); + return function_; + } inline void setFunction(JSFunction *fun); JSFunction *originalFunction() const; @@ -1110,7 +1195,10 @@ class JSScript : public js::gc::BarrieredCell static bool loadSource(JSContext *cx, js::ScriptSource *ss, bool *worked); void setSourceObject(JSObject *object); - JSObject *sourceObject() const { return sourceObject_; } + JSObject *sourceObject() const { + js::AutoThreadSafeAccess ts(this); + return sourceObject_; + } js::ScriptSource *scriptSource() const; JSPrincipals *originPrincipals() const { return scriptSource()->originPrincipals(); } const char *filename() const { return scriptSource()->filename(); } @@ -1149,6 +1237,7 @@ class JSScript : public js::gc::BarrieredCell JSObject *enclosingStaticScope() const { if (isCallsiteClone()) return nullptr; + js::AutoThreadSafeAccess ts(this); return enclosingScopeOrOriginalFunction_; } @@ -1169,7 +1258,11 @@ class JSScript : public js::gc::BarrieredCell bool makeAnalysis(JSContext *cx); public: - uint32_t getUseCount() const { return useCount; } + uint32_t getUseCount() const { + // Note: We ignore races when reading the use count of a script off thread. + js::AutoThreadSafeAccess ts(this); + return useCount; + } uint32_t incUseCount(uint32_t amount = 1) { return useCount += amount; } uint32_t *addressOfUseCount() { return &useCount; } static size_t offsetOfUseCount() { return offsetof(JSScript, useCount); } @@ -1201,8 +1294,11 @@ class JSScript : public js::gc::BarrieredCell /* Script notes are allocated right after the code. */ jssrcnote *notes() { return (jssrcnote *)(code() + length()); } - bool hasArray(ArrayKind kind) { return (hasArrayBits & (1 << kind)); } - void setHasArray(ArrayKind kind) { hasArrayBits |= (1 << kind); } + bool hasArray(ArrayKind kind) { + js::AutoThreadSafeAccess ts(this); + return (hasArrayBits & (1 << kind)); + } + void setHasArray(ArrayKind kind) { hasArrayBits |= (1 << kind); } void cloneHasArray(JSScript *script) { hasArrayBits = script->hasArrayBits; } bool hasConsts() { return hasArray(CONSTS); } @@ -1223,26 +1319,31 @@ class JSScript : public js::gc::BarrieredCell js::ConstArray *consts() { JS_ASSERT(hasConsts()); + js::AutoThreadSafeAccess ts(this); return reinterpret_cast(data + constsOffset()); } js::ObjectArray *objects() { JS_ASSERT(hasObjects()); + js::AutoThreadSafeAccess ts(this); return reinterpret_cast(data + objectsOffset()); } js::ObjectArray *regexps() { JS_ASSERT(hasRegexps()); + js::AutoThreadSafeAccess ts(this); return reinterpret_cast(data + regexpsOffset()); } js::TryNoteArray *trynotes() { JS_ASSERT(hasTrynotes()); + js::AutoThreadSafeAccess ts(this); return reinterpret_cast(data + trynotesOffset()); } js::BlockScopeArray *blockScopes() { JS_ASSERT(hasBlockScopes()); + js::AutoThreadSafeAccess ts(this); return reinterpret_cast(data + blockScopesOffset()); } @@ -1251,6 +1352,7 @@ class JSScript : public js::gc::BarrieredCell size_t natoms() const { return natoms_; } js::HeapPtrAtom &getAtom(size_t index) const { + js::AutoThreadSafeAccess ts(this); JS_ASSERT(index < natoms()); return atoms[index]; } @@ -1604,6 +1706,7 @@ class LazyScript : public gc::BarrieredCell } bool usesArgumentsAndApply() const { + AutoThreadSafeAccess ts(this); return usesArgumentsAndApply_; } void setUsesArgumentsAndApply() { @@ -1628,9 +1731,11 @@ class LazyScript : public gc::BarrieredCell return sourceObject()->source(); } uint32_t begin() const { + AutoThreadSafeAccess ts(this); return begin_; } uint32_t end() const { + AutoThreadSafeAccess ts(this); return end_; } uint32_t lineno() const { diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h index 67398e8d4f8b..e5af19b34682 100644 --- a/js/src/jsscriptinlines.h +++ b/js/src/jsscriptinlines.h @@ -26,9 +26,9 @@ Bindings::Bindings() inline AliasedFormalIter::AliasedFormalIter(JSScript *script) - : begin_(script->bindings.bindingArray()), + : begin_(script->bindingArray()), p_(begin_), - end_(begin_ + (script->funHasAnyAliasedFormal() ? script->bindings.numArgs() : 0)), + end_(begin_ + (script->funHasAnyAliasedFormal() ? script->numArgs() : 0)), slot_(CallObject::RESERVED_SLOTS) { settle(); @@ -98,6 +98,7 @@ JSScript::global() const * A JSScript always marks its compartment's global (via bindings) so we * can assert that maybeGlobal is non-null here. */ + js::AutoThreadSafeAccess ts(this); return *compartment()->maybeGlobal(); } diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index bc443a15c8e1..bd1370b098d8 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -4142,6 +4142,8 @@ js::CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *resul int32_t js::CompareAtoms(JSAtom *atom1, JSAtom *atom2) { + AutoThreadSafeAccess ts0(atom1); + AutoThreadSafeAccess ts1(atom2); return CompareChars(atom1->chars(), atom1->length(), atom2->chars(), atom2->length()); } diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index f531e89b232b..3a0db52e5784 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -77,8 +77,15 @@ JS_FRIEND_API(JSObject *) js::UncheckedUnwrap(JSObject *wrapped, bool stopAtOuter, unsigned *flagsp) { unsigned flags = 0; - while (wrapped->is() && - !JS_UNLIKELY(stopAtOuter && wrapped->getClass()->ext.innerObject)) { + while (true) { + AutoThreadSafeAccess ts0(wrapped); + AutoThreadSafeAccess ts1(wrapped->typeRaw()); + AutoThreadSafeAccess ts2(wrapped->lastProperty()); + if (!wrapped->is() || + JS_UNLIKELY(stopAtOuter && wrapped->getClass()->ext.innerObject)) + { + break; + } flags |= Wrapper::wrapperHandler(wrapped)->flags(); wrapped = wrapped->as().private_().toObjectOrNull(); } diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 954e08b7a1d8..ce678939f2f9 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -452,15 +452,15 @@ GlobalObject::getOrCreateEval(JSContext *cx, Handle global, { if (!global->getOrCreateObjectPrototype(cx)) return false; - eval.set(&global->getSlotRefForCompilation(EVAL).toObject()); + eval.set(&global->getSlotForCompilation(EVAL).toObject()); return true; } bool GlobalObject::valueIsEval(Value val) { - HeapSlot &eval = getSlotRef(EVAL); - return eval.isObject() && eval.get() == val; + Value eval = getSlotForCompilation(EVAL); + return eval.isObject() && eval == val; } /* static */ bool diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 57c13a4c6791..4d469eac3853 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -155,7 +155,7 @@ class GlobalObject : public JSObject public: Value getConstructor(JSProtoKey key) const { JS_ASSERT(key <= JSProto_LIMIT); - return getSlotRefForCompilation(APPLICATION_SLOTS + key); + return getSlotForCompilation(APPLICATION_SLOTS + key); } void setConstructor(JSProtoKey key, const Value &v) { @@ -165,7 +165,7 @@ class GlobalObject : public JSObject Value getPrototype(JSProtoKey key) const { JS_ASSERT(key <= JSProto_LIMIT); - return getSlotRefForCompilation(APPLICATION_SLOTS + JSProto_LIMIT + key); + return getSlotForCompilation(APPLICATION_SLOTS + JSProto_LIMIT + key); } void setPrototype(JSProtoKey key, const Value &value) { @@ -447,13 +447,14 @@ class GlobalObject : public JSObject return &self->getSlot(slot).toObject(); } - const HeapSlot &getSlotRefForCompilation(uint32_t slot) const { + Value getSlotForCompilation(uint32_t slot) const { // This method should only be used for slots that are either eagerly // initialized on creation of the global or only change under the // compilation lock. Note that the dynamic slots pointer for global // objects can only change under the compilation lock. JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(getClass())); uint32_t fixed = numFixedSlotsForCompilation(); + AutoThreadSafeAccess ts(this); if (slot < fixed) return fixedSlots()[slot]; return slots[slot - fixed]; @@ -509,13 +510,18 @@ class GlobalObject : public JSObject } JSObject *intrinsicsHolder() { - JS_ASSERT(!getSlotRefForCompilation(INTRINSICS).isUndefined()); - return &getSlotRefForCompilation(INTRINSICS).toObject(); + JS_ASSERT(!getSlotForCompilation(INTRINSICS).isUndefined()); + return &getSlotForCompilation(INTRINSICS).toObject(); } bool maybeGetIntrinsicValue(jsid id, Value *vp) { JS_ASSERT(CurrentThreadCanReadCompilationData()); JSObject *holder = intrinsicsHolder(); + + AutoThreadSafeAccess ts0(holder); + AutoThreadSafeAccess ts1(holder->lastProperty()); + AutoThreadSafeAccess ts2(holder->lastProperty()->base()); + if (Shape *shape = holder->nativeLookupPure(id)) { *vp = holder->getSlot(shape->slot()); return true; @@ -553,7 +559,8 @@ class GlobalObject : public JSObject unsigned nargs, MutableHandleValue funVal); RegExpStatics *getRegExpStatics() const { - JSObject &resObj = getSlotRefForCompilation(REGEXP_STATICS).toObject(); + JSObject &resObj = getSlotForCompilation(REGEXP_STATICS).toObject(); + AutoThreadSafeAccess ts(&resObj); return static_cast(resObj.getPrivate(/* nfixed = */ 1)); } diff --git a/js/src/vm/ObjectImpl.h b/js/src/vm/ObjectImpl.h index 822a1180ca5e..66d504ca4494 100644 --- a/js/src/vm/ObjectImpl.h +++ b/js/src/vm/ObjectImpl.h @@ -980,13 +980,15 @@ class ObjectImpl : public gc::BarrieredCell /* These functions are public, and they should remain public. */ public: - js::TaggedProto getTaggedProto() const { + TaggedProto getTaggedProto() const { + AutoThreadSafeAccess ts(this); return type_->proto(); } bool hasTenuredProto() const; const Class *getClass() const { + AutoThreadSafeAccess ts(this); return type_->clasp(); } @@ -1193,6 +1195,12 @@ class ObjectImpl : public gc::BarrieredCell types::TypeObject *type() const { MOZ_ASSERT(!hasLazyType()); + return typeRaw(); + } + + types::TypeObject *typeRaw() const { + AutoThreadSafeAccess ts0(this); + AutoThreadSafeAccess ts1(type_); return type_; } @@ -1206,13 +1214,19 @@ class ObjectImpl : public gc::BarrieredCell * Whether this is the only object which has its specified type. This * object will have its type constructed lazily as needed by analysis. */ - bool hasSingletonType() const { return !!type_->singleton; } + bool hasSingletonType() const { + AutoThreadSafeAccess ts(this); + return !!type_->singleton(); + } /* * Whether the object's type has not been constructed yet. If an object * might have a lazy type, use getType() below, otherwise type(). */ - bool hasLazyType() const { return type_->lazy(); } + bool hasLazyType() const { + AutoThreadSafeAccess ts(this); + return type_->lazy(); + } uint32_t slotSpan() const { if (inDictionaryMode()) @@ -1540,6 +1554,10 @@ JS_ALWAYS_INLINE Zone * BarrieredCell::zoneFromAnyThread() const { const ObjectImpl* obj = static_cast(this); + + // Note: This read of obj->shape_ may race, though the zone fetched will be the same. + AutoThreadSafeAccess ts(obj->shape_); + return obj->shape_->zoneFromAnyThread(); } diff --git a/js/src/vm/Runtime-inl.h b/js/src/vm/Runtime-inl.h index 08f74524d0e0..229623677448 100644 --- a/js/src/vm/Runtime-inl.h +++ b/js/src/vm/Runtime-inl.h @@ -47,7 +47,12 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::Initi Entry *entry = &entries[entry_]; JSObject *templateObj = reinterpret_cast(&entry->templateObject); - if (templateObj->type()->shouldPreTenure()) + + // Do an end run around JSObject::type() to avoid doing AutoUnprotectCell + // on the templateObj, which is not a GC thing and can't use runtimeFromAnyThread. + types::TypeObject *type = templateObj->type_; + + if (type->shouldPreTenure()) heap = gc::TenuredHeap; JSObject *obj = js_NewGCObject(cx, entry->kind, heap); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 35382a724ed2..e6e10b6d1a11 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -15,7 +15,7 @@ #include #include -#if defined(DEBUG) && !defined(XP_WIN) +#ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES # include #endif @@ -853,7 +853,7 @@ JSRuntime::activeGCInAtomsZone() return zone->needsBarrier() || zone->isGCScheduled() || zone->wasGCStarted(); } -#if defined(DEBUG) && !defined(XP_WIN) +#ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES AutoProtectHeapForIonCompilation::AutoProtectHeapForIonCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) : runtime(rt) @@ -902,7 +902,7 @@ AutoThreadSafeAccess::AutoThreadSafeAccess(const Cell *cell MOZ_GUARD_OBJECT_NOT arena = base; - if (mprotect(arena, sizeof(Arena), PROT_READ)) + if (mprotect(arena, sizeof(Arena), PROT_READ | PROT_WRITE)) MOZ_CRASH(); if (!runtime->unprotectedArenas.append(arena)) @@ -921,7 +921,7 @@ AutoThreadSafeAccess::~AutoThreadSafeAccess() runtime->unprotectedArenas.popBack(); } -#endif // DEBUG && !XP_WIN +#endif // JS_CAN_CHECK_THREADSAFE_ACCESSES #ifdef JS_WORKER_THREADS diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 6141ab5c76fd..57d3ea613a28 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -2140,7 +2140,7 @@ class AutoEnterIonCompilation class AutoProtectHeapForIonCompilation { public: -#if defined(DEBUG) && !defined(XP_WIN) +#ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES JSRuntime *runtime; AutoProtectHeapForIonCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM); diff --git a/js/src/vm/ScopeObject-inl.h b/js/src/vm/ScopeObject-inl.h index 3271c309efef..16c4dd59bf55 100644 --- a/js/src/vm/ScopeObject-inl.h +++ b/js/src/vm/ScopeObject-inl.h @@ -75,9 +75,11 @@ StaticScopeIter::scopeShape() const { JS_ASSERT(hasDynamicScopeObject()); JS_ASSERT(type() != NAMED_LAMBDA); - return type() == BLOCK - ? block().lastProperty() - : funScript()->bindings.callObjShape(); + if (type() == BLOCK) { + AutoThreadSafeAccess ts(&block()); + return block().lastProperty(); + } + return funScript()->callObjShape(); } template diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 9a93cea655ea..25945209be4e 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -99,7 +99,7 @@ js::ScopeCoordinateName(ScopeCoordinateNameCache &cache, JSScript *script, jsbyt Shape::Range r(shape); while (r.front().slot() != sc.slot) r.popFront(); - id = r.front().propid(); + id = r.front().propidRaw(); } /* Beware nameless destructuring formal. */ diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 7228735aeaf3..2141f440f11c 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -181,6 +181,7 @@ class ScopeObject : public JSObject * enclosing scope of a ScopeObject is necessarily non-null. */ inline JSObject &enclosingScope() const { + AutoThreadSafeAccess ts(this); return getFixedSlot(SCOPE_CHAIN_SLOT).toObject(); } @@ -232,6 +233,7 @@ class CallObject : public ScopeObject /* True if this is for a strict mode eval frame. */ bool isForEval() const { + AutoThreadSafeAccess ts(this); JS_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull()); JS_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(), getFixedSlot(CALLEE_SLOT).toObject().is()); @@ -243,6 +245,7 @@ class CallObject : public ScopeObject * only be called if !isForEval.) */ JSFunction &callee() const { + AutoThreadSafeAccess ts(this); return getFixedSlot(CALLEE_SLOT).toObject().as(); } @@ -368,6 +371,7 @@ class StaticBlockObject : public BlockObject /* See StaticScopeIter comment. */ JSObject *enclosingStaticScope() const { + AutoThreadSafeAccess ts(this); return getFixedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull(); } @@ -400,6 +404,7 @@ class StaticBlockObject : public BlockObject bool needsClone() { // The first variable slot will always indicate whether the object has // any aliased vars. Bypass slotValue() to allow testing this off thread. + AutoThreadSafeAccess ts(this); return !getFixedSlot(RESERVED_SLOTS).isFalse(); } diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index bd5cbbb72675..7765e87b4cd8 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -195,7 +195,7 @@ ShapeTable::search(jsid id, bool adding) /* Hit: return entry. */ shape = SHAPE_CLEAR_COLLISION(stored); - if (shape && shape->propid() == id) + if (shape && shape->propidRaw() == id) return spp; /* Collision: double hash. */ @@ -229,7 +229,7 @@ ShapeTable::search(jsid id, bool adding) return (adding && firstRemoved) ? firstRemoved : spp; shape = SHAPE_CLEAR_COLLISION(stored); - if (shape && shape->propid() == id) { + if (shape && shape->propidRaw() == id) { JS_ASSERT(collision_flag); return spp; } diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 1599a18d7314..6e8c231d08eb 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -1034,11 +1034,15 @@ class Shape : public gc::BarrieredCell void popFront() { JS_ASSERT(!empty()); + AutoThreadSafeAccess ts(cursor); cursor = cursor->parent; } }; - const Class *getObjectClass() const { return base()->clasp; } + const Class *getObjectClass() const { + AutoThreadSafeAccess ts(base()); + return base()->clasp; + } JSObject *getObjectParent() const { return base()->parent; } JSObject *getObjectMetadata() const { return base()->metadata; } @@ -1095,12 +1099,15 @@ class Shape : public gc::BarrieredCell PUBLIC_FLAGS = HAS_SHORTID }; - bool inDictionary() const { return (flags & IN_DICTIONARY) != 0; } - unsigned getFlags() const { return flags & PUBLIC_FLAGS; } + bool inDictionary() const { + AutoThreadSafeAccess ts(this); + return (flags & IN_DICTIONARY) != 0; + } + unsigned getFlags() const { return flags & PUBLIC_FLAGS; } bool hasShortID() const { return (flags & HAS_SHORTID) != 0; } PropertyOp getter() const { return base()->rawGetter; } - bool hasDefaultGetter() 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; } @@ -1158,11 +1165,21 @@ class Shape : public gc::BarrieredCell BaseShape *base() const { return base_.get(); } - bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; } + bool hasSlot() const { + AutoThreadSafeAccess ts(this); + return (attrs & JSPROP_SHARED) == 0; + } uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); } - uint32_t maybeSlot() const { return slotInfo & SLOT_MASK; } + uint32_t maybeSlot() const { + // Note: Reading a shape's slot off thread can race against main thread + // updates to the number of linear searches on the shape, which is + // stored in the same slotInfo field. We tolerate this. + AutoThreadSafeAccess ts(this); + return slotInfo & SLOT_MASK; + } bool isEmptyShape() const { + AutoThreadSafeAccess ts(this); JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot()); return JSID_IS_EMPTY(propid_); } @@ -1184,6 +1201,8 @@ class Shape : public gc::BarrieredCell } uint32_t numFixedSlots() const { + // Note: The same race applies here as in maybeSlot(). + AutoThreadSafeAccess ts(this); return (slotInfo >> FIXED_SLOTS_SHIFT); } @@ -1205,11 +1224,17 @@ class Shape : public gc::BarrieredCell } const EncapsulatedId &propid() const { + AutoThreadSafeAccess ts(this); JS_ASSERT(!isEmptyShape()); JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; } EncapsulatedId &propidRef() { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; } + jsid propidRaw() const { + // Return the actual jsid, not an internal reference. + AutoThreadSafeAccess ts(this); + return propid(); + } int16_t shortid() const { JS_ASSERT(hasShortID()); return maybeShortid(); } int16_t maybeShortid() const { return shortid_; } @@ -1225,6 +1250,7 @@ class Shape : public gc::BarrieredCell bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; } bool writable() const { // JS_ASSERT(isDataDescriptor()); + AutoThreadSafeAccess ts(this); return (attrs & JSPROP_READONLY) == 0; } bool hasGetterValue() const { return attrs & JSPROP_GETTER; } @@ -1616,9 +1642,11 @@ Shape::searchLinear(jsid id) */ JS_ASSERT(!inDictionary()); - for (Shape *shape = this; shape; shape = shape->parent) { + for (Shape *shape = this; shape; ) { + AutoThreadSafeAccess ts(shape); if (shape->propidRef() == id) return shape; + shape = shape->parent; } return nullptr; diff --git a/js/src/vm/String.h b/js/src/vm/String.h index ea4b7ca43638..6b09f44fc5d0 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -394,6 +394,7 @@ class JSString : public js::gc::BarrieredCell JS_ALWAYS_INLINE JSAtom &asAtom() const { + js::AutoThreadSafeAccess ts(this); JS_ASSERT(isAtom()); return *(JSAtom *)this; } @@ -1129,6 +1130,7 @@ JSString::base() const inline js::PropertyName * JSAtom::asPropertyName() { + js::AutoThreadSafeAccess ts(this); #ifdef DEBUG uint32_t dummy; JS_ASSERT(!isIndex(&dummy)); diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index 7baa47cf8adb..4b3d65a1bc53 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -324,9 +324,11 @@ class TypedArrayObject : public ArrayBufferViewObject return tarr->getFixedSlot(BYTEOFFSET_SLOT); } static Value byteLengthValue(TypedArrayObject *tarr) { + AutoThreadSafeAccess ts(tarr); return tarr->getFixedSlot(BYTELENGTH_SLOT); } static Value lengthValue(TypedArrayObject *tarr) { + AutoThreadSafeAccess ts(tarr); return tarr->getFixedSlot(LENGTH_SLOT); } @@ -344,9 +346,11 @@ class TypedArrayObject : public ArrayBufferViewObject } uint32_t type() const { + AutoThreadSafeAccess ts(this); return getFixedSlot(TYPE_SLOT).toInt32(); } void *viewData() const { + AutoThreadSafeAccess ts(this); return static_cast(getPrivate(DATA_SLOT)); }