diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 0c47ee82a5..5655c7dd4f 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -2099,7 +2099,7 @@ static bool ReportLargeAllocationFailure(JSContext* cx, unsigned argc, jsval* vp) { CallArgs args = CallArgsFromVp(argc, vp); - void* buf = cx->runtime()->onOutOfMemoryCanGC(NULL, JSRuntime::LARGE_ALLOCATION); + void* buf = cx->runtime()->onOutOfMemoryCanGC(AllocFunction::Malloc, JSRuntime::LARGE_ALLOCATION); js_free(buf); args.rval().setUndefined(); return true; diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index ddfa54b9cb..3c2ba3deb2 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -335,6 +335,18 @@ namespace CClosure { // libffi callback static void ClosureStub(ffi_cif* cif, void* result, void** args, void* userData); + + struct ArgClosure : public ScriptEnvironmentPreparer::Closure { + ArgClosure(ffi_cif* cifArg, void* resultArg, void** argsArg, ClosureInfo* cinfoArg) + : cif(cifArg), result(resultArg), args(argsArg), cinfo(cinfoArg) {} + + bool operator()(JSContext *cx) override; + + ffi_cif* cif; + void* result; + void** args; + ClosureInfo* cinfo; + }; } namespace CData { @@ -6755,9 +6767,6 @@ CClosure::Create(JSContext* cx, MOZ_ASSERT(proto); MOZ_ASSERT(CType::IsCTypeProto(proto)); - // Get a JSContext for use with the closure. - JSContext* closeureCx = js::DefaultJSContext(JS_GetRuntime(cx)); - // Prepare the error sentinel value. It's important to do this now, because // we might be unable to convert the value to the proper type. If so, we want // the caller to know about it _now_, rather than some uncertain time in the @@ -6794,7 +6803,6 @@ CClosure::Create(JSContext* cx, } // Copy the important bits of context into cinfo. - cinfo->cx = closeureCx; cinfo->errResult = errResult.release(); cinfo->closureObj = result; cinfo->typeObj = typeObj; @@ -6865,9 +6873,14 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) MOZ_ASSERT(userData); // Retrieve the essentials from our closure object. - ClosureInfo* cinfo = static_cast(userData); - JSContext* cx = cinfo->cx; + ArgClosure argClosure(cif, result, args, static_cast(userData)); + JSRuntime* rt = argClosure.cinfo->rt; + RootedObject fun(rt, argClosure.cinfo->jsfnObj); + (void) js::PrepareScriptEnvironmentAndInvoke(rt, fun, argClosure); +} +bool CClosure::ArgClosure::operator()(JSContext* cx) +{ // Let the runtime callback know that we are about to call into JS again. The end callback will // fire automatically when we exit this function. js::AutoCTypesActivityCallback autoCallback(cx, js::CTYPES_CALLBACK_BEGIN, @@ -6876,12 +6889,11 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) RootedObject typeObj(cx, cinfo->typeObj); RootedObject thisObj(cx, cinfo->thisObj); RootedValue jsfnVal(cx, ObjectValue(*cinfo->jsfnObj)); + AssertSameCompartment(cx, cinfo->jsfnObj); + JS_AbortIfWrongThread(JS_GetRuntime(cx)); - JSAutoRequest ar(cx); - JSAutoCompartment ac(cx, cinfo->jsfnObj); - // Assert that our CIFs agree. FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); MOZ_ASSERT(cif == &fninfo->mCIF); @@ -6914,7 +6926,7 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) JS::AutoValueVector argv(cx); if (!argv.resize(cif->nargs)) { JS_ReportOutOfMemory(cx); - return; + return false; } for (uint32_t i = 0; i < cif->nargs; ++i) { @@ -6922,7 +6934,7 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) // the existing buffers. RootedObject argType(cx, fninfo->mArgTypes[i]); if (!ConvertToJS(cx, argType, nullptr, args[i], false, false, argv[i])) - return; + return false; } // Call the JS function. 'thisObj' may be nullptr, in which case the JS @@ -6943,11 +6955,9 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) // Something failed. The callee may have thrown, or it may not have // returned a value that ImplicitConvert() was happy with. Depending on how // prudent the consumer has been, we may or may not have a recovery plan. - - // In any case, a JS exception cannot be passed to C code, so report the - // exception if any and clear it from the cx. - if (JS_IsExceptionPending(cx)) - JS_ReportPendingException(cx); + // + // Note that PrepareScriptEnvironmentAndInvoke should take care of reporting + // the exception. if (cinfo->errResult) { // Good case: we have a sentinel that we can return. Copy it in place of @@ -6961,15 +6971,8 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) memcpy(result, cinfo->errResult, copySize); } else { // Bad case: not much we can do here. The rv is already zeroed out, so we - // just report (another) error and hope for the best. JS_ReportError will - // actually throw an exception here, so then we have to report it. Again. - // Ugh. - JS_ReportError(cx, "JavaScript callback failed, and an error sentinel " - "was not specified."); - if (JS_IsExceptionPending(cx)) - JS_ReportPendingException(cx); - - return; + // just return and hope for the best. + return false; } } @@ -6992,6 +6995,8 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) default: break; } + + return true; } /******************************************************************************* diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index 76de720cc5..56d88b7097 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -314,9 +314,7 @@ struct FunctionInfo // Parameters necessary for invoking a JS function from a C closure. struct ClosureInfo { - JSContext* cx; // JSContext to use - JSRuntime* rt; // Used in the destructor, where cx might have already - // been GCed. + JSRuntime* rt; JS::Heap closureObj; // CClosure object JS::Heap typeObj; // FunctionType describing the C function JS::Heap thisObj; // 'this' object to use for the JS function call diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h index e6e2bd4b5e..6187da8ae0 100644 --- a/js/src/gc/GCInternals.h +++ b/js/src/gc/GCInternals.h @@ -44,7 +44,7 @@ struct AutoFinishGC class AutoTraceSession { public: - explicit AutoTraceSession(JSRuntime* rt, HeapState state = Tracing); + explicit AutoTraceSession(JSRuntime* rt, JS::HeapState state = JS::HeapState::Tracing); ~AutoTraceSession(); protected: @@ -55,7 +55,7 @@ class AutoTraceSession AutoTraceSession(const AutoTraceSession&) = delete; void operator=(const AutoTraceSession&) = delete; - HeapState prevState; + JS::HeapState prevState; }; struct AutoPrepareForTracing diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 53b395ecb1..8b26b4c421 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -594,12 +594,6 @@ class GCRuntime void setParameter(JSGCParamKey key, uint32_t value); uint32_t getParameter(JSGCParamKey key, const AutoLockGC& lock); - bool isHeapBusy() { return heapState != js::Idle; } - bool isHeapMajorCollecting() { return heapState == js::MajorCollecting; } - bool isHeapMinorCollecting() { return heapState == js::MinorCollecting; } - bool isHeapCollecting() { return isHeapMajorCollecting() || isHeapMinorCollecting(); } - bool isHeapCompacting() { return isHeapMajorCollecting() && state() == COMPACT; } - bool triggerGC(JS::gcreason::Reason reason); void maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock); bool triggerZoneGC(Zone* zone, JS::gcreason::Reason reason); @@ -666,7 +660,8 @@ class GCRuntime public: // Internal public interface - js::gc::State state() { return incrementalState; } + js::gc::State state() const { return incrementalState; } + bool isHeapCompacting() const { return state() == COMPACT; } bool isBackgroundSweeping() { return helperState.isBackgroundSweeping(); } void waitBackgroundSweepEnd() { helperState.waitBackgroundSweepEnd(); } void waitBackgroundSweepOrAllocEnd() { @@ -1209,8 +1204,6 @@ class GCRuntime bool poked; - volatile js::HeapState heapState; - /* * These options control the zealousness of the GC. The fundamental values * are nextScheduled and gcDebugCompartmentGC. At every allocation, diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 4e4dcbe463..8284426dfd 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -378,28 +378,13 @@ js::TenuringTracer::TenuringTracer(JSRuntime* rt, Nursery* nursery) , tenuredSize(0) , head(nullptr) , tail(&head) - , savedRuntimeNeedBarrier(rt->needsIncrementalBarrier()) { rt->gc.incGcNumber(); - - // We disable the runtime needsIncrementalBarrier() check so that - // pre-barriers do not fire on objects that have been relocated. The - // pre-barrier's call to obj->zone() will try to look through shape_, - // which is now the relocation magic and will crash. However, - // zone->needsIncrementalBarrier() must still be set correctly so that - // allocations we make in minor GCs between incremental slices will - // allocate their objects marked. - rt->setNeedsIncrementalBarrier(false); } -js::TenuringTracer::~TenuringTracer() -{ - runtime()->setNeedsIncrementalBarrier(savedRuntimeNeedBarrier); -} - -#define TIME_START(name) int64_t timstampStart_##name = enableProfiling_ ? PRMJ_Now() : 0 -#define TIME_END(name) int64_t timstampEnd_##name = enableProfiling_ ? PRMJ_Now() : 0 -#define TIME_TOTAL(name) (timstampEnd_##name - timstampStart_##name) +#define TIME_START(name) int64_t timestampStart_##name = enableProfiling_ ? PRMJ_Now() : 0 +#define TIME_END(name) int64_t timestampEnd_##name = enableProfiling_ ? PRMJ_Now() : 0 +#define TIME_TOTAL(name) (timestampEnd_##name - timestampStart_##name) void js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList* pretenureGroups) @@ -427,9 +412,9 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList TraceMinorGCStart(); - TIME_START(total); + int64_t timestampStart_total = PRMJ_Now(); - AutoTraceSession session(rt, MinorCollecting); + AutoTraceSession session(rt, JS::HeapState::MinorCollecting); AutoStopVerifyingBarriers av(rt, false); AutoDisableProxyCheck disableStrictProxyChecking(rt); mozilla::DebugOnly oomUnsafeRegion; @@ -554,11 +539,10 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList if (rt->gc.usage.gcBytes() >= rt->gc.tunables.gcMaxBytes()) disable(); - TIME_END(total); + int64_t totalTime = PRMJ_Now() - timestampStart_total; TraceMinorGCEnd(); - int64_t totalTime = TIME_TOTAL(total); if (enableProfiling_ && totalTime >= profileThreshold_) { static bool printedHeader = false; if (!printedHeader) { diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index ef991d815c..005680ec6a 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -60,11 +60,7 @@ class TenuringTracer : public JSTracer gc::RelocationOverlay* head; gc::RelocationOverlay** tail; - // Save and restore all of the runtime state we use during MinorGC. - bool savedRuntimeNeedBarrier; - TenuringTracer(JSRuntime* rt, Nursery* nursery); - ~TenuringTracer(); public: const Nursery& nursery() const { return nursery_; } diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 846912c0a0..6ec2ebb3bd 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -456,7 +456,7 @@ js::gc::GCRuntime::markRuntime(JSTracer* trc, TraceRoot(trc, &vec[i].script, "scriptAndCountsVector"); } - if (!rt->isBeingDestroyed() && !trc->runtime()->isHeapMinorCollecting()) { + if (!rt->isBeingDestroyed() && !rt->isHeapMinorCollecting()) { gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_RUNTIME_DATA); if (traceOrMark == TraceRuntime || rt->atomsCompartment()->zone()->isCollecting()) { @@ -475,7 +475,7 @@ js::gc::GCRuntime::markRuntime(JSTracer* trc, continue; /* Do not discard scripts with counts while profiling. */ - if (rt->profilingScripts && !isHeapMinorCollecting()) { + if (rt->profilingScripts && !rt->isHeapMinorCollecting()) { for (ZoneCellIterUnderGC i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { JSScript* script = i.get(); if (script->hasScriptCounts()) { @@ -488,7 +488,7 @@ js::gc::GCRuntime::markRuntime(JSTracer* trc, /* We can't use GCCompartmentsIter if we're called from TraceRuntime. */ for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { - if (trc->runtime()->isHeapMinorCollecting()) + if (rt->isHeapMinorCollecting()) c->globalWriteBarriered = false; if (traceOrMark == MarkRuntime && !c->zone()->isCollecting()) @@ -515,7 +515,7 @@ js::gc::GCRuntime::markRuntime(JSTracer* trc, jit::MarkJitActivations(rt, trc); - if (!isHeapMinorCollecting()) { + if (!rt->isHeapMinorCollecting()) { gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_EMBEDDING); /* diff --git a/js/src/gc/Verifier.cpp b/js/src/gc/Verifier.cpp index d4d0e78b81..b2b1977822 100644 --- a/js/src/gc/Verifier.cpp +++ b/js/src/gc/Verifier.cpp @@ -231,7 +231,6 @@ gc::GCRuntime::startVerifyPreBarriers() incrementalState = MARK; marker.start(); - rt->setNeedsIncrementalBarrier(true); for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { PurgeJITCaches(zone); zone->setNeedsIncrementalBarrier(true, Zone::UpdateJit); @@ -328,7 +327,6 @@ gc::GCRuntime::endVerifyPreBarriers() zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit); PurgeJITCaches(zone); } - rt->setNeedsIncrementalBarrier(false); /* * We need to bump gcNumber so that the methodjit knows that jitcode has diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index c0cb0d574f..601871d04b 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -133,8 +133,8 @@ struct Zone : public JS::shadow::Zone, bool isTooMuchMalloc() const { return gcMallocBytes <= 0; } void onTooMuchMalloc(); - void* onOutOfMemory(void* p, size_t nbytes) { - return runtimeFromMainThread()->onOutOfMemory(p, nbytes); + void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, void* reallocPtr = nullptr) { + return runtimeFromMainThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr); } void reportAllocationOverflow() { js::ReportAllocationOverflow(nullptr); } @@ -187,7 +187,7 @@ struct Zone : public JS::shadow::Zone, // tracer. bool requireGCTracer() const { JSRuntime* rt = runtimeFromAnyThread(); - return rt->isHeapMajorCollecting() && !rt->isHeapCompacting() && gcState_ != NoGC; + return rt->isHeapMajorCollecting() && !rt->gc.isHeapCompacting() && gcState_ != NoGC; } bool isGCMarking() { diff --git a/js/src/jit-test/tests/baseline/bug1153458.js b/js/src/jit-test/tests/baseline/bug1153458.js new file mode 100644 index 0000000000..aadc9f40b1 --- /dev/null +++ b/js/src/jit-test/tests/baseline/bug1153458.js @@ -0,0 +1,13 @@ +// |jit-test| baseline-eager; error: TypeError +try { + __defineGetter__("x", Iterator)() +} catch (e) {} +f = function() { + return (function() { + this.x + }) +}() +try { + f() +} catch (e) {} +f() diff --git a/js/src/jit-test/tests/basic/unboxed-object-getelem.js b/js/src/jit-test/tests/basic/unboxed-object-getelem.js new file mode 100644 index 0000000000..b30b8325a0 --- /dev/null +++ b/js/src/jit-test/tests/basic/unboxed-object-getelem.js @@ -0,0 +1,20 @@ + +function f() { + var propNames = ["a","b","c","d","e","f","g","h","i","j","x","y"]; + var arr = []; + for (var i=0; i<64; i++) + arr.push({x:1, y:2}); + for (var i=0; i<64; i++) { + // Make sure there are expandos with dynamic slots for each object. + for (var j = 0; j < propNames.length; j++) + arr[i][propNames[j]] = j; + } + var res = 0; + for (var i=0; i<100000; i++) { + var o = arr[i % 64]; + var p = propNames[i % propNames.length]; + res += o[p]; + } + assertEq(res, 549984); +} +f(); diff --git a/js/src/jit/BaselineDebugModeOSR.cpp b/js/src/jit/BaselineDebugModeOSR.cpp index a7fba842e9..05a97604cb 100644 --- a/js/src/jit/BaselineDebugModeOSR.cpp +++ b/js/src/jit/BaselineDebugModeOSR.cpp @@ -679,8 +679,10 @@ RecompileBaselineScriptForDebugMode(JSContext* cx, JSScript* script, _(Call_ScriptedApplyArray) \ _(Call_ScriptedApplyArguments) \ _(Call_ScriptedFunCall) \ - _(GetElem_NativePrototypeCallNative) \ - _(GetElem_NativePrototypeCallScripted) \ + _(GetElem_NativePrototypeCallNativeName) \ + _(GetElem_NativePrototypeCallNativeSymbol) \ + _(GetElem_NativePrototypeCallScriptedName) \ + _(GetElem_NativePrototypeCallScriptedSymbol) \ _(GetProp_CallScripted) \ _(GetProp_CallNative) \ _(GetProp_CallDOMProxyNative) \ diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 330bc90045..f3074e8d9b 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -45,6 +45,44 @@ using mozilla::DebugOnly; namespace js { namespace jit { +static void +GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard, + Register object, Register scratch, + size_t receiverGuardOffset, Label* failure) +{ + Address groupAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfGroup()); + Address shapeAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfShape()); + Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); + + if (guard.group) { + masm.loadPtr(groupAddress, scratch); + masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure); + + if (guard.group->clasp() == &UnboxedPlainObject::class_ && !guard.shape) { + // Guard the unboxed object has no expando object. + masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure); + } + } + + if (guard.shape) { + masm.loadPtr(shapeAddress, scratch); + if (guard.group && guard.group->clasp() == &UnboxedPlainObject::class_) { + // Guard the unboxed object has a matching expando object. + masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); + Label done; + masm.push(object); + masm.loadPtr(expandoAddress, object); + masm.branchTestObjShape(Assembler::Equal, object, scratch, &done); + masm.pop(object); + masm.jump(failure); + masm.bind(&done); + masm.pop(object); + } else { + masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); + } + } +} + // // WarmUpCounter_Fallback // @@ -2610,7 +2648,7 @@ CheckDOMProxyExpandoDoesNotShadow(JSContext* cx, MacroAssembler& masm, Register // operations. This procedure not yielding a shape should not be taken as a lack of // existence of the property on the object. static bool -EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, +EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleObject holder, MutableHandleShape shape, bool* checkDOMProxy=nullptr, DOMProxyShadowsResult* shadowsResult=nullptr, @@ -2634,7 +2672,6 @@ EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandlePropertyName n if (obj->hasUncacheableProto()) return true; - RootedId id(cx, NameToId(name)); *shadowsResult = GetDOMProxyShadowsCheck()(cx, obj, id); if (*shadowsResult == ShadowCheckFailed) return false; @@ -2651,7 +2688,7 @@ EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandlePropertyName n return true; } - if (LookupPropertyPure(cx, checkObj, NameToId(name), holder.address(), shape.address())) + if (LookupPropertyPure(cx, checkObj, id, holder.address(), shape.address())) return true; holder.set(nullptr); @@ -2944,33 +2981,50 @@ typedef bool (*LookupNoSuchMethodHandlerFn)(JSContext*, HandleObject, HandleValu static const VMFunction LookupNoSuchMethodHandlerInfo = FunctionInfo(LookupNoSuchMethodHandler); + +template static bool -GetElemNativeStubExists(ICGetElem_Fallback* stub, HandleNativeObject obj, HandleNativeObject holder, - HandlePropertyName propName, bool needsAtomize) +GetElemNativeStubExists(ICGetElem_Fallback* stub, HandleObject obj, HandleObject holder, + Handle key, bool needsAtomize) { bool indirect = (obj.get() != holder.get()); + MOZ_ASSERT_IF(indirect, holder->isNative()); for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) { - if (iter->kind() != ICStub::GetElem_NativeSlot && - iter->kind() != ICStub::GetElem_NativePrototypeSlot && - iter->kind() != ICStub::GetElem_NativePrototypeCallNative && - iter->kind() != ICStub::GetElem_NativePrototypeCallScripted) + if (iter->kind() != ICStub::GetElem_NativeSlotName && + iter->kind() != ICStub::GetElem_NativeSlotSymbol && + iter->kind() != ICStub::GetElem_NativePrototypeSlotName && + iter->kind() != ICStub::GetElem_NativePrototypeSlotSymbol && + iter->kind() != ICStub::GetElem_NativePrototypeCallNativeName && + iter->kind() != ICStub::GetElem_NativePrototypeCallNativeSymbol && + iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedName && + iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedSymbol) { continue; } - if (indirect && (iter->kind() != ICStub::GetElem_NativePrototypeSlot && - iter->kind() != ICStub::GetElem_NativePrototypeCallNative && - iter->kind() != ICStub::GetElem_NativePrototypeCallScripted)) + if (indirect && (iter->kind() != ICStub::GetElem_NativePrototypeSlotName && + iter->kind() != ICStub::GetElem_NativePrototypeSlotSymbol && + iter->kind() != ICStub::GetElem_NativePrototypeCallNativeName && + iter->kind() != ICStub::GetElem_NativePrototypeCallNativeSymbol && + iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedName && + iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedSymbol)) { continue; } - ICGetElemNativeStub* getElemNativeStub = reinterpret_cast(*iter); - if (propName != getElemNativeStub->name()) + if(mozilla::IsSame::value != + static_cast(*iter)->isSymbol()) + { + continue; + } + + ICGetElemNativeStubImpl* getElemNativeStub = + reinterpret_cast*>(*iter); + if (key != getElemNativeStub->key()) continue; - if (obj->lastProperty() != getElemNativeStub->shape()) + if (ReceiverGuard(obj) != getElemNativeStub->receiverGuard()) continue; // If the new stub needs atomization, and the old stub doesn't atomize, then @@ -2980,25 +3034,29 @@ GetElemNativeStubExists(ICGetElem_Fallback* stub, HandleNativeObject obj, Handle // For prototype gets, check the holder and holder shape. if (indirect) { - if (iter->isGetElem_NativePrototypeSlot()) { - ICGetElem_NativePrototypeSlot* protoStub = iter->toGetElem_NativePrototypeSlot(); + if (iter->isGetElem_NativePrototypeSlotName() || + iter->isGetElem_NativePrototypeSlotSymbol()) { + ICGetElem_NativePrototypeSlot* protoStub = + reinterpret_cast*>(*iter); if (holder != protoStub->holder()) continue; - if (holder->lastProperty() != protoStub->holderShape()) + if (holder->as().lastProperty() != protoStub->holderShape()) continue; } else { - MOZ_ASSERT(iter->isGetElem_NativePrototypeCallNative() || - iter->isGetElem_NativePrototypeCallScripted()); + MOZ_ASSERT(iter->isGetElem_NativePrototypeCallNativeName() || + iter->isGetElem_NativePrototypeCallNativeSymbol() || + iter->isGetElem_NativePrototypeCallScriptedName() || + iter->isGetElem_NativePrototypeCallScriptedSymbol()); - ICGetElemNativePrototypeCallStub* protoStub = - reinterpret_cast(*iter); + ICGetElemNativePrototypeCallStub* protoStub = + reinterpret_cast*>(*iter); if (holder != protoStub->holder()) continue; - if (holder->lastProperty() != protoStub->holderShape()) + if (holder->as().lastProperty() != protoStub->holderShape()) continue; } } @@ -3008,60 +3066,75 @@ GetElemNativeStubExists(ICGetElem_Fallback* stub, HandleNativeObject obj, Handle return false; } +template static void -RemoveExistingGetElemNativeStubs(JSContext* cx, ICGetElem_Fallback* stub, HandleNativeObject obj, - HandleNativeObject holder, HandlePropertyName propName, - bool needsAtomize) +RemoveExistingGetElemNativeStubs(JSContext* cx, ICGetElem_Fallback* stub, HandleObject obj, + HandleObject holder, Handle key, bool needsAtomize) { bool indirect = (obj.get() != holder.get()); + MOZ_ASSERT_IF(indirect, holder->isNative()); for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) { switch (iter->kind()) { - case ICStub::GetElem_NativeSlot: + case ICStub::GetElem_NativeSlotName: + case ICStub::GetElem_NativeSlotSymbol: if (indirect) continue; - case ICStub::GetElem_NativePrototypeSlot: - case ICStub::GetElem_NativePrototypeCallNative: - case ICStub::GetElem_NativePrototypeCallScripted: + case ICStub::GetElem_NativePrototypeSlotName: + case ICStub::GetElem_NativePrototypeSlotSymbol: + case ICStub::GetElem_NativePrototypeCallNativeName: + case ICStub::GetElem_NativePrototypeCallNativeSymbol: + case ICStub::GetElem_NativePrototypeCallScriptedName: + case ICStub::GetElem_NativePrototypeCallScriptedSymbol: break; default: continue; } - ICGetElemNativeStub* getElemNativeStub = reinterpret_cast(*iter); - if (propName != getElemNativeStub->name()) + if(mozilla::IsSame::value != + static_cast(*iter)->isSymbol()) + { + continue; + } + + ICGetElemNativeStubImpl* getElemNativeStub = + reinterpret_cast*>(*iter); + if (key != getElemNativeStub->key()) continue; - if (obj->lastProperty() != getElemNativeStub->shape()) + if (ReceiverGuard(obj) != getElemNativeStub->receiverGuard()) continue; // For prototype gets, check the holder and holder shape. if (indirect) { - if (iter->isGetElem_NativePrototypeSlot()) { - ICGetElem_NativePrototypeSlot* protoStub = iter->toGetElem_NativePrototypeSlot(); + if (iter->isGetElem_NativePrototypeSlotName() || + iter->isGetElem_NativePrototypeSlotSymbol()) { + ICGetElem_NativePrototypeSlot* protoStub = + reinterpret_cast*>(*iter); if (holder != protoStub->holder()) continue; // If the holder matches, but the holder's lastProperty doesn't match, then // this stub is invalid anyway. Unlink it. - if (holder->lastProperty() != protoStub->holderShape()) { + if (holder->as().lastProperty() != protoStub->holderShape()) { iter.unlink(cx); continue; } } else { - MOZ_ASSERT(iter->isGetElem_NativePrototypeCallNative() || - iter->isGetElem_NativePrototypeCallScripted()); - - ICGetElemNativePrototypeCallStub* protoStub = - reinterpret_cast(*iter); + MOZ_ASSERT(iter->isGetElem_NativePrototypeCallNativeName() || + iter->isGetElem_NativePrototypeCallNativeSymbol() || + iter->isGetElem_NativePrototypeCallScriptedName() || + iter->isGetElem_NativePrototypeCallScriptedSymbol()); + ICGetElemNativePrototypeCallStub* protoStub = + reinterpret_cast*>(*iter); if (holder != protoStub->holder()) continue; // If the holder matches, but the holder's lastProperty doesn't match, then // this stub is invalid anyway. Unlink it. - if (holder->lastProperty() != protoStub->holderShape()) { + if (holder->as().lastProperty() != protoStub->holderShape()) { iter.unlink(cx); continue; } @@ -3105,6 +3178,31 @@ ArgumentsGetElemStubExists(ICGetElem_Fallback* stub, ICGetElem_Arguments::Which return false; } +template +static T +getKey(jsid id) +{ + MOZ_ASSERT_UNREACHABLE("Key has to be PropertyName or Symbol"); + return false; +} + +template <> +JS::Symbol* getKey(jsid id) +{ + if (!JSID_IS_SYMBOL(id)) + return nullptr; + return JSID_TO_SYMBOL(id); +} + +template <> +PropertyName* getKey(jsid id) +{ + uint32_t dummy; + if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy)) + return nullptr; + return JSID_TO_ATOM(id)->asPropertyName(); +} + static bool IsOptimizableElementPropertyName(JSContext* cx, HandleValue key, MutableHandleId idp) { @@ -3122,82 +3220,168 @@ IsOptimizableElementPropertyName(JSContext* cx, HandleValue key, MutableHandleId return true; } +template static bool -TryAttachNativeGetValueElemStub(JSContext* cx, HandleScript script, jsbytecode* pc, - ICGetElem_Fallback* stub, HandleNativeObject obj, - HandleValue key, bool* attached) +checkAtomize(HandleValue key) { - RootedId id(cx); - if (!IsOptimizableElementPropertyName(cx, key, &id)) - return true; + MOZ_ASSERT_UNREACHABLE("Key has to be PropertyName or Symbol"); + return false; +} - RootedPropertyName propName(cx, JSID_TO_ATOM(id)->asPropertyName()); - bool needsAtomize = !key.toString()->isAtom(); +template <> +bool checkAtomize(HandleValue key) +{ + return false; +} + +template <> +bool checkAtomize(HandleValue key) +{ + return !key.toString()->isAtom(); +} + +template +static bool +TryAttachNativeOrUnboxedGetValueElemStub(JSContext* cx, HandleScript script, jsbytecode* pc, + ICGetElem_Fallback* stub, HandleObject obj, + HandleValue keyVal, bool* attached) +{ + MOZ_ASSERT(keyVal.isString() || keyVal.isSymbol()); + + // Convert to id. + RootedId id(cx); + if (!ValueToId(cx, keyVal, &id)) + return false; + + Rooted key(cx, getKey(id)); + if (!key) + return true; + bool needsAtomize = checkAtomize(keyVal); bool isCallElem = (JSOp(*pc) == JSOP_CALLELEM); RootedShape shape(cx); - RootedObject baseHolder(cx); - if (!EffectlesslyLookupProperty(cx, obj, propName, &baseHolder, &shape)) + RootedObject holder(cx); + if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape)) return false; - if (!baseHolder || !baseHolder->isNative()) + if (!holder || (holder != obj && !holder->isNative())) return true; - HandleNativeObject holder = baseHolder.as(); + // If a suitable stub already exists, nothing else to do. + if (GetElemNativeStubExists(stub, obj, holder, key, needsAtomize)) + return true; - if (IsCacheableGetPropReadSlot(obj, holder, shape)) { - // If a suitable stub already exists, nothing else to do. - if (GetElemNativeStubExists(stub, obj, holder, propName, needsAtomize)) + // Remove any existing stubs that may interfere with the new stub being added. + RemoveExistingGetElemNativeStubs(cx, stub, obj, holder, key, needsAtomize); + + ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); + + if (obj->is() && holder == obj) { + const UnboxedLayout::Property* property = obj->as().layout().lookup(id); + + // Once unboxed objects support symbol-keys, we need to change the following accordingly + MOZ_ASSERT_IF(!keyVal.isString(), !property); + + if (property) { + if (!cx->runtime()->jitSupportsFloatingPoint) + return true; + + RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName()); + ICGetElemNativeCompiler compiler(cx, ICStub::GetElem_UnboxedPropertyName, + isCallElem, monitorStub, obj, holder, + name, + ICGetElemNativeStub::UnboxedProperty, + needsAtomize, property->offset + + UnboxedPlainObject::offsetOfData(), + property->type); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + stub->addNewStub(newStub); + *attached = true; return true; + } - // Remove any existing stubs that may interfere with the new stub being added. - RemoveExistingGetElemNativeStubs(cx, stub, obj, holder, propName, needsAtomize); + Shape* shape = obj->as().maybeExpando()->lookup(cx, id); + if (!shape->hasDefaultGetter() || !shape->hasSlot()) + return true; bool isFixedSlot; uint32_t offset; GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); - ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - ICStub::Kind kind = (obj == holder) ? ICStub::GetElem_NativeSlot - : ICStub::GetElem_NativePrototypeSlot; - - JitSpew(JitSpew_BaselineIC, " Generating GetElem(Native %s%s slot) stub " - "(obj=%p, shape=%p, holder=%p, holderShape=%p)", - (obj == holder) ? "direct" : "prototype", - needsAtomize ? " atomizing" : "", - obj.get(), obj->lastProperty(), holder.get(), holder->lastProperty()); - - ICGetElemNativeStub::AccessType acctype = isFixedSlot ? ICGetElemNativeStub::FixedSlot - : ICGetElemNativeStub::DynamicSlot; - ICGetElemNativeCompiler compiler(cx, kind, isCallElem, monitorStub, obj, holder, propName, - acctype, needsAtomize, offset); + ICGetElemNativeStub::AccessType acctype = + isFixedSlot ? ICGetElemNativeStub::FixedSlot + : ICGetElemNativeStub::DynamicSlot; + ICGetElemNativeCompiler compiler(cx, getGetElemStubKind(ICStub::GetElem_NativeSlotName), + isCallElem, monitorStub, obj, holder, key, + acctype, needsAtomize, offset); ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); if (!newStub) return false; stub->addNewStub(newStub); *attached = true; + return true; } + + if (!holder->isNative()) + return true; + + if (IsCacheableGetPropReadSlot(obj, holder, shape)) { + bool isFixedSlot; + uint32_t offset; + GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); + + ICStub::Kind kind = (obj == holder) ? ICStub::GetElem_NativeSlotName + : ICStub::GetElem_NativePrototypeSlotName; + kind = getGetElemStubKind(kind); + + JitSpew(JitSpew_BaselineIC, " Generating GetElem(Native %s%s slot) stub " + "(obj=%p, holder=%p, holderShape=%p)", + (obj == holder) ? "direct" : "prototype", + needsAtomize ? " atomizing" : "", + obj.get(), holder.get(), holder->as().lastProperty()); + + AccType acctype = isFixedSlot ? ICGetElemNativeStub::FixedSlot + : ICGetElemNativeStub::DynamicSlot; + ICGetElemNativeCompiler compiler(cx, kind, isCallElem, monitorStub, obj, holder, key, + acctype, needsAtomize, offset); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + stub->addNewStub(newStub); + *attached = true; + return true; + } + return true; } +template static bool TryAttachNativeGetAccessorElemStub(JSContext* cx, HandleScript script, jsbytecode* pc, ICGetElem_Fallback* stub, HandleNativeObject obj, - HandleValue key, bool* attached, bool* isTemporarilyUnoptimizable) + HandleValue keyVal, bool* attached, + bool* isTemporarilyUnoptimizable) { MOZ_ASSERT(!*attached); + MOZ_ASSERT(keyVal.isString() || keyVal.isSymbol()); RootedId id(cx); - if (!IsOptimizableElementPropertyName(cx, key, &id)) - return true; + if (!ValueToId(cx, keyVal, &id)) + return false; - RootedPropertyName propName(cx, JSID_TO_ATOM(id)->asPropertyName()); - bool needsAtomize = !key.toString()->isAtom(); + Rooted key(cx, getKey(id)); + if (!key) + return true; + bool needsAtomize = checkAtomize(keyVal); bool isCallElem = (JSOp(*pc) == JSOP_CALLELEM); RootedShape shape(cx); RootedObject baseHolder(cx); - if (!EffectlesslyLookupProperty(cx, obj, propName, &baseHolder, &shape)) + if (!EffectlesslyLookupProperty(cx, obj, id, &baseHolder, &shape)) return false; if (!baseHolder || baseHolder->isNative()) return true; @@ -3222,15 +3406,16 @@ TryAttachNativeGetAccessorElemStub(JSContext* cx, HandleScript script, jsbytecod return true; // If a suitable stub already exists, nothing else to do. - if (GetElemNativeStubExists(stub, obj, holder, propName, needsAtomize)) + if (GetElemNativeStubExists(stub, obj, holder, key, needsAtomize)) return true; // Remove any existing stubs that may interfere with the new stub being added. - RemoveExistingGetElemNativeStubs(cx, stub, obj, holder, propName, needsAtomize); + RemoveExistingGetElemNativeStubs(cx, stub, obj, holder, key, needsAtomize); ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - ICStub::Kind kind = getterIsScripted ? ICStub::GetElem_NativePrototypeCallScripted - : ICStub::GetElem_NativePrototypeCallNative; + ICStub::Kind kind = getterIsScripted ? ICStub::GetElem_NativePrototypeCallScriptedName + : ICStub::GetElem_NativePrototypeCallNativeName; + kind = getGetElemStubKind(kind); if (getterIsScripted) { JitSpew(JitSpew_BaselineIC, @@ -3249,10 +3434,9 @@ TryAttachNativeGetAccessorElemStub(JSContext* cx, HandleScript script, jsbytecod obj.get(), obj->lastProperty(), holder.get(), holder->lastProperty()); } - ICGetElemNativeStub::AccessType acctype = getterIsScripted - ? ICGetElemNativeStub::ScriptedGetter - : ICGetElemNativeStub::NativeGetter; - ICGetElemNativeCompiler compiler(cx, kind, monitorStub, obj, holder, propName, acctype, + AccType acctype = getterIsScripted ? ICGetElemNativeStub::ScriptedGetter + : ICGetElemNativeStub::NativeGetter; + ICGetElemNativeCompiler compiler(cx, kind, monitorStub, obj, holder, key, acctype, needsAtomize, getter, script->pcToOffset(pc), isCallElem); ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); if (!newStub) @@ -3399,13 +3583,21 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ return true; } - // Check for NativeObject[id] shape-optimizable accesses. - if (obj->isNative() && rhs.isString()) { + // Check for NativeObject[id] and UnboxedPlainObject[id] shape-optimizable accesses. + if (obj->isNative() || obj->is()) { RootedScript rootedScript(cx, script); - if (!TryAttachNativeGetValueElemStub(cx, rootedScript, pc, stub, - obj.as(), rhs, attached)) - { - return false; + if (rhs.isString()) { + if (!TryAttachNativeOrUnboxedGetValueElemStub(cx, rootedScript, pc, stub, + obj, rhs, attached)) + { + return false; + } + } else if (rhs.isSymbol()) { + if (!TryAttachNativeOrUnboxedGetValueElemStub(cx, rootedScript, pc, stub, + obj, rhs, attached)) + { + return false; + } } if (*attached) return true; @@ -3511,15 +3703,28 @@ DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_ // Try to attach an optimized getter stub. bool isTemporarilyUnoptimizable = false; - if (!attached && lhs.isObject() && lhs.toObject().isNative() && rhs.isString()){ - RootedScript rootedScript(cx, frame->script()); - RootedNativeObject obj(cx, &lhs.toObject().as()); - if (!TryAttachNativeGetAccessorElemStub(cx, rootedScript, pc, stub, obj, rhs, &attached, - &isTemporarilyUnoptimizable)) - { - return false; + if (!attached && lhs.isObject() && lhs.toObject().isNative()){ + if (rhs.isString()) { + RootedScript rootedScript(cx, frame->script()); + RootedNativeObject obj(cx, &lhs.toObject().as()); + if (!TryAttachNativeGetAccessorElemStub(cx, rootedScript, pc, stub, + obj, rhs, &attached, + &isTemporarilyUnoptimizable)) + { + return false; + } + script = rootedScript; + } else if (rhs.isSymbol()) { + RootedScript rootedScript(cx, frame->script()); + RootedNativeObject obj(cx, &lhs.toObject().as()); + if (!TryAttachNativeGetAccessorElemStub(cx, rootedScript, pc, stub, + obj, rhs, &attached, + &isTemporarilyUnoptimizable)) + { + return false; + } + script = rootedScript; } - script = rootedScript; } if (!isOptimizedArgs) { @@ -3604,8 +3809,9 @@ DoAtomizeString(JSContext* cx, HandleString string, MutableHandleValue result) typedef bool (*DoAtomizeStringFn)(JSContext*, HandleString, MutableHandleValue); static const VMFunction DoAtomizeStringInfo = FunctionInfo(DoAtomizeString); +template bool -ICGetElemNativeCompiler::emitCallNative(MacroAssembler& masm, Register objReg) +ICGetElemNativeCompiler::emitCallNative(MacroAssembler& masm, Register objReg) { AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); regs.takeUnchecked(objReg); @@ -3617,7 +3823,7 @@ ICGetElemNativeCompiler::emitCallNative(MacroAssembler& masm, Register objReg) masm.push(objReg); // Push native callee. - masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub::offsetOfGetter()), objReg); + masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub::offsetOfGetter()), objReg); masm.push(objReg); regs.add(objReg); @@ -3631,8 +3837,9 @@ ICGetElemNativeCompiler::emitCallNative(MacroAssembler& masm, Register objReg) return true; } +template bool -ICGetElemNativeCompiler::emitCallScripted(MacroAssembler& masm, Register objReg) +ICGetElemNativeCompiler::emitCallScripted(MacroAssembler& masm, Register objReg) { AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); regs.takeUnchecked(objReg); @@ -3656,7 +3863,7 @@ ICGetElemNativeCompiler::emitCallScripted(MacroAssembler& masm, Register objReg) regs.add(objReg); Register callee = regs.takeAny(); - masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub::offsetOfGetter()), callee); + masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub::offsetOfGetter()), callee); // Push argc, callee, and descriptor. { @@ -3698,32 +3905,34 @@ ICGetElemNativeCompiler::emitCallScripted(MacroAssembler& masm, Register objReg) return true; } +template bool -ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) +ICGetElemNativeCompiler::emitCheckKey(MacroAssembler& masm, Label& failure) { - MOZ_ASSERT(engine_ == Engine::Baseline); + MOZ_ASSERT_UNREACHABLE("Key has to be PropertyName or Symbol"); + return false; +} - Label failure; - Label failurePopR1; - bool popR1 = false; +template <> +bool +ICGetElemNativeCompiler::emitCheckKey(MacroAssembler& masm, Label& failure) +{ + MOZ_ASSERT(!needsAtomize_); + masm.branchTestSymbol(Assembler::NotEqual, R1, &failure); + Address symbolAddr(ICStubReg, ICGetElemNativeStubImpl::offsetOfKey()); + Register symExtract = masm.extractObject(R1, ExtractTemp1); + masm.branchPtr(Assembler::NotEqual, symbolAddr, symExtract, &failure); + return true; +} - masm.branchTestObject(Assembler::NotEqual, R0, &failure); +template <> +bool +ICGetElemNativeCompiler::emitCheckKey(MacroAssembler& masm, Label& failure) +{ masm.branchTestString(Assembler::NotEqual, R1, &failure); - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); - Register scratchReg = regs.takeAny(); - - // Unbox object. - Register objReg = masm.extractObject(R0, ExtractTemp0); - - // Check object shape. - masm.loadPtr(Address(objReg, JSObject::offsetOfShape()), scratchReg); - Address shapeAddr(ICStubReg, ICGetElemNativeStub::offsetOfShape()); - masm.branchPtr(Assembler::NotEqual, shapeAddr, scratchReg, &failure); - // Check key identity. Don't automatically fail if this fails, since the incoming // key maybe a non-interned string. Switch to a slowpath vm-call based check. - Address nameAddr(ICStubReg, ICGetElemNativeStub::offsetOfName()); + Address nameAddr(ICStubReg, ICGetElemNativeStubImpl::offsetOfKey()); Register strExtract = masm.extractString(R1, ExtractTemp1); // If needsAtomize_ is true, and the string is not already an atom, then atomize the @@ -3763,17 +3972,52 @@ ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) masm.bind(&skipAtomize); } - // Since this stub sometimes enter a stub frame, we manually set this to true (lie). + // Key has been atomized if necessary. Do identity check on string pointer. + masm.branchPtr(Assembler::NotEqual, nameAddr, strExtract, &failure); + return true; +} + +template +bool +ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) +{ + MOZ_ASSERT(engine_ == Engine::Baseline); + + Label failure; + Label failurePopR1; + bool popR1 = false; + + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + + AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); + Register scratchReg = regs.takeAny(); + + // Unbox object. + Register objReg = masm.extractObject(R0, ExtractTemp0); + + // Check object shape/group. + GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratchReg, + ICGetElemNativeStub::offsetOfReceiverGuard(), &failure); + + // Since this stub sometimes enters a stub frame, we manually set this to true (lie). #ifdef DEBUG entersStubFrame_ = true; #endif - // Key has been atomized if necessary. Do identity check on string pointer. - masm.branchPtr(Assembler::NotEqual, nameAddr, strExtract, &failure); + if (!emitCheckKey(masm, failure)) + return false; Register holderReg; if (obj_ == holder_) { holderReg = objReg; + + if (obj_->is() && acctype_ != ICGetElemNativeStub::UnboxedProperty) { + // The property will be loaded off the unboxed expando. + masm.push(R1.scratchReg()); + popR1 = true; + holderReg = R1.scratchReg(); + masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg); + } } else { // Shape guard holder. if (regs.empty()) { @@ -3784,21 +4028,23 @@ ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) holderReg = regs.takeAny(); } - if (kind == ICStub::GetElem_NativePrototypeCallNative || - kind == ICStub::GetElem_NativePrototypeCallScripted) + if (kind == ICStub::GetElem_NativePrototypeCallNativeName || + kind == ICStub::GetElem_NativePrototypeCallNativeSymbol || + kind == ICStub::GetElem_NativePrototypeCallScriptedName || + kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol) { masm.loadPtr(Address(ICStubReg, - ICGetElemNativePrototypeCallStub::offsetOfHolder()), + ICGetElemNativePrototypeCallStub::offsetOfHolder()), holderReg); masm.loadPtr(Address(ICStubReg, - ICGetElemNativePrototypeCallStub::offsetOfHolderShape()), + ICGetElemNativePrototypeCallStub::offsetOfHolderShape()), scratchReg); } else { masm.loadPtr(Address(ICStubReg, - ICGetElem_NativePrototypeSlot::offsetOfHolder()), + ICGetElem_NativePrototypeSlot::offsetOfHolder()), holderReg); masm.loadPtr(Address(ICStubReg, - ICGetElem_NativePrototypeSlot::offsetOfHolderShape()), + ICGetElem_NativePrototypeSlot::offsetOfHolderShape()), scratchReg); } masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratchReg, @@ -3808,7 +4054,7 @@ ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) if (acctype_ == ICGetElemNativeStub::DynamicSlot || acctype_ == ICGetElemNativeStub::FixedSlot) { - masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub::offsetOfOffset()), + masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub::offsetOfOffset()), scratchReg); // Load from object. @@ -3872,11 +4118,20 @@ ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) masm.addToStackPtr(ImmWord(sizeof(size_t))); #endif + } else if (acctype_ == ICGetElemNativeStub::UnboxedProperty) { + masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub::offsetOfOffset()), + scratchReg); + masm.loadUnboxedProperty(BaseIndex(objReg, scratchReg, TimesOne), unboxedType_, + TypedOrValueRegister(R0)); + if (popR1) + masm.addPtr(ImmWord(sizeof(size_t)), BaselineStackReg); } else { MOZ_ASSERT(acctype_ == ICGetElemNativeStub::NativeGetter || acctype_ == ICGetElemNativeStub::ScriptedGetter); - MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallNative || - kind == ICStub::GetElem_NativePrototypeCallScripted); + MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallNativeName || + kind == ICStub::GetElem_NativePrototypeCallNativeSymbol || + kind == ICStub::GetElem_NativePrototypeCallScriptedName || + kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol); if (acctype_ == ICGetElemNativeStub::NativeGetter) { // If calling a native getter, there is no chance of failure now. @@ -3891,7 +4146,7 @@ ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) MOZ_ASSERT(acctype_ == ICGetElemNativeStub::ScriptedGetter); // Load function in scratchReg and ensure that it has a jit script. - masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub::offsetOfGetter()), + masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub::offsetOfGetter()), scratchReg); masm.branchIfFunctionHasNoScript(scratchReg, popR1 ? &failurePopR1 : &failure); masm.loadPtr(Address(scratchReg, JSFunction::offsetOfNativeOrScript()), scratchReg); @@ -5395,7 +5650,7 @@ TryAttachNativeInStub(JSContext* cx, HandleScript script, ICIn_Fallback* stub, RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName()); RootedShape shape(cx); RootedObject holder(cx); - if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape)) + if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape)) return false; if (IsCacheableGetPropReadSlot(obj, holder, shape)) { @@ -5735,19 +5990,12 @@ UpdateExistingGetPropCallStubs(ICFallbackStub* fallbackStub, if (isOwnGetter) getPropStub->receiverGuard().update(receiverGuard); + MOZ_ASSERT(getPropStub->holderShape() != holder->lastProperty() || + !getPropStub->receiverGuard().matches(receiverGuard), + "Why didn't we end up using this stub?"); + // We want to update the holder shape to match the new one no // matter what, even if the receiver shape is different. - - // We would like to assert that either - // getPropStub->holderShape() != holder->lastProperty() or - // receiverShape != cachedReceiverShape, but that assertion can - // fail if there is something in a getter that changes something - // that we guard on in our stub but don't check for before/after - // differences across the set during stub generation. For - // example, a getter mutating the shape of the proto the getter - // lives on would cause us to create an IC stub that never - // matches as protos with the old shape flow into it, but always - // matches post-get, which is where we are now. getPropStub->holderShape() = holder->lastProperty(); // Make sure to update the getter, since a shape change might @@ -5811,11 +6059,11 @@ UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub, return foundMatchingStub; } -// Attach an optimized stub for a GETGNAME/CALLGNAME op. +// Attach an optimized stub for a GETGNAME/CALLGNAME slot-read op. static bool -TryAttachGlobalNameStub(JSContext* cx, HandleScript script, jsbytecode* pc, - ICGetName_Fallback* stub, Handle global, - HandlePropertyName name, bool* attached, bool* isTemporarilyUnoptimizable) +TryAttachGlobalNameValueStub(JSContext* cx, HandleScript script, jsbytecode* pc, + ICGetName_Fallback* stub, Handle global, + HandlePropertyName name, bool* attached) { MOZ_ASSERT(global->is()); MOZ_ASSERT(!*attached); @@ -5870,8 +6118,37 @@ TryAttachGlobalNameStub(JSContext* cx, HandleScript script, jsbytecode* pc, stub->addNewStub(newStub); *attached = true; - return true; } + return true; +} + +// Attach an optimized stub for a GETGNAME/CALLGNAME getter op. +static bool +TryAttachGlobalNameAccessorStub(JSContext* cx, HandleScript script, jsbytecode* pc, + ICGetName_Fallback* stub, Handle global, + HandlePropertyName name, bool* attached, + bool* isTemporarilyUnoptimizable) +{ + MOZ_ASSERT(global->is()); + + RootedId id(cx, NameToId(name)); + + // The property must be found, and it must be found as a normal data property. + RootedShape shape(cx); + RootedNativeObject current(cx, global); + while (true) { + shape = current->lookup(cx, id); + if (shape) + break; + JSObject* proto = current->getProto(); + if (!proto || !proto->is()) + return true; + current = &proto->as(); + } + + // Instantiate this global property, for use during Ion compilation. + if (IsIonEnabled(cx)) + EnsureTrackPropertyTypes(cx, current, id); // Try to add a getter stub. We don't handle scripted getters yet; if this // changes we need to make sure IonBuilder::getPropTryCommonGetter (which @@ -5901,9 +6178,7 @@ TryAttachGlobalNameStub(JSContext* cx, HandleScript script, jsbytecode* pc, stub->addNewStub(newStub); *attached = true; - return true; } - return true; } @@ -6019,6 +6294,22 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_ MOZ_ASSERT(op == JSOP_GETNAME || op == JSOP_GETGNAME); RootedPropertyName name(cx, script->getName(pc)); + bool attached = false; + bool isTemporarilyUnoptimizable = false; + + // Attach new stub. + if (stub->numOptimizedStubs() >= ICGetName_Fallback::MAX_OPTIMIZED_STUBS) { + // TODO: Discard all stubs in this IC and replace with generic stub. + attached = true; + } + + if (!attached && IsGlobalOp(JSOp(*pc)) && !script->hasPollutedGlobalScope()) { + if (!TryAttachGlobalNameAccessorStub(cx, script, pc, stub, scopeChain.as(), + name, &attached, &isTemporarilyUnoptimizable)) + { + return false; + } + } static_assert(JSOP_GETGNAME_LENGTH == JSOP_GETNAME_LENGTH, "Otherwise our check for JSOP_TYPEOF isn't ok"); @@ -6039,22 +6330,13 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_ // Add a type monitor stub for the resulting value. if (!stub->addMonitorStubForValue(cx, script, res)) return false; - - // Attach new stub. - if (stub->numOptimizedStubs() >= ICGetName_Fallback::MAX_OPTIMIZED_STUBS) { - // TODO: Discard all stubs in this IC and replace with generic stub. + if (attached) return true; - } - bool attached = false; - bool isTemporarilyUnoptimizable = false; if (IsGlobalOp(JSOp(*pc)) && !script->hasPollutedGlobalScope()) { Handle global = scopeChain.as(); - if (!TryAttachGlobalNameStub(cx, script, pc, stub, global, name, &attached, - &isTemporarilyUnoptimizable)) - { + if (!TryAttachGlobalNameValueStub(cx, script, pc, stub, global, name, &attached)) return false; - } } else { if (!TryAttachScopeNameStub(cx, script, stub, scopeChain, name, &attached)) return false; @@ -6462,14 +6744,12 @@ StripPreliminaryObjectStubs(JSContext* cx, ICFallbackStub* stub) } static bool -TryAttachNativeGetPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, - ICGetProp_Fallback* stub, HandlePropertyName name, - HandleValue val, HandleShape oldShape, - HandleValue res, bool* attached, - bool* isTemporarilyUnoptimizable) +TryAttachNativeGetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, + ICGetProp_Fallback* stub, HandlePropertyName name, + HandleValue val, HandleShape oldShape, + HandleValue res, bool* attached) { MOZ_ASSERT(!*attached); - MOZ_ASSERT(!*isTemporarilyUnoptimizable); if (!val.isObject()) return true; @@ -6481,21 +6761,16 @@ TryAttachNativeGetPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, return true; } - bool isDOMProxy; - bool domProxyHasGeneration; - DOMProxyShadowsResult domProxyShadowsResult; RootedShape shape(cx); RootedObject holder(cx); - if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape, &isDOMProxy, - &domProxyShadowsResult, &domProxyHasGeneration)) - { + RootedId id(cx, NameToId(name)); + if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape)) return false; - } bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP); ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - if (!isDOMProxy && IsCacheableGetPropReadSlot(obj, holder, shape)) { + if (IsCacheableGetPropReadSlot(obj, holder, shape)) { bool isFixedSlot; uint32_t offset; GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); @@ -6507,8 +6782,7 @@ TryAttachNativeGetPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, ICStub::Kind kind = (obj == holder) ? ICStub::GetProp_Native : ICStub::GetProp_NativePrototype; - JitSpew(JitSpew_BaselineIC, " Generating GetProp(%s %s) stub", - isDOMProxy ? "DOMProxy" : "Native", + JitSpew(JitSpew_BaselineIC, " Generating GetProp(Native %s) stub", (obj == holder) ? "direct" : "prototype"); ICGetPropNativeCompiler compiler(cx, kind, isCallProp, monitorStub, obj, holder, name, isFixedSlot, offset); @@ -6525,6 +6799,39 @@ TryAttachNativeGetPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, *attached = true; return true; } + return true; +} + + +static bool +TryAttachNativeGetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, + ICGetProp_Fallback* stub, HandlePropertyName name, + HandleValue val, HandleValue res, bool* attached, + bool* isTemporarilyUnoptimizable) +{ + MOZ_ASSERT(!*attached); + MOZ_ASSERT(!*isTemporarilyUnoptimizable); + + if (!val.isObject()) + return true; + + RootedObject obj(cx, &val.toObject()); + + bool isDOMProxy; + bool domProxyHasGeneration; + DOMProxyShadowsResult domProxyShadowsResult; + RootedShape shape(cx); + RootedObject holder(cx); + RootedId id(cx, NameToId(name)); + if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape, &isDOMProxy, + &domProxyShadowsResult, &domProxyHasGeneration)) + { + return false; + } + + bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP); + + ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); bool isScripted = false; bool cacheableCall = IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted, @@ -6593,7 +6900,7 @@ TryAttachNativeGetPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, return true; // ICGetProp_CallNative*::Compiler::generateStubCode depends on this. MOZ_ASSERT(&((GetProxyDataLayout(outer)->values->privateSlot).toObject()) == obj); - if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape, &isDOMProxy, + if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape, &isDOMProxy, &domProxyShadowsResult, &domProxyHasGeneration)) { return false; @@ -6927,35 +7234,6 @@ DoGetPropFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback* stub_ if (val.isObject()) oldShape = val.toObject().maybeShape(); - // After the Genericstub was added, we should never reach the Fallbackstub again. - MOZ_ASSERT(!stub->hasStub(ICStub::GetProp_Generic)); - - RootedPropertyName name(cx, frame->script()->getName(pc)); - if (!ComputeGetPropResult(cx, frame, op, name, val, res)) - return false; - - TypeScript::Monitor(cx, frame->script(), pc, res); - - // Check if debug mode toggling made the stub invalid. - if (stub.invalid()) - return true; - - // Add a type monitor stub for the resulting value. - if (!stub->addMonitorStubForValue(cx, frame->script(), res)) - return false; - - if (stub->numOptimizedStubs() >= ICGetProp_Fallback::MAX_OPTIMIZED_STUBS) { - // Discard all stubs in this IC and replace with generic getprop stub. - for(ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) - iter.unlink(cx); - ICGetProp_Generic::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub()); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(frame->script())); - if (!newStub) - return false; - stub->addNewStub(newStub); - return true; - } - bool attached = false; // There are some reasons we can fail to attach a stub that are temporary. // We want to avoid calling noteUnoptimizableAccess() if the reason we @@ -6963,22 +7241,60 @@ DoGetPropFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback* stub_ // end up attaching a stub for the exact same access later. bool isTemporarilyUnoptimizable = false; + RootedScript script(cx, frame->script()); + RootedPropertyName name(cx, frame->script()->getName(pc)); + + // After the Genericstub was added, we should never reach the Fallbackstub again. + MOZ_ASSERT(!stub->hasStub(ICStub::GetProp_Generic)); + + if (stub->numOptimizedStubs() >= ICGetProp_Fallback::MAX_OPTIMIZED_STUBS) { + // Discard all stubs in this IC and replace with generic getprop stub. + for(ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) + iter.unlink(cx); + ICGetProp_Generic::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub()); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + stub->addNewStub(newStub); + attached = true; + } + + if (!attached && !TryAttachNativeGetAccessorPropStub(cx, script, pc, stub, name, val, res, + &attached, &isTemporarilyUnoptimizable)) + { + return false; + } + + if (!ComputeGetPropResult(cx, frame, op, name, val, res)) + return false; + + TypeScript::Monitor(cx, script, pc, res); + + // Check if debug mode toggling made the stub invalid. + if (stub.invalid()) + return true; + + // Add a type monitor stub for the resulting value. + if (!stub->addMonitorStubForValue(cx, script, res)) + return false; + + if (attached) + return true; + if (op == JSOP_LENGTH) { - if (!TryAttachLengthStub(cx, frame->script(), stub, val, res, &attached)) + if (!TryAttachLengthStub(cx, script, stub, val, res, &attached)) return false; if (attached) return true; } - if (!TryAttachMagicArgumentsGetPropStub(cx, frame->script(), stub, name, val, res, &attached)) + if (!TryAttachMagicArgumentsGetPropStub(cx, script, stub, name, val, res, &attached)) return false; if (attached) return true; - RootedScript script(cx, frame->script()); - - if (!TryAttachNativeGetPropStub(cx, script, pc, stub, name, val, oldShape, - res, &attached, &isTemporarilyUnoptimizable)) + if (!TryAttachNativeGetValuePropStub(cx, script, pc, stub, name, val, oldShape, + res, &attached)) return false; if (attached) return true; @@ -7225,44 +7541,6 @@ ICGetPropNativeCompiler::getStub(ICStubSpace* space) } } -static void -GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard, - Register object, Register scratch, - size_t receiverGuardOffset, Label* failure) -{ - Address groupAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfGroup()); - Address shapeAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfShape()); - Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); - - if (guard.group) { - masm.loadPtr(groupAddress, scratch); - masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure); - - if (guard.group->clasp() == &UnboxedPlainObject::class_ && !guard.shape) { - // Guard the unboxed object has no expando object. - masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure); - } - } - - if (guard.shape) { - masm.loadPtr(shapeAddress, scratch); - if (guard.group && guard.group->clasp() == &UnboxedPlainObject::class_) { - // Guard the unboxed object has a matching expando object. - masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); - Label done; - masm.push(object); - masm.loadPtr(expandoAddress, object); - masm.branchTestObjShape(Assembler::Equal, object, scratch, &done); - masm.pop(object); - masm.jump(failure); - masm.bind(&done); - masm.pop(object); - } else { - masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); - } - } -} - bool ICGetPropNativeCompiler::generateStubCode(MacroAssembler& masm) { @@ -7560,14 +7838,13 @@ ICGetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm) Label failure; - AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); + AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); Register objReg = InvalidReg; MOZ_ASSERT(!(inputDefinitelyObject_ && outerClass_)); if (inputDefinitelyObject_) { objReg = R0.scratchReg(); } else { - regs.take(R0); // Guard input is an object and unbox. masm.branchTestObject(Assembler::NotEqual, R0, &failure); objReg = masm.extractObject(R0, ExtractTemp0); @@ -7577,12 +7854,11 @@ ICGetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm) masm.branchTestObjClass(Assembler::NotEqual, objReg, tmp, outerClass_, &failure); masm.loadPtr(Address(objReg, ProxyDataOffset + offsetof(ProxyDataLayout, values)), tmp); masm.loadValue(Address(tmp, offsetof(ProxyValueArray, privateSlot)), val); - objReg = masm.extractObject(val, ExtractTemp0); + masm.movePtr(masm.extractObject(val, ExtractTemp0), objReg); regs.add(val); regs.add(tmp); } } - regs.takeUnchecked(objReg); Register scratch = regs.takeAnyExcluding(ICTailCallReg); @@ -7598,6 +7874,13 @@ ICGetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm) regs.add(holderReg); } + // Box and push obj onto baseline frame stack for decompiler + if (inputDefinitelyObject_) + masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0); + EmitStowICValues(masm, 1); + if (inputDefinitelyObject_) + objReg = masm.extractObject(R0, ExtractTemp0); + // Push a stub frame so that we can perform a non-tail call. enterStubFrame(masm, scratch); @@ -7609,15 +7892,14 @@ ICGetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm) masm.push(objReg); masm.push(callee); - if (!inputDefinitelyObject_) - regs.add(R0); - else - regs.add(objReg); + regs.add(R0); if (!callVM(DoCallNativeGetterInfo, masm)) return false; leaveStubFrame(masm); + EmitUnstowICValues(masm, 1, /* discard = */true); + // Enter type monitor IC to type-check result. EmitEnterTypeMonitorIC(masm); @@ -8102,7 +8384,7 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC RootedShape shape(cx); RootedObject holder(cx); - if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape)) + if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape)) return false; if (obj != holder) return true; @@ -8211,7 +8493,7 @@ TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, RootedShape shape(cx); RootedObject holder(cx); - if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape)) + if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape)) return false; bool isScripted = false; @@ -11899,76 +12181,83 @@ ICTypeUpdate_ObjectGroup::ICTypeUpdate_ObjectGroup(JitCode* stubCode, ObjectGrou ICGetElemNativeStub::ICGetElemNativeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, PropertyName* name, - AccessType acctype, bool needsAtomize) + ReceiverGuard guard, AccessType acctype, + bool needsAtomize, bool isSymbol) : ICMonitoredStub(kind, stubCode, firstMonitorStub), - shape_(shape), - name_(name) + receiverGuard_(guard) { extra_ = (static_cast(acctype) << ACCESSTYPE_SHIFT) | - (static_cast(needsAtomize) << NEEDS_ATOMIZE_SHIFT); + (static_cast(needsAtomize) << NEEDS_ATOMIZE_SHIFT) | + (static_cast(isSymbol) << ISSYMBOL_SHIFT); } ICGetElemNativeStub::~ICGetElemNativeStub() { } -ICGetElemNativeGetterStub::ICGetElemNativeGetterStub( - ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, PropertyName* name, AccessType acctype, - bool needsAtomize, JSFunction* getter, uint32_t pcOffset) - : ICGetElemNativeStub(kind, stubCode, firstMonitorStub, shape, name, acctype, needsAtomize), +template +ICGetElemNativeGetterStub::ICGetElemNativeGetterStub( + ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard guard, const T* key, AccType acctype, bool needsAtomize, + JSFunction* getter, uint32_t pcOffset) + : ICGetElemNativeStubImpl(kind, stubCode, firstMonitorStub, guard, key, acctype, needsAtomize), getter_(getter), pcOffset_(pcOffset) { - MOZ_ASSERT(kind == GetElem_NativePrototypeCallNative || - kind == GetElem_NativePrototypeCallScripted); - MOZ_ASSERT(acctype == NativeGetter || acctype == ScriptedGetter); + MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallNativeName || + kind == ICStub::GetElem_NativePrototypeCallNativeSymbol || + kind == ICStub::GetElem_NativePrototypeCallScriptedName || + kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol); + MOZ_ASSERT(acctype == ICGetElemNativeStub::NativeGetter || + acctype == ICGetElemNativeStub::ScriptedGetter); } -ICGetElem_NativePrototypeSlot::ICGetElem_NativePrototypeSlot( - JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, PropertyName* name, - AccessType acctype, bool needsAtomize, uint32_t offset, - JSObject* holder, Shape* holderShape) - : ICGetElemNativeSlotStub(ICStub::GetElem_NativePrototypeSlot, stubCode, firstMonitorStub, shape, - name, acctype, needsAtomize, offset), +template +ICGetElem_NativePrototypeSlot::ICGetElem_NativePrototypeSlot( + JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, + const T* key, AccType acctype, bool needsAtomize, uint32_t offset, + JSObject* holder, Shape* holderShape) + : ICGetElemNativeSlotStub(getGetElemStubKind(ICStub::GetElem_NativePrototypeSlotName), + stubCode, firstMonitorStub, guard, key, acctype, needsAtomize, offset), holder_(holder), holderShape_(holderShape) { } -ICGetElemNativePrototypeCallStub::ICGetElemNativePrototypeCallStub( - ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, PropertyName* name, - AccessType acctype, bool needsAtomize, JSFunction* getter, - uint32_t pcOffset, JSObject* holder, Shape* holderShape) - : ICGetElemNativeGetterStub(kind, stubCode, firstMonitorStub, shape, name, acctype, needsAtomize, - getter, pcOffset), +template +ICGetElemNativePrototypeCallStub::ICGetElemNativePrototypeCallStub( + ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard guard, const T* key, AccType acctype, + bool needsAtomize, JSFunction* getter, uint32_t pcOffset, + JSObject* holder, Shape* holderShape) + : ICGetElemNativeGetterStub(kind, stubCode, firstMonitorStub, guard, key, acctype, needsAtomize, + getter, pcOffset), holder_(holder), holderShape_(holderShape) {} -/* static */ ICGetElem_NativePrototypeCallNative* -ICGetElem_NativePrototypeCallNative::Clone(JSContext* cx, - ICStubSpace* space, - ICStub* firstMonitorStub, - ICGetElem_NativePrototypeCallNative& other) +template +/* static */ ICGetElem_NativePrototypeCallNative* +ICGetElem_NativePrototypeCallNative::Clone(JSContext* cx, + ICStubSpace* space, + ICStub* firstMonitorStub, + ICGetElem_NativePrototypeCallNative& other) { - return New(cx, space, other.jitCode(), firstMonitorStub, - other.shape(), other.name(), other.accessType(), - other.needsAtomize(), other.getter(), other.pcOffset_, - other.holder(), other.holderShape()); + return ICStub::New>(cx, space, other.jitCode(), + firstMonitorStub, other.receiverGuard(), other.key().unsafeGet(), other.accessType(), + other.needsAtomize(), other.getter(), other.pcOffset_, other.holder(), + other.holderShape()); } -/* static */ ICGetElem_NativePrototypeCallScripted* -ICGetElem_NativePrototypeCallScripted::Clone(JSContext* cx, - ICStubSpace* space, - ICStub* firstMonitorStub, - ICGetElem_NativePrototypeCallScripted& other) +template +/* static */ ICGetElem_NativePrototypeCallScripted* +ICGetElem_NativePrototypeCallScripted::Clone(JSContext* cx, + ICStubSpace* space, + ICStub* firstMonitorStub, + ICGetElem_NativePrototypeCallScripted& other) { - return New(cx, space, other.jitCode(), firstMonitorStub, - other.shape(), other.name(), - other.accessType(), other.needsAtomize(), other.getter(), - other.pcOffset_, other.holder(), other.holderShape()); + return ICStub::New>(cx, space, other.jitCode(), + firstMonitorStub, other.receiverGuard(), other.key().unsafeGet(), other.accessType(), + other.needsAtomize(), other.getter(), other.pcOffset_, other.holder(), + other.holderShape()); } ICGetElem_Dense::ICGetElem_Dense(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape) diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index bcd0974508..ede92e365f 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -1527,11 +1527,10 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub class ICGetElemNativeStub : public ICMonitoredStub { public: - enum AccessType { FixedSlot = 0, DynamicSlot, NativeGetter, ScriptedGetter }; + enum AccessType { FixedSlot = 0, DynamicSlot, UnboxedProperty, NativeGetter, ScriptedGetter }; protected: - HeapPtrShape shape_; - HeapPtrPropertyName name_; + HeapReceiverGuard receiverGuard_; static const unsigned NEEDS_ATOMIZE_SHIFT = 0; static const uint16_t NEEDS_ATOMIZE_MASK = 0x1; @@ -1539,25 +1538,20 @@ class ICGetElemNativeStub : public ICMonitoredStub static const unsigned ACCESSTYPE_SHIFT = 1; static const uint16_t ACCESSTYPE_MASK = 0x3; + static const unsigned ISSYMBOL_SHIFT = 3; + static const uint16_t ISSYMBOL_MASK = 0x1; + ICGetElemNativeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, PropertyName* name, AccessType acctype, - bool needsAtomize); + ReceiverGuard guard, AccessType acctype, bool needsAtomize, bool isSymbol); ~ICGetElemNativeStub(); public: - HeapPtrShape& shape() { - return shape_; + HeapReceiverGuard& receiverGuard() { + return receiverGuard_; } - static size_t offsetOfShape() { - return offsetof(ICGetElemNativeStub, shape_); - } - - HeapPtrPropertyName& name() { - return name_; - } - static size_t offsetOfName() { - return offsetof(ICGetElemNativeStub, name_); + static size_t offsetOfReceiverGuard() { + return offsetof(ICGetElemNativeStub, receiverGuard_); } AccessType accessType() const { @@ -1567,21 +1561,56 @@ class ICGetElemNativeStub : public ICMonitoredStub bool needsAtomize() const { return (extra_ >> NEEDS_ATOMIZE_SHIFT) & NEEDS_ATOMIZE_MASK; } + + bool isSymbol() const { + return (extra_ >> ISSYMBOL_SHIFT) & ISSYMBOL_MASK; + } }; -class ICGetElemNativeSlotStub : public ICGetElemNativeStub +template +class ICGetElemNativeStubImpl : public ICGetElemNativeStub +{ + protected: + HeapPtr key_; + + ICGetElemNativeStubImpl(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard guard, const T* key, AccessType acctype, bool needsAtomize) + : ICGetElemNativeStub(kind, stubCode, firstMonitorStub, guard, acctype, needsAtomize, + mozilla::IsSame::value), + key_(*key) + {} + + public: + HeapPtr& key() { + return key_; + } + static size_t offsetOfKey() { + return offsetof(ICGetElemNativeStubImpl, key_); + } +}; + +typedef ICGetElemNativeStub::AccessType AccType; + +template +class ICGetElemNativeSlotStub : public ICGetElemNativeStubImpl { protected: uint32_t offset_; ICGetElemNativeSlotStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, PropertyName* name, - AccessType acctype, bool needsAtomize, uint32_t offset) - : ICGetElemNativeStub(kind, stubCode, firstMonitorStub, shape, name, acctype, needsAtomize), + ReceiverGuard guard, const T* key, AccType acctype, bool needsAtomize, + uint32_t offset) + : ICGetElemNativeStubImpl(kind, stubCode, firstMonitorStub, guard, key, acctype, needsAtomize), offset_(offset) { - MOZ_ASSERT(kind == GetElem_NativeSlot || kind == GetElem_NativePrototypeSlot); - MOZ_ASSERT(acctype == FixedSlot || acctype == DynamicSlot); + MOZ_ASSERT(kind == ICStub::GetElem_NativeSlotName || + kind == ICStub::GetElem_NativeSlotSymbol || + kind == ICStub::GetElem_NativePrototypeSlotName || + kind == ICStub::GetElem_NativePrototypeSlotSymbol || + kind == ICStub::GetElem_UnboxedPropertyName); + MOZ_ASSERT(acctype == ICGetElemNativeStub::FixedSlot || + acctype == ICGetElemNativeStub::DynamicSlot || + acctype == ICGetElemNativeStub::UnboxedProperty); } public: @@ -1594,15 +1623,16 @@ class ICGetElemNativeSlotStub : public ICGetElemNativeStub } }; -class ICGetElemNativeGetterStub : public ICGetElemNativeStub +template +class ICGetElemNativeGetterStub : public ICGetElemNativeStubImpl { protected: HeapPtrFunction getter_; uint32_t pcOffset_; ICGetElemNativeGetterStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, PropertyName* name, AccessType acctype, - bool needsAtomize, JSFunction* getter, uint32_t pcOffset); + ReceiverGuard guard, const T* key, AccType acctype, bool needsAtomize, + JSFunction* getter, uint32_t pcOffset); public: HeapPtrFunction& getter() { @@ -1617,26 +1647,61 @@ class ICGetElemNativeGetterStub : public ICGetElemNativeStub } }; -class ICGetElem_NativeSlot : public ICGetElemNativeSlotStub +template +ICStub::Kind +getGetElemStubKind(ICStub::Kind kind) +{ + MOZ_ASSERT(kind == ICStub::GetElem_NativeSlotName || + kind == ICStub::GetElem_NativePrototypeSlotName || + kind == ICStub::GetElem_NativePrototypeCallNativeName || + kind == ICStub::GetElem_NativePrototypeCallScriptedName); + return static_cast(kind + mozilla::IsSame::value); +} + +template +class ICGetElem_NativeSlot : public ICGetElemNativeSlotStub { friend class ICStubSpace; - ICGetElem_NativeSlot(JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, PropertyName* name, - AccessType acctype, bool needsAtomize, uint32_t offset) - : ICGetElemNativeSlotStub(ICStub::GetElem_NativeSlot, stubCode, firstMonitorStub, shape, - name, acctype, needsAtomize, offset) + ICGetElem_NativeSlot(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, + const T* key, AccType acctype, bool needsAtomize, uint32_t offset) + : ICGetElemNativeSlotStub(getGetElemStubKind(ICStub::GetElem_NativeSlotName), + stubCode, firstMonitorStub, guard, + key, acctype, needsAtomize, offset) {} }; -class ICGetElem_NativePrototypeSlot : public ICGetElemNativeSlotStub +class ICGetElem_NativeSlotName : + public ICGetElem_NativeSlot +{}; +class ICGetElem_NativeSlotSymbol : + public ICGetElem_NativeSlot +{}; + +template +class ICGetElem_UnboxedProperty : public ICGetElemNativeSlotStub +{ + friend class ICStubSpace; + ICGetElem_UnboxedProperty(JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard guard, const T* key, AccType acctype, + bool needsAtomize, uint32_t offset) + : ICGetElemNativeSlotStub(ICStub::GetElem_UnboxedPropertyName, stubCode, firstMonitorStub, + guard, key, acctype, needsAtomize, offset) + {} +}; + +class ICGetElem_UnboxedPropertyName : + public ICGetElem_UnboxedProperty +{}; + +template +class ICGetElem_NativePrototypeSlot : public ICGetElemNativeSlotStub { friend class ICStubSpace; HeapPtrObject holder_; HeapPtrShape holderShape_; - ICGetElem_NativePrototypeSlot(JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, PropertyName* name, - AccessType acctype, bool needsAtomize, uint32_t offset, + ICGetElem_NativePrototypeSlot(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, + const T* key, AccType acctype, bool needsAtomize, uint32_t offset, JSObject* holder, Shape* holderShape); public: @@ -1655,7 +1720,15 @@ class ICGetElem_NativePrototypeSlot : public ICGetElemNativeSlotStub } }; -class ICGetElemNativePrototypeCallStub : public ICGetElemNativeGetterStub +class ICGetElem_NativePrototypeSlotName : + public ICGetElem_NativePrototypeSlot +{}; +class ICGetElem_NativePrototypeSlotSymbol : + public ICGetElem_NativePrototypeSlot +{}; + +template +class ICGetElemNativePrototypeCallStub : public ICGetElemNativeGetterStub { friend class ICStubSpace; HeapPtrObject holder_; @@ -1663,10 +1736,9 @@ class ICGetElemNativePrototypeCallStub : public ICGetElemNativeGetterStub protected: ICGetElemNativePrototypeCallStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, PropertyName* name, - AccessType acctype, bool needsAtomize, JSFunction* getter, - uint32_t pcOffset, JSObject* holder, - Shape* holderShape); + ReceiverGuard guard, const T* key, AccType acctype, + bool needsAtomize, JSFunction* getter, uint32_t pcOffset, + JSObject* holder, Shape* holderShape); public: HeapPtrObject& holder() { @@ -1684,146 +1756,178 @@ class ICGetElemNativePrototypeCallStub : public ICGetElemNativeGetterStub } }; -class ICGetElem_NativePrototypeCallNative : public ICGetElemNativePrototypeCallStub +template +class ICGetElem_NativePrototypeCallNative : public ICGetElemNativePrototypeCallStub { friend class ICStubSpace; ICGetElem_NativePrototypeCallNative(JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, PropertyName* name, - AccessType acctype, bool needsAtomize, - JSFunction* getter, uint32_t pcOffset, + ReceiverGuard guard, const T* key, AccType acctype, + bool needsAtomize, JSFunction* getter, uint32_t pcOffset, JSObject* holder, Shape* holderShape) - : ICGetElemNativePrototypeCallStub(GetElem_NativePrototypeCallNative, - stubCode, firstMonitorStub, shape, name, - acctype, needsAtomize, getter, pcOffset, holder, - holderShape) + : ICGetElemNativePrototypeCallStub(getGetElemStubKind( + ICStub::GetElem_NativePrototypeCallNativeName), + stubCode, firstMonitorStub, guard, key, + acctype, needsAtomize, getter, pcOffset, holder, + holderShape) {} public: - static ICGetElem_NativePrototypeCallNative* Clone(JSContext* cx, ICStubSpace* space, - ICStub* firstMonitorStub, - ICGetElem_NativePrototypeCallNative& other); + static ICGetElem_NativePrototypeCallNative* Clone(JSContext* cx, ICStubSpace* space, + ICStub* firstMonitorStub, + ICGetElem_NativePrototypeCallNative& other); }; -class ICGetElem_NativePrototypeCallScripted : public ICGetElemNativePrototypeCallStub +class ICGetElem_NativePrototypeCallNativeName : + public ICGetElem_NativePrototypeCallNative +{}; +class ICGetElem_NativePrototypeCallNativeSymbol : + public ICGetElem_NativePrototypeCallNative +{}; + +template +class ICGetElem_NativePrototypeCallScripted : public ICGetElemNativePrototypeCallStub { friend class ICStubSpace; ICGetElem_NativePrototypeCallScripted(JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, PropertyName* name, - AccessType acctype, bool needsAtomize, - JSFunction* getter, uint32_t pcOffset, + ReceiverGuard guard, const T* key, AccType acctype, + bool needsAtomize, JSFunction* getter, uint32_t pcOffset, JSObject* holder, Shape* holderShape) - : ICGetElemNativePrototypeCallStub(GetElem_NativePrototypeCallScripted, - stubCode, firstMonitorStub, shape, name, - acctype, needsAtomize, getter, pcOffset, holder, - holderShape) + : ICGetElemNativePrototypeCallStub(getGetElemStubKind( + ICStub::GetElem_NativePrototypeCallScriptedName), + stubCode, firstMonitorStub, guard, key, acctype, + needsAtomize, getter, pcOffset, holder, holderShape) {} public: - static ICGetElem_NativePrototypeCallScripted* + static ICGetElem_NativePrototypeCallScripted* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetElem_NativePrototypeCallScripted& other); + ICGetElem_NativePrototypeCallScripted& other); }; +class ICGetElem_NativePrototypeCallScriptedName : + public ICGetElem_NativePrototypeCallScripted +{}; +class ICGetElem_NativePrototypeCallScriptedSymbol : + public ICGetElem_NativePrototypeCallScripted +{}; + // Compiler for GetElem_NativeSlot and GetElem_NativePrototypeSlot stubs. +template class ICGetElemNativeCompiler : public ICStubCompiler { bool isCallElem_; ICStub* firstMonitorStub_; HandleObject obj_; HandleObject holder_; - HandlePropertyName name_; - ICGetElemNativeStub::AccessType acctype_; + Handle key_; + AccType acctype_; bool needsAtomize_; uint32_t offset_; + JSValueType unboxedType_; HandleFunction getter_; uint32_t pcOffset_; + bool emitCheckKey(MacroAssembler& masm, Label& failure); bool emitCallNative(MacroAssembler& masm, Register objReg); bool emitCallScripted(MacroAssembler& masm, Register objReg); bool generateStubCode(MacroAssembler& masm); protected: virtual int32_t getKey() const { + MOZ_ASSERT(static_cast(acctype_) <= 7); + MOZ_ASSERT(static_cast(unboxedType_) <= 8); + return static_cast(engine_) | + (static_cast(kind) << 1) | #if JS_HAS_NO_SUCH_METHOD - return static_cast(engine_) | - (static_cast(kind) << 1) | (static_cast(isCallElem_) << 17) | - (static_cast(needsAtomize_) << 18) | - (static_cast(acctype_) << 19); -#else - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(needsAtomize_) << 17) | - (static_cast(acctype_) << 18); #endif + (static_cast(needsAtomize_) << 18) | + (static_cast(acctype_) << 19) | + (static_cast(unboxedType_) << 22) | + (static_cast(mozilla::IsSame::value) << 26) | + (HeapReceiverGuard::keyBits(obj_) << 27); } public: ICGetElemNativeCompiler(JSContext* cx, ICStub::Kind kind, bool isCallElem, ICStub* firstMonitorStub, HandleObject obj, HandleObject holder, - HandlePropertyName name, ICGetElemNativeStub::AccessType acctype, - bool needsAtomize, uint32_t offset) + Handle key, AccType acctype, bool needsAtomize, uint32_t offset, + JSValueType unboxedType = JSVAL_TYPE_MAGIC) : ICStubCompiler(cx, kind, Engine::Baseline), isCallElem_(isCallElem), firstMonitorStub_(firstMonitorStub), obj_(obj), holder_(holder), - name_(name), + key_(key), acctype_(acctype), needsAtomize_(needsAtomize), offset_(offset), + unboxedType_(unboxedType), getter_(nullptr), pcOffset_(0) {} ICGetElemNativeCompiler(JSContext* cx, ICStub::Kind kind, ICStub* firstMonitorStub, - HandleObject obj, HandleObject holder, HandlePropertyName name, - ICGetElemNativeStub::AccessType acctype, bool needsAtomize, - HandleFunction getter, uint32_t pcOffset, bool isCallElem) + HandleObject obj, HandleObject holder, Handle key, AccType acctype, + bool needsAtomize, HandleFunction getter, uint32_t pcOffset, + bool isCallElem) : ICStubCompiler(cx, kind, Engine::Baseline), isCallElem_(false), firstMonitorStub_(firstMonitorStub), obj_(obj), holder_(holder), - name_(name), + key_(key), acctype_(acctype), needsAtomize_(needsAtomize), offset_(0), + unboxedType_(JSVAL_TYPE_MAGIC), getter_(getter), pcOffset_(pcOffset) {} ICStub* getStub(ICStubSpace* space) { - RootedShape shape(cx, obj_->as().lastProperty()); - if (kind == ICStub::GetElem_NativeSlot) { + RootedReceiverGuard guard(cx, ReceiverGuard(obj_)); + if (kind == ICStub::GetElem_NativeSlotName || kind == ICStub::GetElem_NativeSlotSymbol) { MOZ_ASSERT(obj_ == holder_); - return newStub( - space, getStubCode(), firstMonitorStub_, shape, name_, acctype_, needsAtomize_, - offset_); + return newStub>( + space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_, + needsAtomize_, offset_); + } + + if (kind == ICStub::GetElem_UnboxedPropertyName) { + MOZ_ASSERT(obj_ == holder_); + return newStub>( + space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_, + needsAtomize_, offset_); } MOZ_ASSERT(obj_ != holder_); RootedShape holderShape(cx, holder_->as().lastProperty()); - if (kind == ICStub::GetElem_NativePrototypeSlot) { - return newStub( - space, getStubCode(), firstMonitorStub_, shape, name_, acctype_, needsAtomize_, - offset_, holder_, holderShape); + if (kind == ICStub::GetElem_NativePrototypeSlotName || + kind == ICStub::GetElem_NativePrototypeSlotSymbol) + { + return newStub>( + space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_, + needsAtomize_, offset_, holder_, holderShape); } - if (kind == ICStub::GetElem_NativePrototypeCallNative) { - return newStub( - space, getStubCode(), firstMonitorStub_, shape, name_, acctype_, needsAtomize_, - getter_, pcOffset_, holder_, holderShape); + if (kind == ICStub::GetElem_NativePrototypeCallNativeSymbol || + kind == ICStub::GetElem_NativePrototypeCallNativeName) { + return newStub>( + space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_, + needsAtomize_, getter_, pcOffset_, holder_, holderShape); } - MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallScripted); - if (kind == ICStub::GetElem_NativePrototypeCallScripted) { - return newStub( - space, getStubCode(), firstMonitorStub_, shape, name_, acctype_, needsAtomize_, - getter_, pcOffset_, holder_, holderShape); + MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallScriptedName || + kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol); + if (kind == ICStub::GetElem_NativePrototypeCallScriptedName || + kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol) { + return newStub>( + space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_, + needsAtomize_, getter_, pcOffset_, holder_, holderShape); } MOZ_CRASH("Invalid kind."); diff --git a/js/src/jit/BaselineICList.h b/js/src/jit/BaselineICList.h index afed113e35..698b1cf7fb 100644 --- a/js/src/jit/BaselineICList.h +++ b/js/src/jit/BaselineICList.h @@ -73,10 +73,15 @@ namespace jit { _(Call_IsSuspendedStarGenerator) \ \ _(GetElem_Fallback) \ - _(GetElem_NativeSlot) \ - _(GetElem_NativePrototypeSlot) \ - _(GetElem_NativePrototypeCallNative) \ - _(GetElem_NativePrototypeCallScripted) \ + _(GetElem_NativeSlotName) \ + _(GetElem_NativeSlotSymbol) \ + _(GetElem_NativePrototypeSlotName) \ + _(GetElem_NativePrototypeSlotSymbol) \ + _(GetElem_NativePrototypeCallNativeName) \ + _(GetElem_NativePrototypeCallNativeSymbol) \ + _(GetElem_NativePrototypeCallScriptedName) \ + _(GetElem_NativePrototypeCallScriptedSymbol) \ + _(GetElem_UnboxedPropertyName) \ _(GetElem_String) \ _(GetElem_Dense) \ _(GetElem_UnboxedArray) \ diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index 90edefe1e0..965db22986 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -744,10 +744,15 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc) case ICStub::GetProp_CallDOMProxyNative: case ICStub::GetProp_CallDOMProxyWithGenerationNative: case ICStub::GetProp_DOMProxyShadowed: - case ICStub::GetElem_NativeSlot: - case ICStub::GetElem_NativePrototypeSlot: - case ICStub::GetElem_NativePrototypeCallNative: - case ICStub::GetElem_NativePrototypeCallScripted: + case ICStub::GetElem_NativeSlotName: + case ICStub::GetElem_NativeSlotSymbol: + case ICStub::GetElem_NativePrototypeSlotName: + case ICStub::GetElem_NativePrototypeSlotSymbol: + case ICStub::GetElem_NativePrototypeCallNativeName: + case ICStub::GetElem_NativePrototypeCallNativeSymbol: + case ICStub::GetElem_NativePrototypeCallScriptedName: + case ICStub::GetElem_NativePrototypeCallScriptedSymbol: + case ICStub::GetElem_UnboxedPropertyName: case ICStub::GetElem_String: case ICStub::GetElem_Dense: case ICStub::GetElem_TypedArray: diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index d2876b3bfd..e792c63700 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -860,18 +860,24 @@ static void GenerateReadUnboxed(JSContext* cx, IonScript* ion, MacroAssembler& masm, IonCache::StubAttacher& attacher, JSObject* obj, const UnboxedLayout::Property* property, - Register object, TypedOrValueRegister output) + Register object, TypedOrValueRegister output, + Label* failures = nullptr) { - // Guard on the type of the object. - attacher.branchNextStub(masm, Assembler::NotEqual, - Address(object, JSObject::offsetOfGroup()), - ImmGCPtr(obj->group())); + // Guard on the group of the object. + attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, + Address(object, JSObject::offsetOfGroup()), + ImmGCPtr(obj->group()), failures); Address address(object, UnboxedPlainObject::offsetOfData() + property->offset); masm.loadUnboxedProperty(address, property->type, output); attacher.jumpRejoin(masm); + + if (failures) { + masm.bind(failures); + attacher.jumpNextStub(masm); + } } static bool @@ -3313,7 +3319,7 @@ const size_t GetElementIC::MAX_FAILED_UPDATES = 16; GetElementIC::canAttachGetProp(JSObject* obj, const Value& idval, jsid id) { uint32_t dummy; - return obj->isNative() && + return (obj->isNative() || obj->is()) && idval.isString() && JSID_IS_ATOM(id) && !JSID_TO_ATOM(id)->isIndex(&dummy); @@ -3339,18 +3345,39 @@ GetElementIC::attachGetProp(JSContext* cx, HandleScript outerScript, IonScript* { MOZ_ASSERT(index().reg().hasValue()); - RootedNativeObject holder(cx); + RootedNativeObject baseHolder(cx); RootedShape shape(cx); GetPropertyIC::NativeGetPropCacheability canCache = - CanAttachNativeGetProp(cx, *this, obj, name, &holder, &shape, + CanAttachNativeGetProp(cx, *this, obj, name, &baseHolder, &shape, /* skipArrayLen =*/true); - bool cacheable = canCache == GetPropertyIC::CanAttachReadSlot || - (canCache == GetPropertyIC::CanAttachCallGetter && - output().hasValue()); + RootedObject holder(cx, baseHolder); - if (!cacheable) { + if (canCache == GetPropertyIC::CanAttachReadSlot) { + // OK to attach. + } else if (canCache == GetPropertyIC::CanAttachCallGetter) { + if (!output().hasValue()) { + JitSpew(JitSpew_IonIC, "GETELEM uncacheable property"); + return true; + } + } else if (obj->is()) { + MOZ_ASSERT(canCache == GetPropertyIC::CanAttachNone); + const UnboxedLayout::Property* property = + obj->as().layout().lookup(name); + if (property) { + // OK to attach. + } else { + UnboxedExpandoObject* expando = obj->as().maybeExpando(); + shape = expando ? expando->lookup(cx, name) : nullptr; + if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) { + JitSpew(JitSpew_IonIC, "GETELEM uncacheable property"); + return true; + } + canCache = GetPropertyIC::CanAttachReadSlot; + holder = obj; + } + } else { JitSpew(JitSpew_IonIC, "GETELEM uncacheable property"); return true; } @@ -3413,9 +3440,7 @@ GetElementIC::attachGetProp(JSContext* cx, HandleScript outerScript, IonScript* if (canCache == GetPropertyIC::CanAttachReadSlot) { GenerateReadSlot(cx, ion, masm, attacher, obj, holder, shape, object(), output(), &failures); - } else { - MOZ_ASSERT(canCache == GetPropertyIC::CanAttachCallGetter); - + } else if (canCache == GetPropertyIC::CanAttachCallGetter) { // Set the frame for bailout safety of the OOL call. void* returnAddr = GetReturnAddressToIonCode(cx); if (!GenerateCallGetter(cx, ion, masm, attacher, obj, name, holder, shape, liveRegs_, @@ -3423,6 +3448,11 @@ GetElementIC::attachGetProp(JSContext* cx, HandleScript outerScript, IonScript* { return false; } + } else { + MOZ_ASSERT(canCache == GetPropertyIC::CanAttachNone); + GenerateReadUnboxed(cx, ion, masm, attacher, obj, + obj->as().layout().lookup(name), + object(), output(), &failures); } return linkAndAttachStub(cx, masm, attacher, ion, "property"); diff --git a/js/src/jit/JitcodeMap.cpp b/js/src/jit/JitcodeMap.cpp index a5a2051c62..d2a5ca4a5f 100644 --- a/js/src/jit/JitcodeMap.cpp +++ b/js/src/jit/JitcodeMap.cpp @@ -738,6 +738,14 @@ JitcodeGlobalTable::verifySkiplist() } #endif // DEBUG +void +JitcodeGlobalTable::setAllEntriesAsExpired(JSRuntime* rt) +{ + AutoSuppressProfilerSampling suppressSampling(rt); + for (Range r(*this); !r.empty(); r.popFront()) + r.front()->setAsExpired(); +} + bool JitcodeGlobalTable::markIteratively(JSTracer* trc) { @@ -782,7 +790,7 @@ JitcodeGlobalTable::markIteratively(JSTracer* trc) // types used by optimizations and scripts used for pc to line number // mapping, alive as well. if (!entry->isSampled(gen, lapCount)) { - entry->setGeneration(UINT32_MAX); + entry->setAsExpired(); if (!entry->baseEntry().isJitcodeMarkedFromAnyThread()) continue; } diff --git a/js/src/jit/JitcodeMap.h b/js/src/jit/JitcodeMap.h index bc85972ce3..a8761d01e7 100644 --- a/js/src/jit/JitcodeMap.h +++ b/js/src/jit/JitcodeMap.h @@ -613,6 +613,9 @@ class JitcodeGlobalEntry void setGeneration(uint32_t gen) { baseEntry().setGeneration(gen); } + void setAsExpired() { + baseEntry().setGeneration(UINT32_MAX); + } bool isSampled(uint32_t currentGen, uint32_t lapCount) { return baseEntry().isSampled(currentGen, lapCount); } @@ -984,6 +987,7 @@ class JitcodeGlobalTable void removeEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt); void releaseEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt); + void setAllEntriesAsExpired(JSRuntime* rt); bool markIteratively(JSTracer* trc); void sweep(JSRuntime* rt); diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index 3890bb1451..48b4e11fe7 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -189,29 +189,61 @@ ICStub::trace(JSTracer* trc) TraceEdge(trc, &callStub->expectedThis(), "baseline-callstringsplit-this"); break; } - case ICStub::GetElem_NativeSlot: { - ICGetElem_NativeSlot* getElemStub = toGetElem_NativeSlot(); - TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-native-shape"); - TraceEdge(trc, &getElemStub->name(), "baseline-getelem-native-name"); + case ICStub::GetElem_NativeSlotName: + case ICStub::GetElem_NativeSlotSymbol: + case ICStub::GetElem_UnboxedPropertyName: { + ICGetElemNativeStub* getElemStub = static_cast(this); + getElemStub->receiverGuard().trace(trc); + if (getElemStub->isSymbol()) { + ICGetElem_NativeSlot* typedGetElemStub = toGetElem_NativeSlotSymbol(); + TraceEdge(trc, &typedGetElemStub->key(), "baseline-getelem-native-key"); + } else { + ICGetElemNativeSlotStub* typedGetElemStub = + reinterpret_cast*>(this); + TraceEdge(trc, &typedGetElemStub->key(), "baseline-getelem-native-key"); + } break; } - case ICStub::GetElem_NativePrototypeSlot: { - ICGetElem_NativePrototypeSlot* getElemStub = toGetElem_NativePrototypeSlot(); - TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-nativeproto-shape"); - TraceEdge(trc, &getElemStub->name(), "baseline-getelem-nativeproto-name"); - TraceEdge(trc, &getElemStub->holder(), "baseline-getelem-nativeproto-holder"); - TraceEdge(trc, &getElemStub->holderShape(), "baseline-getelem-nativeproto-holdershape"); + case ICStub::GetElem_NativePrototypeSlotName: + case ICStub::GetElem_NativePrototypeSlotSymbol: { + ICGetElemNativeStub* getElemStub = static_cast(this); + getElemStub->receiverGuard().trace(trc); + if (getElemStub->isSymbol()) { + ICGetElem_NativePrototypeSlot* typedGetElemStub + = toGetElem_NativePrototypeSlotSymbol(); + TraceEdge(trc, &typedGetElemStub->key(), "baseline-getelem-nativeproto-key"); + TraceEdge(trc, &typedGetElemStub->holder(), "baseline-getelem-nativeproto-holder"); + TraceEdge(trc, &typedGetElemStub->holderShape(), "baseline-getelem-nativeproto-holdershape"); + } else { + ICGetElem_NativePrototypeSlot* typedGetElemStub + = toGetElem_NativePrototypeSlotName(); + TraceEdge(trc, &typedGetElemStub->key(), "baseline-getelem-nativeproto-key"); + TraceEdge(trc, &typedGetElemStub->holder(), "baseline-getelem-nativeproto-holder"); + TraceEdge(trc, &typedGetElemStub->holderShape(), "baseline-getelem-nativeproto-holdershape"); + } break; } - case ICStub::GetElem_NativePrototypeCallNative: - case ICStub::GetElem_NativePrototypeCallScripted: { - ICGetElemNativePrototypeCallStub* callStub = - reinterpret_cast(this); - TraceEdge(trc, &callStub->shape(), "baseline-getelem-nativeprotocall-shape"); - TraceEdge(trc, &callStub->name(), "baseline-getelem-nativeprotocall-name"); - TraceEdge(trc, &callStub->getter(), "baseline-getelem-nativeprotocall-getter"); - TraceEdge(trc, &callStub->holder(), "baseline-getelem-nativeprotocall-holder"); - TraceEdge(trc, &callStub->holderShape(), "baseline-getelem-nativeprotocall-holdershape"); + case ICStub::GetElem_NativePrototypeCallNativeName: + case ICStub::GetElem_NativePrototypeCallNativeSymbol: + case ICStub::GetElem_NativePrototypeCallScriptedName: + case ICStub::GetElem_NativePrototypeCallScriptedSymbol: { + ICGetElemNativeStub* getElemStub = static_cast(this); + getElemStub->receiverGuard().trace(trc); + if (getElemStub->isSymbol()) { + ICGetElemNativePrototypeCallStub* callStub = + reinterpret_cast*>(this); + TraceEdge(trc, &callStub->key(), "baseline-getelem-nativeprotocall-key"); + TraceEdge(trc, &callStub->getter(), "baseline-getelem-nativeprotocall-getter"); + TraceEdge(trc, &callStub->holder(), "baseline-getelem-nativeprotocall-holder"); + TraceEdge(trc, &callStub->holderShape(), "baseline-getelem-nativeprotocall-holdershape"); + } else { + ICGetElemNativePrototypeCallStub* callStub = + reinterpret_cast*>(this); + TraceEdge(trc, &callStub->key(), "baseline-getelem-nativeprotocall-key"); + TraceEdge(trc, &callStub->getter(), "baseline-getelem-nativeprotocall-getter"); + TraceEdge(trc, &callStub->holder(), "baseline-getelem-nativeprotocall-holder"); + TraceEdge(trc, &callStub->holderShape(), "baseline-getelem-nativeprotocall-holdershape"); + } break; } case ICStub::GetElem_Dense: { diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h index 4169efb641..1abf93e5dc 100644 --- a/js/src/jit/SharedIC.h +++ b/js/src/jit/SharedIC.h @@ -689,10 +689,15 @@ class ICStub case Call_ScriptedFunCall: case Call_StringSplit: case WarmUpCounter_Fallback: - case GetElem_NativeSlot: - case GetElem_NativePrototypeSlot: - case GetElem_NativePrototypeCallNative: - case GetElem_NativePrototypeCallScripted: + case GetElem_NativeSlotName: + case GetElem_NativeSlotSymbol: + case GetElem_NativePrototypeSlotName: + case GetElem_NativePrototypeSlotSymbol: + case GetElem_NativePrototypeCallNativeName: + case GetElem_NativePrototypeCallNativeSymbol: + case GetElem_NativePrototypeCallScriptedName: + case GetElem_NativePrototypeCallScriptedSymbol: + case GetElem_UnboxedPropertyName: case GetProp_CallScripted: case GetProp_CallNative: case GetProp_CallDOMProxyNative: diff --git a/js/src/jsalloc.cpp b/js/src/jsalloc.cpp index e8e135e458..2eaaf49cca 100644 --- a/js/src/jsalloc.cpp +++ b/js/src/jsalloc.cpp @@ -11,9 +11,9 @@ using namespace js; void* -TempAllocPolicy::onOutOfMemory(void* p, size_t nbytes) +TempAllocPolicy::onOutOfMemory(AllocFunction allocFunc, size_t nbytes, void* reallocPtr) { - return static_cast(cx_)->onOutOfMemory(p, nbytes); + return static_cast(cx_)->onOutOfMemory(allocFunc, nbytes, reallocPtr); } void diff --git a/js/src/jsalloc.h b/js/src/jsalloc.h index e20fa5f269..ec819575f6 100644 --- a/js/src/jsalloc.h +++ b/js/src/jsalloc.h @@ -19,6 +19,12 @@ namespace js { +enum class AllocFunction { + Malloc, + Calloc, + Realloc +}; + struct ContextFriendFields; /* Policy for using system memory functions and doing no error reporting. */ @@ -51,14 +57,15 @@ class TempAllocPolicy * Non-inline helper to call JSRuntime::onOutOfMemory with minimal * code bloat. */ - JS_FRIEND_API(void*) onOutOfMemory(void* p, size_t nbytes); + JS_FRIEND_API(void*) onOutOfMemory(AllocFunction allocFunc, size_t nbytes, + void* reallocPtr = nullptr); template - T* onOutOfMemoryTyped(void* p, size_t numElems) { + T* onOutOfMemoryTyped(AllocFunction allocFunc, size_t numElems) { size_t bytes; if (MOZ_UNLIKELY(!CalculateAllocSize(numElems, &bytes))) return nullptr; - return static_cast(onOutOfMemory(p, bytes)); + return static_cast(onOutOfMemory(allocFunc, bytes)); } public: @@ -69,7 +76,7 @@ class TempAllocPolicy T* pod_malloc(size_t numElems) { T* p = js_pod_malloc(numElems); if (MOZ_UNLIKELY(!p)) - p = onOutOfMemoryTyped(nullptr, numElems); + p = onOutOfMemoryTyped(AllocFunction::Malloc, numElems); return p; } @@ -77,7 +84,7 @@ class TempAllocPolicy T* pod_calloc(size_t numElems) { T* p = js_pod_calloc(numElems); if (MOZ_UNLIKELY(!p)) - p = onOutOfMemoryTyped(reinterpret_cast(1), numElems); + p = onOutOfMemoryTyped(AllocFunction::Calloc, numElems); return p; } @@ -85,7 +92,7 @@ class TempAllocPolicy T* pod_realloc(T* prior, size_t oldSize, size_t newSize) { T* p2 = js_pod_realloc(prior, oldSize, newSize); if (MOZ_UNLIKELY(!p2)) - p2 = onOutOfMemoryTyped(p2, newSize); + p2 = onOutOfMemoryTyped(AllocFunction::Realloc, newSize); return p2; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 76d2ad43af..2dcda13234 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1447,8 +1447,7 @@ typedef void (*JSIterateCompartmentCallback)(JSRuntime* rt, void* data, JSCompar /* * This function calls |compartmentCallback| on every compartment. Beware that * there is no guarantee that the compartment will survive after the callback - * returns. Also, if the callback can GC, there is no guarantee that every - * compartment will be visited. + * returns. Also, barriers are disabled via the TraceSession. */ extern JS_PUBLIC_API(void) JS_IterateCompartments(JSRuntime* rt, void* data, diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 302ac089c1..4976098272 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -161,8 +161,8 @@ class ExclusiveContext : public ContextFriendFields, return thing->compartment() == compartment_; } - void* onOutOfMemory(void* p, size_t nbytes) { - return runtime_->onOutOfMemory(p, nbytes, maybeJSContext()); + void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, void* reallocPtr = nullptr) { + return runtime_->onOutOfMemory(allocFunc, nbytes, reallocPtr, maybeJSContext()); } /* Clear the pending exception (if any) due to OOM. */ diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index ee2adb1c9e..8b4f5f3fe2 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -476,9 +476,10 @@ JSCompartment::wrap(JSContext* cx, MutableHandle desc) * and when compacting to update cross-compartment pointers. */ void -JSCompartment::markCrossCompartmentWrappers(JSTracer *trc) +JSCompartment::markCrossCompartmentWrappers(JSTracer* trc) { - MOZ_ASSERT(!zone()->isCollecting() || trc->runtime()->isHeapCompacting()); + MOZ_ASSERT(trc->runtime()->isHeapMajorCollecting()); + MOZ_ASSERT(!zone()->isCollecting() || trc->runtime()->gc.isHeapCompacting()); for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { Value v = e.front().value(); diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 1c536c844e..9fae06ae4f 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1125,22 +1125,30 @@ js::detail::IdMatchesAtom(jsid id, JSAtom* atom) return id == INTERNED_STRING_TO_JSID(nullptr, atom); } -JS_FRIEND_API(JSContext*) -js::DefaultJSContext(JSRuntime* rt) +JS_FRIEND_API(bool) +js::PrepareScriptEnvironmentAndInvoke(JSRuntime* rt, HandleObject scope, ScriptEnvironmentPreparer::Closure& closure) { - if (rt->defaultJSContextCallback) { - JSContext* cx = rt->defaultJSContextCallback(rt); - MOZ_ASSERT(cx); - return cx; - } + if (rt->scriptEnvironmentPreparer) + return rt->scriptEnvironmentPreparer->invoke(scope, closure); + MOZ_ASSERT(rt->contextList.getFirst() == rt->contextList.getLast()); - return rt->contextList.getFirst(); + JSContext* cx = rt->contextList.getFirst(); + JSAutoCompartment ac(cx, scope); + bool ok = closure(cx); + + // NB: This does not affect Gecko, which has a prepareScriptEnvironment + // callback. + if (JS_IsExceptionPending(cx)) { + JS_ReportPendingException(cx); + } + + return ok; } JS_FRIEND_API(void) -js::SetDefaultJSContextCallback(JSRuntime* rt, DefaultJSContextCallback cb) +js::SetScriptEnvironmentPreparer(JSRuntime* rt, ScriptEnvironmentPreparer* preparer) { - rt->defaultJSContextCallback = cb; + rt->scriptEnvironmentPreparer = preparer; } #ifdef DEBUG diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 531e2cc4d7..b5d59c7537 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -2590,18 +2590,32 @@ IdToValue(jsid id) } /* - * If the embedder has registered a default JSContext callback, returns the - * result of the callback. Otherwise, asserts that |rt| has exactly one - * JSContext associated with it, and returns that context. + * If the embedder has registered a ScriptEnvironmentPreparer, + * PrepareScriptEnvironmentAndInvoke will call the preparer's 'invoke' method + * with the given |closure|, with the assumption that the preparer will set up + * any state necessary to run script in |scope|, invoke |closure| with a valid + * JSContext*, and return. + * + * If no preparer is registered, PrepareScriptEnvironmentAndInvoke will assert + * that |rt| has exactly one JSContext associated with it, enter the compartment + * of |scope| on that context, and invoke |closure|. */ -extern JS_FRIEND_API(JSContext*) -DefaultJSContext(JSRuntime* rt); -typedef JSContext* -(* DefaultJSContextCallback)(JSRuntime* rt); +struct ScriptEnvironmentPreparer { + struct Closure { + virtual bool operator()(JSContext* cx) = 0; + }; + + virtual bool invoke(JS::HandleObject scope, Closure& closure) = 0; +}; + +extern JS_FRIEND_API(bool) +PrepareScriptEnvironmentAndInvoke(JSRuntime* rt, JS::HandleObject scope, + ScriptEnvironmentPreparer::Closure& closure); JS_FRIEND_API(void) -SetDefaultJSContextCallback(JSRuntime* rt, DefaultJSContextCallback cb); +SetScriptEnvironmentPreparer(JSRuntime* rt, ScriptEnvironmentPreparer* +preparer); /* * To help embedders enforce their invariants, we allow them to specify in diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index a4f2d5029a..e8c6658cc2 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1068,7 +1068,7 @@ GCRuntime::allocateArena(Chunk* chunk, Zone* zone, AllocKind thingKind, const Au MOZ_ASSERT(chunk->hasAvailableArenas()); // Fail the allocation if we are over our heap size limits. - if (!isHeapMinorCollecting() && + if (!rt->isHeapMinorCollecting() && !isHeapCompacting() && usage.gcBytes() >= tunables.gcMaxBytes()) { @@ -1079,7 +1079,7 @@ GCRuntime::allocateArena(Chunk* chunk, Zone* zone, AllocKind thingKind, const Au zone->usage.addGCArena(); // Trigger an incremental slice if needed. - if (!isHeapMinorCollecting() && !isHeapCompacting()) + if (!rt->isHeapMinorCollecting() && !isHeapCompacting()) maybeAllocTriggerZoneGC(zone, lock); return aheader; @@ -1152,7 +1152,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) : manipulatingDeadZones(false), objectsMarkedInDeadZones(0), poked(false), - heapState(Idle), #ifdef JS_GC_ZEAL zealMode(0), zealFrequency(0), @@ -1530,7 +1529,7 @@ GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) void GCRuntime::setMarkStackLimit(size_t limit) { - MOZ_ASSERT(!isHeapBusy()); + MOZ_ASSERT(!rt->isHeapBusy()); AutoStopVerifyingBarriers pauseVerification(rt, false); marker.setMaxCapacity(limit); } @@ -2127,7 +2126,7 @@ ArenaLists::relocateArenas(ArenaHeader *&relocatedListOut, JS::gcreason::Reason // This is only called from the main thread while we are doing a GC, so // there is no need to lock. MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_)); - MOZ_ASSERT(runtime_->isHeapCompacting()); + MOZ_ASSERT(runtime_->gc.isHeapCompacting()); MOZ_ASSERT(!runtime_->gc.isBackgroundSweeping()); // Flush all the freeLists back into the arena headers @@ -3450,7 +3449,7 @@ GCRuntime::queueZonesForBackgroundSweep(ZoneList& zones) void GCRuntime::freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo) { - MOZ_ASSERT(isHeapBusy()); + MOZ_ASSERT(rt->isHeapBusy()); AutoLockGC lock(rt); freeLifoAlloc.transferUnusedFrom(lifo); } @@ -3458,7 +3457,7 @@ GCRuntime::freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo) void GCRuntime::freeAllLifoBlocksAfterSweeping(LifoAlloc* lifo) { - MOZ_ASSERT(isHeapBusy()); + MOZ_ASSERT(rt->isHeapBusy()); AutoLockGC lock(rt); freeLifoAlloc.transferFrom(lifo); } @@ -4306,17 +4305,6 @@ GCRuntime::finishMarkingValidation() #endif } -static void -AssertNeedsBarrierFlagsConsistent(JSRuntime* rt) -{ -#ifdef JS_GC_MARKING_VALIDATION - bool anyNeedsBarrier = false; - for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) - anyNeedsBarrier |= zone->needsIncrementalBarrier(); - MOZ_ASSERT(rt->needsIncrementalBarrier() == anyNeedsBarrier); -#endif -} - static void DropStringWrappers(JSRuntime* rt) { @@ -4482,8 +4470,6 @@ GCRuntime::getNextZoneGroup() zone->setGCState(Zone::NoGC); zone->gcGrayRoots.clearAndFree(); } - rt->setNeedsIncrementalBarrier(false); - AssertNeedsBarrierFlagsConsistent(rt); for (GCCompartmentGroupIter comp(rt); !comp.done(); comp.next()) ResetGrayList(comp); @@ -5521,14 +5507,14 @@ GCRuntime::finishCollection(JS::gcreason::Reason reason) } /* Start a new heap session. */ -AutoTraceSession::AutoTraceSession(JSRuntime* rt, js::HeapState heapState) +AutoTraceSession::AutoTraceSession(JSRuntime* rt, JS::HeapState heapState) : lock(rt), runtime(rt), - prevState(rt->gc.heapState) + prevState(rt->heapState_) { - MOZ_ASSERT(rt->gc.heapState == Idle); - MOZ_ASSERT(heapState != Idle); - MOZ_ASSERT_IF(heapState == MajorCollecting, rt->gc.nursery.isEmpty()); + MOZ_ASSERT(rt->heapState_ == JS::HeapState::Idle); + MOZ_ASSERT(heapState != JS::HeapState::Idle); + MOZ_ASSERT_IF(heapState == JS::HeapState::MajorCollecting, rt->gc.nursery.isEmpty()); // Threads with an exclusive context can hit refillFreeList while holding // the exclusive access lock. To avoid deadlocking when we try to acquire @@ -5540,9 +5526,9 @@ AutoTraceSession::AutoTraceSession(JSRuntime* rt, js::HeapState heapState) // Lock the helper thread state when changing the heap state in the // presence of exclusive threads, to avoid racing with refillFreeList. AutoLockHelperThreadState lock; - rt->gc.heapState = heapState; + rt->heapState_ = heapState; } else { - rt->gc.heapState = heapState; + rt->heapState_ = heapState; } } @@ -5552,12 +5538,12 @@ AutoTraceSession::~AutoTraceSession() if (runtime->exclusiveThreadsPresent()) { AutoLockHelperThreadState lock; - runtime->gc.heapState = prevState; + runtime->heapState_ = prevState; // Notify any helper threads waiting for the trace session to end. HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER); } else { - runtime->gc.heapState = prevState; + runtime->heapState_ = prevState; } } @@ -5614,8 +5600,6 @@ GCRuntime::resetIncrementalGC(const char* reason) zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit); zone->setGCState(Zone::NoGC); } - rt->setNeedsIncrementalBarrier(false); - AssertNeedsBarrierFlagsConsistent(rt); freeLifoAlloc.freeAll(); @@ -5727,25 +5711,19 @@ AutoGCSlice::AutoGCSlice(JSRuntime* rt) MOZ_ASSERT(!zone->needsIncrementalBarrier()); } } - rt->setNeedsIncrementalBarrier(false); - AssertNeedsBarrierFlagsConsistent(rt); } AutoGCSlice::~AutoGCSlice() { /* We can't use GCZonesIter if this is the end of the last slice. */ - bool haveBarriers = false; for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) { if (zone->isGCMarking()) { zone->setNeedsIncrementalBarrier(true, Zone::UpdateJit); zone->arenas.prepareForIncrementalGC(runtime); - haveBarriers = true; } else { zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit); } } - runtime->setNeedsIncrementalBarrier(haveBarriers); - AssertNeedsBarrierFlagsConsistent(runtime); } void @@ -6008,7 +5986,7 @@ GCRuntime::gcCycle(bool incremental, SliceBudget& budget, JS::gcreason::Reason r */ AutoDisableStoreBuffer adsb(this); - AutoTraceSession session(rt, MajorCollecting); + AutoTraceSession session(rt, JS::HeapState::MajorCollecting); majorGCTriggerReason = JS::gcreason::NO_REASON; interFrameGC = true; @@ -6278,7 +6256,7 @@ GCRuntime::abortGC() evictNursery(JS::gcreason::ABORT_GC); AutoDisableStoreBuffer adsb(this); - AutoTraceSession session(rt, MajorCollecting); + AutoTraceSession session(rt, JS::HeapState::MajorCollecting); number++; resetIncrementalGC("abort"); @@ -6665,14 +6643,14 @@ GCRuntime::runDebugGC() void GCRuntime::setValidate(bool enabled) { - MOZ_ASSERT(!isHeapMajorCollecting()); + MOZ_ASSERT(!rt->isHeapMajorCollecting()); validate = enabled; } void GCRuntime::setFullCompartmentChecks(bool enabled) { - MOZ_ASSERT(!isHeapMajorCollecting()); + MOZ_ASSERT(!rt->isHeapMajorCollecting()); fullCompartmentChecks = enabled; } @@ -6680,7 +6658,7 @@ GCRuntime::setFullCompartmentChecks(bool enabled) bool GCRuntime::selectForMarking(JSObject* object) { - MOZ_ASSERT(!isHeapMajorCollecting()); + MOZ_ASSERT(!rt->isHeapMajorCollecting()); return selectedForMarking.append(object); } @@ -6693,7 +6671,7 @@ GCRuntime::clearSelectedForMarking() void GCRuntime::setDeterministic(bool enabled) { - MOZ_ASSERT(!isHeapMajorCollecting()); + MOZ_ASSERT(!rt->isHeapMajorCollecting()); deterministicOnly = enabled; } #endif diff --git a/js/src/jsgc.h b/js/src/jsgc.h index f1e682c5d0..dd82b9730b 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -39,13 +39,6 @@ namespace js { unsigned GetCPUCount(); -enum HeapState { - Idle, // doing nothing with the GC heap - Tracing, // tracing the GC heap without collecting, e.g. IterateCompartments() - MajorCollecting, // doing a GC of the major heap - MinorCollecting // doing a GC of the minor heap (nursery) -}; - enum ThreadType { MainThread, diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index d00cf33806..4602196bc2 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -128,6 +128,7 @@ namespace js { void FinishGC(JSRuntime* rt); namespace gc { +class AutoTraceSession; class StoreBuffer; void MarkPersistentRootedChains(JSTracer*); void FinishPersistentRootedChains(JSRuntime*); @@ -138,24 +139,32 @@ namespace JS { typedef void (*OffThreadCompileCallback)(void* token, void* callbackData); +enum class HeapState { + Idle, // doing nothing with the GC heap + Tracing, // tracing the GC heap without collecting, e.g. IterateCompartments() + MajorCollecting, // doing a GC of the major heap + MinorCollecting // doing a GC of the minor heap (nursery) +}; + namespace shadow { struct Runtime { - /* Restrict zone access during Minor GC. */ - bool needsIncrementalBarrier_; + protected: + // Allow inlining of heapState checks. + friend class js::gc::AutoTraceSession; + JS::HeapState heapState_; - private: js::gc::StoreBuffer* gcStoreBufferPtr_; public: Runtime() - : needsIncrementalBarrier_(false) + : heapState_(JS::HeapState::Idle) , gcStoreBufferPtr_(nullptr) {} bool needsIncrementalBarrier() const { - return needsIncrementalBarrier_; + return heapState_ == JS::HeapState::Idle; } js::gc::StoreBuffer* gcStoreBufferPtr() { return gcStoreBufferPtr_; } diff --git a/js/src/tests/ecma_6/TypedArray/slice.js b/js/src/tests/ecma_6/TypedArray/slice.js index 2f3333959b..4bef07430f 100644 --- a/js/src/tests/ecma_6/TypedArray/slice.js +++ b/js/src/tests/ecma_6/TypedArray/slice.js @@ -68,8 +68,8 @@ for (var constructor of constructors) { mathConstructor.constructor = Math.sin; assertDeepEq(mathConstructor.slice(4), new constructor(4)); - assertEq("species" in Symbol, false, - "you've implemented @@species -- add real tests here!"); + assertEq(Symbol.species in Int8Array, false, + "you've implemented %TypedArray%[@@species] -- add real tests here!"); } if (typeof reportCompare === "function") diff --git a/js/src/tests/lib/jittests.py b/js/src/tests/lib/jittests.py index 180234153c..9e036bbc9f 100755 --- a/js/src/tests/lib/jittests.py +++ b/js/src/tests/lib/jittests.py @@ -215,6 +215,8 @@ class Test: test.test_join.append([name[len('test-join='):]]) elif name == 'ion-eager': test.jitflags.append('--ion-eager') + elif name == 'baseline-eager': + test.jitflags.append('--baseline-eager') elif name == 'dump-bytecode': test.jitflags.append('--dump-bytecode') elif name.startswith('--'): diff --git a/js/src/vm/MallocProvider.h b/js/src/vm/MallocProvider.h index f334eb13f7..c4783c0a18 100644 --- a/js/src/vm/MallocProvider.h +++ b/js/src/vm/MallocProvider.h @@ -69,7 +69,7 @@ struct MallocProvider client()->reportAllocationOverflow(); return nullptr; } - return static_cast(client()->onOutOfMemory(nullptr, bytes)); + return static_cast(client()->onOutOfMemory(AllocFunction::Malloc, bytes)); } template @@ -84,7 +84,7 @@ struct MallocProvider client()->updateMallocCounter(bytes); return p; } - return static_cast(client()->onOutOfMemory(nullptr, bytes)); + return static_cast(client()->onOutOfMemory(AllocFunction::Malloc, bytes)); } template @@ -110,7 +110,7 @@ struct MallocProvider client()->reportAllocationOverflow(); return nullptr; } - return static_cast(client()->onOutOfMemory(nullptr, bytes)); + return static_cast(client()->onOutOfMemory(AllocFunction::Calloc, bytes)); } template @@ -125,7 +125,7 @@ struct MallocProvider client()->updateMallocCounter(bytes); return p; } - return static_cast(client()->onOutOfMemory(nullptr, bytes)); + return static_cast(client()->onOutOfMemory(AllocFunction::Calloc, bytes)); } template @@ -150,7 +150,7 @@ struct MallocProvider client()->reportAllocationOverflow(); return nullptr; } - return static_cast(client()->onOutOfMemory(prior, bytes)); + return static_cast(client()->onOutOfMemory(AllocFunction::Realloc, bytes)); } JS_DECLARE_NEW_METHODS(new_, pod_malloc, MOZ_ALWAYS_INLINE) diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 474d60651f..33d83c2841 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -933,7 +933,8 @@ RegExpCompartment::sweep(JSRuntime* rt) keep = false; } } - if (keep || rt->isHeapCompacting()) { + MOZ_ASSERT(rt->isHeapMajorCollecting()); + if (keep || rt->gc.isHeapCompacting()) { shared->clearMarked(); } else { js_delete(shared); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 76314ac4b3..a0d00a95e6 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -207,7 +207,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) jitSupportsFloatingPoint(false), jitSupportsSimd(false), ionPcScriptCache(nullptr), - defaultJSContextCallback(nullptr), + scriptEnvironmentPreparer(nullptr), ctypesActivityCallback(nullptr), offthreadIonCompilationEnabled_(true), parallelParsingEnabled_(true), @@ -727,14 +727,11 @@ JSRuntime::onTooMuchMalloc() } JS_FRIEND_API(void*) -JSRuntime::onOutOfMemory(void* p, size_t nbytes) +JSRuntime::onOutOfMemory(AllocFunction allocFunc, size_t nbytes, void* reallocPtr, + JSContext* maybecx) { - return onOutOfMemory(p, nbytes, nullptr); -} + MOZ_ASSERT_IF(allocFunc != AllocFunction::Realloc, !reallocPtr); -JS_FRIEND_API(void*) -JSRuntime::onOutOfMemory(void* p, size_t nbytes, JSContext* cx) -{ if (isHeapBusy()) return nullptr; @@ -743,25 +740,34 @@ JSRuntime::onOutOfMemory(void* p, size_t nbytes, JSContext* cx) * all the allocations and released the empty GC chunks. */ gc.onOutOfMallocMemory(); - if (!p) + void* p; + switch (allocFunc) { + case AllocFunction::Malloc: p = js_malloc(nbytes); - else if (p == reinterpret_cast(1)) + break; + case AllocFunction::Calloc: p = js_calloc(nbytes); - else - p = js_realloc(p, nbytes); + break; + case AllocFunction::Realloc: + p = js_realloc(reallocPtr, nbytes); + break; + default: + MOZ_CRASH(); + } if (p) return p; - if (cx) - ReportOutOfMemory(cx); + + if (maybecx) + ReportOutOfMemory(maybecx); return nullptr; } void* -JSRuntime::onOutOfMemoryCanGC(void* p, size_t bytes) +JSRuntime::onOutOfMemoryCanGC(AllocFunction allocFunc, size_t bytes, void* reallocPtr) { if (largeAllocationFailureCallback && bytes >= LARGE_ALLOCATION) largeAllocationFailureCallback(largeAllocationFailureCallbackData); - return onOutOfMemory(p, bytes); + return onOutOfMemory(allocFunc, bytes, reallocPtr); } bool diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index f3fd3a3a62..6c3a193e66 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -709,18 +709,34 @@ struct JSRuntime : public JS::shadow::Runtime, uint32_t profilerSampleBufferGen() { return profilerSampleBufferGen_; } + void resetProfilerSampleBufferGen() { + profilerSampleBufferGen_ = 0; + } void setProfilerSampleBufferGen(uint32_t gen) { - profilerSampleBufferGen_ = gen; + // May be called from sampler thread or signal handler; use + // compareExchange to make sure we have monotonic increase. + for (;;) { + uint32_t curGen = profilerSampleBufferGen_; + if (curGen >= gen) + break; + + if (profilerSampleBufferGen_.compareExchange(curGen, gen)) + break; + } } uint32_t profilerSampleBufferLapCount() { MOZ_ASSERT(profilerSampleBufferLapCount_ > 0); return profilerSampleBufferLapCount_; } + void resetProfilerSampleBufferLapCount() { + profilerSampleBufferLapCount_ = 1; + } void updateProfilerSampleBufferLapCount(uint32_t lapCount) { MOZ_ASSERT(profilerSampleBufferLapCount_ > 0); - // Use compareExchange to make sure we have monotonic increase. + // May be called from sampler thread or signal handler; use + // compareExchange to make sure we have monotonic increase. for (;;) { uint32_t curLapCount = profilerSampleBufferLapCount_; if (curLapCount >= lapCount) @@ -984,11 +1000,10 @@ struct JSRuntime : public JS::shadow::Runtime, /* Garbage collector state has been sucessfully initialized. */ bool gcInitialized; - bool isHeapBusy() { return gc.isHeapBusy(); } - bool isHeapMajorCollecting() { return gc.isHeapMajorCollecting(); } - bool isHeapMinorCollecting() { return gc.isHeapMinorCollecting(); } - bool isHeapCollecting() { return gc.isHeapCollecting(); } - bool isHeapCompacting() { return gc.isHeapCompacting(); } + bool isHeapBusy() const { return heapState_ != JS::HeapState::Idle; } + bool isHeapMajorCollecting() const { return heapState_ == JS::HeapState::MajorCollecting; } + bool isHeapMinorCollecting() const { return heapState_ == JS::HeapState::MinorCollecting; } + bool isHeapCollecting() const { return isHeapMinorCollecting() || isHeapMajorCollecting(); } int gcZeal() { return gc.zeal(); } @@ -1006,10 +1021,6 @@ struct JSRuntime : public JS::shadow::Runtime, #endif public: - void setNeedsIncrementalBarrier(bool needs) { - needsIncrementalBarrier_ = needs; - } - #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) js::jit::Simulator* simulator() const; uintptr_t* addressOfSimulatorStackLimit(); @@ -1308,7 +1319,7 @@ struct JSRuntime : public JS::shadow::Runtime, // Cache for jit::GetPcScript(). js::jit::PcScriptCache* ionPcScriptCache; - js::DefaultJSContextCallback defaultJSContextCallback; + js::ScriptEnvironmentPreparer* scriptEnvironmentPreparer; js::CTypesActivityCallback ctypesActivityCallback; @@ -1346,18 +1357,18 @@ struct JSRuntime : public JS::shadow::Runtime, JS_FRIEND_API(void) onTooMuchMalloc(); /* - * This should be called after system malloc/realloc returns nullptr to try - * to recove some memory or to report an error. Failures in malloc and - * calloc are signaled by p == null and p == reinterpret_cast(1). - * Other values of p mean a realloc failure. + * This should be called after system malloc/calloc/realloc returns nullptr + * to try to recove some memory or to report an error. For realloc, the + * original pointer must be passed as reallocPtr. * * The function must be called outside the GC lock. */ - JS_FRIEND_API(void*) onOutOfMemory(void* p, size_t nbytes); - JS_FRIEND_API(void*) onOutOfMemory(void* p, size_t nbytes, JSContext* cx); + JS_FRIEND_API(void*) onOutOfMemory(js::AllocFunction allocator, size_t nbytes, + void* reallocPtr = nullptr, JSContext* maybecx = nullptr); /* onOutOfMemory but can call the largeAllocationFailureCallback. */ - JS_FRIEND_API(void*) onOutOfMemoryCanGC(void* p, size_t bytes); + JS_FRIEND_API(void*) onOutOfMemoryCanGC(js::AllocFunction allocator, size_t nbytes, + void* reallocPtr = nullptr); void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes* runtime); @@ -1422,7 +1433,7 @@ struct JSRuntime : public JS::shadow::Runtime, reportAllocationOverflow(); return nullptr; } - return static_cast(onOutOfMemoryCanGC(reinterpret_cast(1), bytes)); + return static_cast(onOutOfMemoryCanGC(js::AllocFunction::Calloc, bytes)); } template @@ -1435,7 +1446,7 @@ struct JSRuntime : public JS::shadow::Runtime, reportAllocationOverflow(); return nullptr; } - return static_cast(onOutOfMemoryCanGC(p, bytes)); + return static_cast(onOutOfMemoryCanGC(js::AllocFunction::Realloc, bytes)); } /* diff --git a/js/src/vm/SPSProfiler.cpp b/js/src/vm/SPSProfiler.cpp index c010affe58..72204474ec 100644 --- a/js/src/vm/SPSProfiler.cpp +++ b/js/src/vm/SPSProfiler.cpp @@ -14,6 +14,7 @@ #include "jit/BaselineFrame.h" #include "jit/BaselineJIT.h" +#include "jit/JitcodeMap.h" #include "jit/JitFrameIterator.h" #include "jit/JitFrames.h" #include "vm/StringBuffer.h" @@ -87,6 +88,15 @@ SPSProfiler::enable(bool enabled) */ ReleaseAllJITCode(rt->defaultFreeOp()); + // This function is called when the Gecko profiler makes a new TableTicker + // (and thus, a new circular buffer). Set all current entries in the + // JitcodeGlobalTable as expired and reset the buffer generation and lap + // count. + if (rt->hasJitRuntime() && rt->jitRuntime()->hasJitcodeGlobalTable()) + rt->jitRuntime()->getJitcodeGlobalTable()->setAllEntriesAsExpired(rt); + rt->resetProfilerSampleBufferGen(); + rt->resetProfilerSampleBufferLapCount(); + // Ensure that lastProfilingFrame is null before 'enabled' becomes true. if (rt->jitActivation) { rt->jitActivation->setLastProfilingFrame(nullptr); @@ -449,8 +459,8 @@ AutoSuppressProfilerSampling::AutoSuppressProfilerSampling(JSRuntime* rt AutoSuppressProfilerSampling::~AutoSuppressProfilerSampling() { - if (previouslyEnabled_) - rt_->enableProfilerSampling(); + if (previouslyEnabled_) + rt_->enableProfilerSampling(); } void* diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index 8e0f9b0f17..09120f012f 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -337,7 +337,8 @@ UnboxedPlainObject::ensureExpando(JSContext* cx, Handle obj if (obj->expando_) return obj->expando_; - UnboxedExpandoObject* expando = NewObjectWithGivenProto(cx, nullptr); + UnboxedExpandoObject* expando = + NewObjectWithGivenProto(cx, nullptr, AllocKind::OBJECT4); if (!expando) return nullptr; diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 07994227f1..a082e7fa6e 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -26,6 +26,7 @@ #include "nsPrintfCString.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" +#include "mozilla/dom/ScriptSettings.h" #include "nsContentUtils.h" #include "nsCCUncollectableMarker.h" @@ -54,6 +55,7 @@ using namespace mozilla; using namespace xpc; using namespace JS; using mozilla::dom::PerThreadAtomCache; +using mozilla::dom::AutoEntryScript; /***************************************************************************/ @@ -1338,11 +1340,15 @@ xpc::SimulateActivityCallback(bool aActive) } // static -JSContext* -XPCJSRuntime::DefaultJSContextCallback(JSRuntime* rt) +bool +XPCJSRuntime::EnvironmentPreparer::invoke(HandleObject scope, js::ScriptEnvironmentPreparer::Closure& closure) { - MOZ_ASSERT(rt == Get()->Runtime()); - return Get()->GetJSContextStack()->GetSafeJSContext(); + MOZ_ASSERT(NS_IsMainThread()); + nsIGlobalObject* global = NativeGlobal(scope); + NS_ENSURE_TRUE(global && global->GetGlobalJSObject(), false); + AutoEntryScript aes(global, "JS-engine-initiated execution"); + aes.TakeOwnershipOfErrorReporting(); + return closure(aes.cx()); } // static @@ -1357,22 +1363,6 @@ XPCJSRuntime::ActivityCallback(void* arg, bool active) self->mWatchdogManager->RecordRuntimeActivity(active); } -// static -// -// JS-CTypes creates and caches a JSContext that it uses when executing JS -// callbacks. When we're notified that ctypes is about to call into some JS, -// push the cx to maintain the integrity of the context stack. -void -XPCJSRuntime::CTypesActivityCallback(JSContext* cx, js::CTypesActivityType type) -{ - if (type == js::CTYPES_CALLBACK_BEGIN) { - if (!xpc::PushJSContextNoScriptContext(cx)) - MOZ_CRASH(); - } else if (type == js::CTYPES_CALLBACK_END) { - xpc::PopJSContextNoScriptContext(); - } -} - // static bool XPCJSRuntime::InterruptCallback(JSContext* cx) @@ -3395,9 +3385,12 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) JS_AddWeakPointerCallback(runtime, WeakPointerCallback, this); JS_SetWrapObjectCallbacks(runtime, &WrapObjectCallbacks); js::SetPreserveWrapperCallback(runtime, PreserveWrapper); - js::SetDefaultJSContextCallback(runtime, DefaultJSContextCallback); +#ifdef MOZ_ENABLE_PROFILER_SPS + if (PseudoStack* stack = mozilla_get_pseudo_stack()) + stack->sampleRuntime(runtime); +#endif + js::SetScriptEnvironmentPreparer(runtime, &mEnvironmentPreparer); js::SetActivityCallback(runtime, ActivityCallback, this); - js::SetCTypesActivityCallback(runtime, CTypesActivityCallback); JS_SetInterruptCallback(runtime, InterruptCallback); // The JS engine needs to keep the source code around in order to implement diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 50a36145b6..d7c5a883a3 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -974,7 +974,7 @@ nsXPConnect::OnProcessNextEvent(nsIThreadInternal* aThread, bool aMayWait, // Push a null JSContext so that we don't see any script during // event processing. - bool ok = PushJSContextNoScriptContext(nullptr); + bool ok = PushNullJSContext(); NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); return NS_OK; } @@ -1000,7 +1000,7 @@ nsXPConnect::AfterProcessNextEvent(nsIThreadInternal* aThread, Promise::PerformMicroTaskCheckpoint(); - PopJSContextNoScriptContext(); + PopNullJSContext(); return NS_OK; } @@ -1080,15 +1080,15 @@ nsXPConnect::GetSafeJSContext() namespace xpc { bool -PushJSContextNoScriptContext(JSContext* aCx) +PushNullJSContext() { - MOZ_ASSERT_IF(aCx, !GetScriptContextFromJSContext(aCx)); - return XPCJSRuntime::Get()->GetJSContextStack()->Push(aCx); + return XPCJSRuntime::Get()->GetJSContextStack()->Push(nullptr); } void -PopJSContextNoScriptContext() +PopNullJSContext() { + MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == nullptr); XPCJSRuntime::Get()->GetJSContextStack()->Pop(); } diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 155925f13f..2bdd828f69 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -611,10 +611,12 @@ public: void AddContextCallback(xpcContextCallback cb); void RemoveContextCallback(xpcContextCallback cb); - static JSContext* DefaultJSContextCallback(JSRuntime* rt); + struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer { + bool invoke(JS::HandleObject scope, Closure& closure) override; + }; + EnvironmentPreparer mEnvironmentPreparer; + static void ActivityCallback(void* arg, bool active); - static void CTypesActivityCallback(JSContext* cx, - js::CTypesActivityType type); static bool InterruptCallback(JSContext* cx); size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); @@ -2848,14 +2850,8 @@ struct XPCJSContextInfo { }; namespace xpc { - -// These functions are used in a few places where a callback model makes it -// impossible to push a JSContext using one of our stack-scoped classes. We -// depend on those stack-scoped classes to maintain nsIScriptContext -// invariants, so these functions may only be used of the context is not -// associated with an nsJSContext/nsIScriptContext. -bool PushJSContextNoScriptContext(JSContext* aCx); -void PopJSContextNoScriptContext(); +bool PushNullJSContext(); +void PopNullJSContext(); } /* namespace xpc */ @@ -2896,8 +2892,8 @@ public: private: friend class mozilla::dom::danger::AutoCxPusher; - friend bool xpc::PushJSContextNoScriptContext(JSContext* aCx); - friend void xpc::PopJSContextNoScriptContext(); + friend bool xpc::PushNullJSContext(); + friend void xpc::PopNullJSContext(); // We make these private so that stack manipulation can only happen // through one of the above friends. diff --git a/tools/profiler/TableTicker.cpp b/tools/profiler/TableTicker.cpp index 35a2a01cc7..d01bef05b5 100644 --- a/tools/profiler/TableTicker.cpp +++ b/tools/profiler/TableTicker.cpp @@ -785,7 +785,10 @@ void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, Native uint32_t lapCount = aProfile.bufferGeneration() - startBufferGen; // Update the JS runtime with the current profile sample buffer generation. - if (pseudoStack->mRuntime) { + // + // Do not do this for synchronous sampling, which create their own + // ProfileBuffers. + if (!aSample->isSamplingCurrentThread && pseudoStack->mRuntime) { JS::UpdateJSRuntimeProfilerSampleBufferGen(pseudoStack->mRuntime, aProfile.bufferGeneration(), lapCount);