author | Till Schneidereit <till@tillschneidereit.net> |
Thu, 09 Jan 2014 17:13:25 +0100 | |
changeset 162936 | 0292b2de36c0dede739ce870f47fe8eeb72ab688 |
parent 162935 | 2984d53fe0cf45004ddc0d24deff8897dba0b8d6 |
child 162937 | 508ad49212c05a35643656ac554ba7a480a48020 |
push id | 25975 |
push user | ryanvm@gmail.com |
push date | Fri, 10 Jan 2014 19:46:47 +0000 |
treeherder | autoland@e89afc241513 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jandem |
bugs | 886193 |
milestone | 29.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/jsd/jsd_scpt.cpp +++ b/js/jsd/jsd_scpt.cpp @@ -355,17 +355,17 @@ JSScript * jsd_GetJSScript (JSDContext *jsdc, JSDScript *script) { return script->script; } JSFunction * jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script) { - AutoSafeJSContext cx; // NB: Actually unused. + AutoSafeJSContext cx; return JS_GetScriptFunction(cx, script->script); } JSDScript* jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp) { JSDScript *jsdscript = *iterp;
--- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -234,20 +234,18 @@ frontend::CompileScript(ExclusiveContext Parser<FullParseHandler> parser(cx, alloc, options, chars, length, /* foldConstants = */ true, canLazilyParse ? &syntaxParser.ref() : nullptr, nullptr); parser.sct = sct; parser.ss = ss; Directives directives(options.strictOption); GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption); - bool savedCallerFun = - options.compileAndGo && - evalCaller && - (evalCaller->function() || evalCaller->savedCallerFun()); + bool savedCallerFun = options.compileAndGo && + evalCaller && evalCaller->functionOrCallerFunction(); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, sourceObject, 0, length)); if (!script) return nullptr; // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). InternalHandle<Bindings*> bindings(script, &script->bindings); @@ -403,17 +401,17 @@ frontend::CompileScript(ExclusiveContext return nullptr; return script; } bool frontend::CompileLazyFunction(JSContext *cx, Handle<LazyScript*> lazy, const jschar *chars, size_t length) { - JS_ASSERT(cx->compartment() == lazy->function()->compartment()); + JS_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment()); CompileOptions options(cx, lazy->version()); options.setPrincipals(cx->compartment()->principals) .setOriginPrincipals(lazy->originPrincipals()) .setFileAndLine(lazy->source()->filename(), lazy->lineno()) .setColumn(lazy->column()) .setCompileAndGo(true) .setNoScriptRval(false) @@ -426,17 +424,17 @@ frontend::CompileLazyFunction(JSContext options); #endif Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length, /* foldConstants = */ true, nullptr, lazy); uint32_t staticLevel = lazy->staticLevel(cx); - Rooted<JSFunction*> fun(cx, lazy->function()); + Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying()); JS_ASSERT(!lazy->isLegacyGenerator()); ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict(), lazy->generatorKind()); if (!pn) return false; if (!NameFunctions(cx, pn)) return false;
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1280,17 +1280,17 @@ TryConvertFreeName(BytecodeEmitter *bce, // Use generic ops if a catch block is encountered. return false; } if (ssi.hasDynamicScopeObject()) hops++; continue; } RootedScript script(bce->sc->context, ssi.funScript()); - if (script->function()->atom() == pn->pn_atom) + if (script->functionNonDelazifying()->atom() == pn->pn_atom) return false; if (ssi.hasDynamicScopeObject()) { uint16_t slot; if (LookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot)) { JSOp op; switch (pn->getOp()) { case JSOP_NAME: op = JSOP_GETALIASEDVAR; break; case JSOP_SETNAME: op = JSOP_SETALIASEDVAR; break; @@ -1862,17 +1862,17 @@ BytecodeEmitter::needsImplicitThis() void BytecodeEmitter::tellDebuggerAboutCompiledScript(ExclusiveContext *cx) { // Note: when parsing off thread the resulting scripts need to be handed to // the debugger after rejoining to the main thread. if (!cx->isJSContext()) return; - RootedFunction function(cx, script->function()); + RootedFunction function(cx, script->functionNonDelazifying()); CallNewScriptHook(cx->asJSContext(), script, function); // Lazy scripts are never top level (despite always being invoked with a // nullptr parent), and so the hook should never be fired. if (emitterMode != LazyFunction && !parent) { GlobalObject *compileAndGoGlobal = nullptr; if (script->compileAndGo()) compileAndGoGlobal = &script->global(); Debugger::onNewScript(cx->asJSContext(), script, compileAndGoGlobal); @@ -2790,17 +2790,17 @@ frontend::EmitFunctionScript(ExclusiveCo * initializers created within it may be given more precise types. */ if (runOnce) { bce->script->setTreatAsRunOnce(); JS_ASSERT(!bce->script->hasRunOnce()); } /* Initialize fun->script() so that the debugger has a valid fun->script(). */ - RootedFunction fun(cx, bce->script->function()); + RootedFunction fun(cx, bce->script->functionNonDelazifying()); JS_ASSERT(fun->isInterpreted()); if (fun->isInterpretedLazy()) { AutoLockForCompilation lock(cx); fun->setUnlazifiedScript(bce->script); } else { fun->setScript(bce->script); }
--- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -108,18 +108,18 @@ BaselineCompiler::compile() return Method_Error; Linker linker(masm); JitCode *code = linker.newCode<CanGC>(cx, JSC::BASELINE_CODE); if (!code) return Method_Error; JSObject *templateScope = nullptr; - if (script->function()) { - RootedFunction fun(cx, script->function()); + if (script->functionNonDelazifying()) { + RootedFunction fun(cx, script->functionNonDelazifying()); if (fun->isHeavyweight()) { templateScope = CallObject::createTemplateObject(cx, script, gc::TenuredHeap); if (!templateScope) return Method_Error; if (fun->isNamedLambda()) { RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap)); if (!declEnvObject)
--- a/js/src/jit/BaselineFrame.h +++ b/js/src/jit/BaselineFrame.h @@ -177,17 +177,17 @@ class BaselineFrame } unsigned numActualArgs() const { return *(size_t *)(reinterpret_cast<const uint8_t *>(this) + BaselineFrame::Size() + offsetOfNumActualArgs()); } unsigned numFormalArgs() const { - return script()->function()->nargs(); + return script()->functionNonDelazifying()->nargs(); } Value &thisValue() const { return *(Value *)(reinterpret_cast<const uint8_t *>(this) + BaselineFrame::Size() + offsetOfThis()); } Value *argv() const { return (Value *)(reinterpret_cast<const uint8_t *>(this) +
--- a/js/src/jit/BaselineFrameInfo.h +++ b/js/src/jit/BaselineFrameInfo.h @@ -177,17 +177,17 @@ class FrameInfo { } bool init(TempAllocator &alloc); uint32_t nlocals() const { return script->nfixed(); } uint32_t nargs() const { - return script->function()->nargs(); + return script->functionNonDelazifying()->nargs(); } private: inline StackValue *rawPush() { StackValue *val = &stack[spIndex++]; val->reset(); return val; }
--- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -215,16 +215,18 @@ jit::EnterBaselineAtBranch(JSContext *cx MethodStatus jit::BaselineCompile(JSContext *cx, HandleScript script) { JS_ASSERT(!script->hasBaselineScript()); JS_ASSERT(script->canBaselineCompile()); LifoAlloc alloc(BASELINE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); + script->ensureNonLazyCanonicalFunction(cx); + TempAllocator *temp = alloc.new_<TempAllocator>(&alloc); if (!temp) return Method_Error; IonContext ictx(cx, temp); BaselineCompiler compiler(cx, *temp, script); if (!compiler.init())
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3396,17 +3396,18 @@ bool CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir) { Register obj = ToRegister(lir->output()); JSObject *templateObj = lir->mir()->templateObj(); CompileInfo &info = lir->mir()->block()->info(); // If we have a template object, we can inline call object creation. OutOfLineCode *ool = oolCallVM(NewDeclEnvObjectInfo, lir, - (ArgList(), ImmGCPtr(info.fun()), Imm32(gc::DefaultHeap)), + (ArgList(), ImmGCPtr(info.funMaybeLazy()), + Imm32(gc::DefaultHeap)), StoreRegisterTo(obj)); if (!ool) return false; masm.newGCThing(obj, templateObj, ool->entry(), gc::DefaultHeap); masm.initGCThing(obj, templateObj); masm.bind(ool->rejoin()); return true;
--- a/js/src/jit/CompileInfo.h +++ b/js/src/jit/CompileInfo.h @@ -46,20 +46,22 @@ class CompileInfo public: CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing, ExecutionMode executionMode, bool scriptNeedsArgsObj) : script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing), executionMode_(executionMode), scriptNeedsArgsObj_(scriptNeedsArgsObj) { JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY); - // The function here can flow in from anywhere so look up the canonical function to ensure that - // we do not try to embed a nursery pointer in jit-code. + // The function here can flow in from anywhere so look up the canonical + // function to ensure that we do not try to embed a nursery pointer in + // jit-code. Precisely because it can flow in from anywhere, it's not + // guaranteed to be non-lazy. Hence, don't access its script! if (fun_) { - fun_ = fun_->nonLazyScript()->function(); + fun_ = fun_->nonLazyScript()->functionNonDelazifying(); JS_ASSERT(fun_->isTenured()); } nimplicit_ = StartArgSlot(script) /* scope chain and argument obj */ + (fun ? 1 : 0); /* this */ nargs_ = fun ? fun->nargs() : 0; nlocals_ = script->nfixed(); nstack_ = script->nslots() - script->nfixed(); @@ -75,17 +77,17 @@ class CompileInfo nlocals_ = nlocals; nstack_ = 1; /* For FunctionCompiler::pushPhiInput/popPhiOutput */ nslots_ = nlocals_ + nstack_; } JSScript *script() const { return script_; } - JSFunction *fun() const { + JSFunction *funMaybeLazy() const { return fun_; } bool constructing() const { return constructing_; } jsbytecode *osrPc() { return osrPc_; } @@ -169,17 +171,17 @@ class CompileInfo JS_ASSERT(script()); return 1; } uint32_t argsObjSlot() const { JS_ASSERT(hasArguments()); return 2; } uint32_t thisSlot() const { - JS_ASSERT(fun()); + JS_ASSERT(funMaybeLazy()); JS_ASSERT(nimplicit_ > 0); return nimplicit_ - 1; } uint32_t firstArgSlot() const { return nimplicit_; } uint32_t argSlotUnchecked(uint32_t i) const { // During initialization, some routines need to get at arg @@ -208,26 +210,26 @@ class CompileInfo } uint32_t startArgSlot() const { JS_ASSERT(script()); return StartArgSlot(script()); } uint32_t endArgSlot() const { JS_ASSERT(script()); - return CountArgSlots(script(), fun()); + return CountArgSlots(script(), funMaybeLazy()); } uint32_t totalSlots() const { - JS_ASSERT(script() && fun()); + JS_ASSERT(script() && funMaybeLazy()); return nimplicit() + nargs() + nlocals(); } bool isSlotAliased(uint32_t index) const { - if (fun() && index == thisSlot()) + if (funMaybeLazy() && index == thisSlot()) return false; uint32_t arg = index - firstArgSlot(); if (arg < nargs()) { if (script()->formalIsAliased(arg)) return true; return false; }
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -1617,17 +1617,19 @@ TrackAllProperties(JSContext *cx, JSObje static void TrackPropertiesForSingletonScopes(JSContext *cx, JSScript *script, BaselineFrame *baselineFrame) { // Ensure that all properties of singleton call objects which the script // could access are tracked. These are generally accessed through // ALIASEDVAR operations in baseline and will not be tracked even if they // have been accessed in baseline code. - JSObject *environment = script->function() ? script->function()->environment() : nullptr; + JSObject *environment = script->functionNonDelazifying() + ? script->functionNonDelazifying()->environment() + : nullptr; while (environment && !environment->is<GlobalObject>()) { if (environment->is<CallObject>() && environment->hasSingletonType()) TrackAllProperties(cx, environment); environment = environment->enclosingScope(); } if (baselineFrame) { @@ -1646,16 +1648,20 @@ IonCompile(JSContext *cx, JSScript *scri #if JS_TRACE_LOGGING AutoTraceLog logger(TraceLogging::defaultLogger(), TraceLogging::ION_COMPILE_START, TraceLogging::ION_COMPILE_STOP, script); #endif JS_ASSERT(optimizationLevel > Optimization_DontCompile); + // Make sure the script's canonical function isn't lazy. We can't de-lazify + // it in a worker thread. + script->ensureNonLazyCanonicalFunction(cx); + TrackPropertiesForSingletonScopes(cx, script, baselineFrame); LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); if (!alloc) return AbortReason_Alloc; ScopedJSDeletePtr<LifoAlloc> autoDelete(alloc); @@ -1672,18 +1678,19 @@ IonCompile(JSContext *cx, JSScript *scri if (!cx->compartment()->jitCompartment()->ensureIonStubsExist(cx)) return AbortReason_Alloc; MIRGraph *graph = alloc->new_<MIRGraph>(temp); if (!graph) return AbortReason_Alloc; - CompileInfo *info = alloc->new_<CompileInfo>(script, script->function(), osrPc, constructing, - executionMode, script->needsArgsObj()); + CompileInfo *info = alloc->new_<CompileInfo>(script, script->functionNonDelazifying(), osrPc, + constructing, executionMode, + script->needsArgsObj()); if (!info) return AbortReason_Alloc; BaselineInspector *inspector = alloc->new_<BaselineInspector>(script); if (!inspector) return AbortReason_Alloc; BaselineFrameInspector *baselineFrameInspector = nullptr; @@ -2213,17 +2220,17 @@ jit::CanEnterUsingFastInvoke(JSContext * JS_ASSERT(jit::IsIonEnabled(cx)); // Skip if the code is expected to result in a bailout. if (!script->hasIonScript() || script->ionScript()->bailoutExpected()) return Method_Skipped; // Don't handle arguments underflow, to make this work we would have to pad // missing arguments with |undefined|. - if (numActualArgs < script->function()->nargs()) + if (numActualArgs < script->functionNonDelazifying()->nargs()) return Method_Skipped; if (!cx->compartment()->ensureJitCompartmentExists(cx)) return Method_Error; // This can GC, so afterward, script->ion is not guaranteed to be valid. if (!cx->runtime()->jitRuntime()->enterIon()) return Method_Error; @@ -2274,17 +2281,17 @@ EnterIon(JSContext *cx, EnterJitData &da bool jit::SetEnterJitData(JSContext *cx, EnterJitData &data, RunState &state, AutoValueVector &vals) { data.osrFrame = nullptr; if (state.isInvoke()) { CallArgs &args = state.asInvoke()->args(); - unsigned numFormals = state.script()->function()->nargs(); + unsigned numFormals = state.script()->functionNonDelazifying()->nargs(); data.constructing = state.asInvoke()->constructing(); data.numActualArgs = args.length(); data.maxArgc = Max(args.length(), numFormals) + 1; data.scopeChain = nullptr; data.calleeToken = CalleeToToken(&args.callee().as<JSFunction>()); if (data.numActualArgs >= numFormals) { data.maxArgv = args.base() + 1;
--- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -211,17 +211,17 @@ IsPhiObservable(MPhi *phi, Observability !iter->consumer()->toDefinition()->isPhi()) return true; } break; } uint32_t slot = phi->slot(); CompileInfo &info = phi->block()->info(); - JSFunction *fun = info.fun(); + JSFunction *fun = info.funMaybeLazy(); // If the Phi is of the |this| value, it must always be observable. if (fun && slot == info.thisSlot()) return true; // If the function may need an arguments object, then make sure to preserve // the scope chain, because it may be needed to construct the arguments // object during bailout.
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -70,17 +70,17 @@ jit::NewBaselineFrameInspector(TempAlloc inspector->thisType = types::GetValueType(frame->thisValue()); if (frame->scopeChain()->hasSingletonType()) inspector->singletonScopeChain = frame->scopeChain(); JSScript *script = frame->script(); - if (script->function()) { + if (script->functionNonDelazifying()) { if (!inspector->argTypes.reserve(frame->numFormalArgs())) return nullptr; for (size_t i = 0; i < frame->numFormalArgs(); i++) { if (script->formalIsAliased(i)) inspector->argTypes.infallibleAppend(types::Type::UndefinedType()); else if (!script->argsObjAliasesFormals()) inspector->argTypes.infallibleAppend(types::GetValueType(frame->unaliasedFormal(i))); else if (frame->hasArgsObj()) @@ -682,17 +682,17 @@ IonBuilder::build() // It's safe to start emitting actual IR, so now build the scope chain. if (!initScopeChain()) return false; if (info().needsArgsObj() && !initArgumentsObject()) return false; // Prevent |this| from being DCE'd: necessary for constructors. - if (info().fun()) + if (info().funMaybeLazy()) current->getSlot(info().thisSlot())->setGuard(); // The type analysis phase attempts to insert unbox operations near // definitions of values. It also attempts to replace uses in resume points // with the narrower, unboxed variants. However, we must prevent this // replacement from happening on values in the entry snapshot. Otherwise we // could get this: // @@ -914,29 +914,29 @@ IonBuilder::rewriteParameter(uint32_t sl // Apply Type Inference information to parameters early on, unboxing them if // they have a definitive type. The actual guards will be emitted by the code // generator, explicitly, as part of the function prologue. void IonBuilder::rewriteParameters() { JS_ASSERT(info().scopeChainSlot() == 0); - if (!info().fun()) + if (!info().funMaybeLazy()) return; for (uint32_t i = info().startArgSlot(); i < info().endArgSlot(); i++) { MDefinition *param = current->getSlot(i); rewriteParameter(i, param, param->toParameter()->index()); } } bool IonBuilder::initParameters() { - if (!info().fun()) + if (!info().funMaybeLazy()) return true; // If we are doing OSR on a frame which initially executed in the // interpreter and didn't accumulate type information, try to use that OSR // frame to determine possible initial types for 'this' and parameters. // For unknownProperties() tests under addType. lock(); @@ -985,17 +985,17 @@ IonBuilder::initScopeChain(MDefinition * // will try to access the scope. For other scripts, the scope instructions // will be held live by resume points and code will still be generated for // them, so just use a constant undefined value. if (!script()->compileAndGo()) return abort("non-CNG global scripts are not supported"); lock(); - if (JSFunction *fun = info().fun()) { + if (JSFunction *fun = info().funMaybeLazy()) { if (!callee) { MCallee *calleeIns = MCallee::New(alloc()); current->add(calleeIns); callee = calleeIns; } scope = MFunctionEnvironment::New(alloc(), callee); current->add(scope); @@ -5293,17 +5293,17 @@ IonBuilder::jsop_eval(uint32_t argc) JSFunction *singleton = getSingleCallTarget(calleeTypes); if (!singleton) return abort("No singleton callee for eval()"); if (script()->global().valueIsEval(ObjectValue(*singleton))) { if (argc != 1) return abort("Direct eval with more than one argument"); - if (!info().fun()) + if (!info().funMaybeLazy()) return abort("Direct eval in global code"); // The 'this' value for the outer and eval scripts must be the // same. This is not guaranteed if a primitive string/number/etc. // is passed through to the eval invoke as the primitive may be // boxed into different objects if accessed via 'this'. JSValueType type = thisTypes->getKnownTypeTag(); if (type != JSVAL_TYPE_OBJECT && type != JSVAL_TYPE_NULL && type != JSVAL_TYPE_UNDEFINED) @@ -5715,17 +5715,17 @@ IonBuilder::newOsrPreheader(MBasicBlock if (needsArgsObj) argsObj = MOsrArgumentsObject::New(alloc(), entry); else argsObj = MConstant::New(alloc(), UndefinedValue()); osrBlock->add(argsObj); osrBlock->initSlot(info().argsObjSlot(), argsObj); } - if (info().fun()) { + if (info().funMaybeLazy()) { // Initialize |this| parameter. MParameter *thisv = MParameter::New(alloc(), MParameter::THIS_SLOT, nullptr); osrBlock->add(thisv); osrBlock->initSlot(info().thisSlot(), thisv); // Initialize arguments. for (uint32_t i = 0; i < info().nargs(); i++) { uint32_t slot = needsArgsObj ? info().argSlotUnchecked(i) : info().argSlot(i); @@ -5820,17 +5820,17 @@ IonBuilder::newOsrPreheader(MBasicBlock // Finish the osrBlock. osrBlock->end(MGoto::New(alloc(), preheader)); preheader->addPredecessor(alloc(), osrBlock); graph().setOsrBlock(osrBlock); // Wrap |this| with a guaranteed use, to prevent instruction elimination. // Prevent |this| from being DCE'd: necessary for constructors. - if (info().fun()) + if (info().funMaybeLazy()) preheader->getSlot(info().thisSlot())->setGuard(); return preheader; } MBasicBlock * IonBuilder::newPendingLoopHeader(MBasicBlock *predecessor, jsbytecode *pc, bool osr) { @@ -5860,17 +5860,17 @@ IonBuilder::newPendingLoopHeader(MBasicB continue; MPhi *phi = block->getSlot(i)->toPhi(); // Get the type from the baseline frame. types::Type existingType = types::Type::UndefinedType(); uint32_t arg = i - info().firstArgSlot(); uint32_t var = i - info().firstLocalSlot(); - if (info().fun() && i == info().thisSlot()) + if (info().funMaybeLazy() && i == info().thisSlot()) existingType = baselineFrame_->thisType; else if (arg < info().nargs()) existingType = baselineFrame_->argTypes[arg]; else existingType = baselineFrame_->varTypes[var]; // Extract typeset from value. types::TemporaryTypeSet *typeSet = @@ -9235,20 +9235,20 @@ IonBuilder::jsop_deffun(uint32_t index) current->add(deffun); return resumeAfter(deffun); } bool IonBuilder::jsop_this() { - if (!info().fun()) + if (!info().funMaybeLazy()) return abort("JSOP_THIS outside of a JSFunction."); - if (script()->strict() || info().fun()->isSelfHostedBuiltin()) { + if (script()->strict() || info().funMaybeLazy()->isSelfHostedBuiltin()) { // No need to wrap primitive |this| in strict mode or self-hosted code. current->pushSlot(info().thisSlot()); return true; } if (thisTypes->getKnownTypeTag() == JSVAL_TYPE_OBJECT || (thisTypes->empty() && baselineFrame_ && baselineFrame_->thisType.isSomeObject())) { @@ -9394,33 +9394,34 @@ IonBuilder::walkScopeChain(unsigned hops bool IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall) { JSScript *outerScript = ScopeCoordinateFunctionScript(script(), pc); if (!outerScript || !outerScript->treatAsRunOnce()) return false; - types::TypeObjectKey *funType = types::TypeObjectKey::get(outerScript->function()); + types::TypeObjectKey *funType = + types::TypeObjectKey::get(outerScript->functionNonDelazifying()); if (funType->hasFlags(constraints(), types::OBJECT_FLAG_RUNONCE_INVALIDATED)) return false; // The script this aliased var operation is accessing will run only once, // so there will be only one call object and the aliased var access can be // compiled in the same manner as a global access. We still need to find // the call object though. // Look for the call object on the current script's function's scope chain. // If the current script is inner to the outer script and the function has // singleton type then it should show up here. MDefinition *scope = current->getSlot(info().scopeChainSlot()); scope->setImplicitlyUsedUnchecked(); - JSObject *environment = script()->function()->environment(); + JSObject *environment = script()->functionNonDelazifying()->environment(); while (environment && !environment->is<GlobalObject>()) { if (environment->is<CallObject>() && !environment->as<CallObject>().isForEval() && environment->as<CallObject>().callee().nonLazyScript() == outerScript) { JS_ASSERT(environment->hasSingletonType()); *pcall = environment; return true;
--- a/js/src/jit/IonFrames.cpp +++ b/js/src/jit/IonFrames.cpp @@ -614,17 +614,17 @@ HandleException(ResumeFromException *rfe bool popSPSFrame = cx->runtime()->spsProfiler.enabled(); if (invalidated) popSPSFrame = ionScript->hasSPSInstrumentation(); // When profiling, each frame popped needs a notification that // the function has exited, so invoke the probe that a function // is exiting. JSScript *script = frames.script(); - probes::ExitScript(cx, script, script->function(), popSPSFrame); + probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); if (!frames.more()) break; ++frames; } if (invalidated) ionScript->decref(cx->runtime()->defaultFreeOp()); @@ -633,17 +633,17 @@ HandleException(ResumeFromException *rfe bool calledDebugEpilogue = false; HandleExceptionBaseline(cx, iter, rfe, &calledDebugEpilogue); if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME) return; // Unwind profiler pseudo-stack JSScript *script = iter.script(); - probes::ExitScript(cx, script, script->function(), + probes::ExitScript(cx, script, script->functionNonDelazifying(), iter.baselineFrame()->hasPushedSPSFrame()); // After this point, any pushed SPS frame would have been popped if it needed // to be. Unset the flag here so that if we call DebugEpilogue below, // it doesn't try to pop the SPS frame again. iter.baselineFrame()->unsetPushedSPSFrame(); if (cx->compartment()->debugMode() && !calledDebugEpilogue) { // If DebugEpilogue returns |true|, we have to perform a forced
--- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -511,23 +511,23 @@ JSObject * NewStringObject(JSContext *cx, HandleString str) { return StringObject::create(cx, str); } bool SPSEnter(JSContext *cx, HandleScript script) { - return cx->runtime()->spsProfiler.enter(cx, script, script->function()); + return cx->runtime()->spsProfiler.enter(cx, script, script->functionNonDelazifying()); } bool SPSExit(JSContext *cx, HandleScript script) { - cx->runtime()->spsProfiler.exit(cx, script, script->function()); + cx->runtime()->spsProfiler.exit(cx, script, script->functionNonDelazifying()); return true; } bool OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, bool *out) { RootedId id(cx); if (!ValueToId<CanGC>(cx, key, &id))
--- a/js/src/jit/shared/BaselineCompiler-shared.h +++ b/js/src/jit/shared/BaselineCompiler-shared.h @@ -91,17 +91,19 @@ class BaselineCompilerShared JS_ASSERT(!icEntries_.empty()); ICLoadLabel loadLabel; loadLabel.label = label; loadLabel.icEntry = icEntries_.length() - 1; return icLoadLabels_.append(loadLabel); } JSFunction *function() const { - return script->function(); + // Not delazifying here is ok as the function is guaranteed to have + // been delazified before compilation started. + return script->functionNonDelazifying(); } PCMappingSlotInfo getStackTopSlotInfo() { JS_ASSERT(frame.numUnsyncedSlots() <= 2); switch (frame.numUnsyncedSlots()) { case 0: return PCMappingSlotInfo::MakeSlotInfo(); case 1:
--- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -256,17 +256,17 @@ CodeGeneratorShared::encode(LSnapshot *s uint32_t startIndex = 0; for (MResumePoint **it = mirOperandIter.begin(), **end = mirOperandIter.end(); it != end; ++it) { MResumePoint *mir = *it; MBasicBlock *block = mir->block(); - JSFunction *fun = block->info().fun(); + JSFunction *fun = block->info().funMaybeLazy(); JSScript *script = block->info().script(); jsbytecode *pc = mir->pc(); uint32_t exprStack = mir->stackDepth() - block->info().ninvoke(); snapshots_.startFrame(fun, script, pc, exprStack); // Ensure that all snapshot which are encoded can safely be used for // bailouts. DebugOnly<jsbytecode *> bailPC = pc;
--- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -220,17 +220,18 @@ FollowBranch(JSContext *cx, JSScript *sc /* Common representation of slots throughout analyses and the compiler. */ static inline uint32_t ThisSlot() { return 0; } static inline uint32_t ArgSlot(uint32_t arg) { return 1 + arg; } static inline uint32_t LocalSlot(JSScript *script, uint32_t local) { - return 1 + (script->function() ? script->function()->nargs() : 0) + local; + return 1 + local + + (script->functionNonDelazifying() ? script->functionNonDelazifying()->nargs() : 0); } static inline uint32_t TotalSlots(JSScript *script) { return LocalSlot(script, 0) + script->nfixed(); } static inline uint32_t StackSlot(JSScript *script, uint32_t index) { return TotalSlots(script) + index; }
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4686,17 +4686,18 @@ JS_CompileFunction(JSContext *cx, JS::Ha JS_PUBLIC_API(JSString *) JS_DecompileScript(JSContext *cx, JSScript *scriptArg, const char *name, unsigned indent) { JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); RootedScript script(cx, scriptArg); - RootedFunction fun(cx, script->function()); + script->ensureNonLazyCanonicalFunction(cx); + RootedFunction fun(cx, script->functionNonDelazifying()); if (fun) return JS_DecompileFunction(cx, fun, indent); bool haveSource = script->scriptSource()->hasSourceData(); if (!haveSource && !JSScript::loadSource(cx, script->scriptSource(), &haveSource)) return nullptr; return haveSource ? script->sourceData(cx) : js_NewStringCopyZ<CanGC>(cx, "[no source]"); }
--- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -719,17 +719,17 @@ CreateLazyScriptsForCompartment(JSContex AutoObjectVector lazyFunctions(cx); // Find all live lazy scripts in the compartment, and via them all root // lazy functions in the compartment: those which have not been compiled // and which have a source object, indicating that their parent has been // compiled. for (gc::CellIter i(cx->zone(), gc::FINALIZE_LAZY_SCRIPT); !i.done(); i.next()) { LazyScript *lazy = i.get<LazyScript>(); - JSFunction *fun = lazy->function(); + JSFunction *fun = lazy->functionNonDelazifying(); if (fun->compartment() == cx->compartment() && lazy->sourceObject() && !lazy->maybeScript()) { MOZ_ASSERT(fun->isInterpretedLazy()); MOZ_ASSERT(lazy == fun->lazyScriptOrNull()); if (!lazyFunctions.append(fun)) return false; }
--- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1126,18 +1126,20 @@ JSFunction::createScriptForLazilyInterpr RootedScript script(cx, lazy->maybeScript()); if (script) { AutoLockForCompilation lock(cx); fun->setUnlazifiedScript(script); return true; } - if (fun != lazy->function()) { - script = lazy->function()->getOrCreateScript(cx); + if (fun != lazy->functionNonDelazifying()) { + if (!lazy->functionDelazifying(cx)) + return false; + script = lazy->functionNonDelazifying()->nonLazyScript(); if (!script) return false; AutoLockForCompilation lock(cx); fun->setUnlazifiedScript(script); return true; } @@ -1157,17 +1159,17 @@ JSFunction::createScriptForLazilyInterpr if (script) { RootedObject enclosingScope(cx, lazy->enclosingScope()); RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, fun, script)); if (!clonedScript) return false; clonedScript->setSourceObject(lazy->sourceObject()); - fun->initAtom(script->function()->displayAtom()); + fun->initAtom(script->functionNonDelazifying()->displayAtom()); clonedScript->setFunction(fun); { AutoLockForCompilation lock(cx); fun->setUnlazifiedScript(clonedScript); } CallNewScriptHook(cx, clonedScript, fun);
--- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -701,17 +701,17 @@ TypeScript::FreezeTypeSets(CompilerConst PodZero(types, count); for (size_t i = 0; i < count; i++) { if (!existing[i].clone(alloc, &types[i])) return false; } *pThisTypes = types + (ThisTypes(script) - existing); - *pArgTypes = (script->function() && script->function()->nargs()) + *pArgTypes = (script->functionNonDelazifying() && script->functionNonDelazifying()->nargs()) ? (types + (ArgTypes(script, 0) - existing)) : nullptr; *pBytecodeTypes = types; constraints->freezeScript(script, *pThisTypes, *pArgTypes, *pBytecodeTypes); return true; } @@ -996,17 +996,19 @@ types::FinishCompilation(JSContext *cx, } for (size_t i = 0; i < constraints->numFrozenScripts(); i++) { const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i); JS_ASSERT(entry.script->types); if (!CheckFrozenTypeSet(cx, entry.thisTypes, types::TypeScript::ThisTypes(entry.script))) succeeded = false; - unsigned nargs = entry.script->function() ? entry.script->function()->nargs() : 0; + unsigned nargs = entry.script->functionNonDelazifying() + ? entry.script->functionNonDelazifying()->nargs() + : 0; for (size_t i = 0; i < nargs; i++) { if (!CheckFrozenTypeSet(cx, &entry.argTypes[i], types::TypeScript::ArgTypes(entry.script, i))) succeeded = false; } for (size_t i = 0; i < entry.script->nTypeSets(); i++) { if (!CheckFrozenTypeSet(cx, &entry.bytecodeTypes[i], &entry.script->types->typeArray()[i])) succeeded = false; } @@ -1060,33 +1062,37 @@ types::FinishDefinitePropertiesAnalysis( // than once. See also CheckDefinitePropertiesTypeSet. for (size_t i = 0; i < constraints->numFrozenScripts(); i++) { const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i); JSScript *script = entry.script; JS_ASSERT(script->types); JS_ASSERT(TypeScript::ThisTypes(script)->isSubset(entry.thisTypes)); - unsigned nargs = script->function() ? script->function()->nargs() : 0; + unsigned nargs = entry.script->functionNonDelazifying() + ? entry.script->functionNonDelazifying()->nargs() + : 0; for (size_t j = 0; j < nargs; j++) JS_ASSERT(TypeScript::ArgTypes(script, j)->isSubset(&entry.argTypes[j])); for (size_t j = 0; j < script->nTypeSets(); j++) JS_ASSERT(script->types->typeArray()[j].isSubset(&entry.bytecodeTypes[j])); } #endif for (size_t i = 0; i < constraints->numFrozenScripts(); i++) { const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i); JSScript *script = entry.script; JS_ASSERT(script->types); CheckDefinitePropertiesTypeSet(cx, entry.thisTypes, TypeScript::ThisTypes(script)); - unsigned nargs = script->function() ? script->function()->nargs() : 0; + unsigned nargs = script->functionNonDelazifying() + ? script->functionNonDelazifying()->nargs() + : 0; for (size_t j = 0; j < nargs; j++) CheckDefinitePropertiesTypeSet(cx, &entry.argTypes[j], TypeScript::ArgTypes(script, j)); for (size_t j = 0; j < script->nTypeSets(); j++) CheckDefinitePropertiesTypeSet(cx, &entry.bytecodeTypes[j], &script->types->typeArray()[j]); } } @@ -2058,17 +2064,17 @@ NewObjectKind types::UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key) { /* * Objects created outside loops in global and eval scripts should have * singleton types. For now this is only done for plain objects and typed * arrays, but not normal arrays. */ - if (script->function() && !script->treatAsRunOnce()) + if (script->functionNonDelazifying() && !script->treatAsRunOnce()) return GenericObject; if (key != JSProto_Object && !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray)) return GenericObject; /* * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note * indicating their boundary. @@ -2274,18 +2280,18 @@ TypeZone::addPendingRecompile(JSContext if (script->hasParallelIonScript()) addPendingRecompile(cx, script->parallelIonScript()->recompileInfo()); #endif // When one script is inlined into another the caller listens to state // changes on the callee's script, so trigger these to force recompilation // of any such callers. - if (script->function() && !script->function()->hasLazyType()) - ObjectStateChange(cx, script->function()->type(), false); + if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyType()) + ObjectStateChange(cx, script->functionNonDelazifying()->type(), false); } void TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target) { JS_ASSERT(this == &cx->compartment()->types); JS_ASSERT(!(target->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN)); JS_ASSERT(!target->singleton()); @@ -3397,17 +3403,17 @@ types::AddClearDefiniteFunctionUsesInScr JSScript *script, JSScript *calleeScript) { // Look for any uses of the specified calleeScript in type sets for // |script|, and add constraints to ensure that if the type sets' contents // change then the definite properties are cleared from the type. // This ensures that the inlining performed when the definite properties // analysis was done is stable. - TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->function()).objectKey(); + TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->functionNonDelazifying()).objectKey(); unsigned count = TypeScript::NumTypeSets(script); StackTypeSet *typeArray = script->types->typeArray(); for (unsigned i = 0; i < count; i++) { StackTypeSet *types = &typeArray[i]; if (!types->unknownObject() && types->getObjectCount() == 1) { if (calleeKey != types->getObject(0)) { @@ -3691,17 +3697,17 @@ JSScript::makeTypes(JSContext *cx) for (unsigned i = 0; i < nTypeSets(); i++) InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u", InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(), i, id()); TypeSet *thisTypes = TypeScript::ThisTypes(this); InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u", InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(), id()); - unsigned nargs = function() ? function()->nargs() : 0; + unsigned nargs = functionNonDelazifying() ? functionNonDelazifying()->nargs() : 0; for (unsigned i = 0; i < nargs; i++) { TypeSet *types = TypeScript::ArgTypes(this, i); InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u", InferSpewColor(types), types, InferSpewColorReset(), i, id()); } #endif @@ -3975,19 +3981,19 @@ ExclusiveContext::getNewType(const Class TypeObjectWithNewScriptSet &newTypeObjects = compartment()->newTypeObjects; if (!newTypeObjects.initialized() && !newTypeObjects.init()) return nullptr; // Canonicalize new functions to use the original one associated with its script. if (fun) { if (fun->hasScript()) - fun = fun->nonLazyScript()->function(); + fun = fun->nonLazyScript()->functionNonDelazifying(); else if (fun->isInterpretedLazy()) - fun = fun->lazyScript()->function(); + fun = fun->lazyScript()->functionNonDelazifying(); else fun = nullptr; } TypeObjectWithNewScriptSet::AddPtr p = newTypeObjects.lookupForAdd(TypeObjectWithNewScriptSet::Lookup(clasp, proto, fun)); if (p) { TypeObject *type = p->object; @@ -4565,35 +4571,38 @@ TypeScript::printTypes(JSContext *cx, Ha { JS_ASSERT(script->types == this); if (!script->hasBaselineScript()) return; AutoEnterAnalysis enter(nullptr, script->compartment()); - if (script->function()) + if (script->functionNonDelazifying()) fprintf(stderr, "Function"); else if (script->isForEval()) fprintf(stderr, "Eval"); else fprintf(stderr, "Main"); fprintf(stderr, " #%u %s:%d ", script->id(), script->filename(), (int) script->lineno()); - if (script->function()) { - if (js::PropertyName *name = script->function()->name()) { + if (script->functionNonDelazifying()) { + if (js::PropertyName *name = script->functionNonDelazifying()->name()) { const jschar *chars = name->getChars(nullptr); JSString::dumpChars(chars, name->length()); } } fprintf(stderr, "\n this:"); TypeScript::ThisTypes(script)->print(); - for (unsigned i = 0; script->function() && i < script->function()->nargs(); i++) { + for (unsigned i = 0; + script->functionNonDelazifying() && i < script->functionNonDelazifying()->nargs(); + i++) + { fprintf(stderr, "\n arg%u:", i); TypeScript::ArgTypes(script, i)->print(); } fprintf(stderr, "\n"); for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) { PrintBytecode(cx, script, pc);
--- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -572,17 +572,17 @@ TypeScript::ThisTypes(JSScript *script) * Note: for non-escaping arguments and locals, argTypes/localTypes reflect * only the initial type of the variable (e.g. passed values for argTypes, * or undefined for localTypes) and not types from subsequent assignments. */ /* static */ inline StackTypeSet * TypeScript::ArgTypes(JSScript *script, unsigned i) { - JS_ASSERT(i < script->function()->nargs()); + JS_ASSERT(i < script->functionNonDelazifying()->nargs()); JS_ASSERT(CurrentThreadCanReadCompilationData()); return script->types->typeArray() + script->nTypeSets() + analyze::ArgSlot(i); } template <typename TYPESET> /* static */ inline TYPESET * TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *hint, TYPESET *typeArray) {
--- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -405,17 +405,18 @@ class BytecodeParser } void reportOOM() { allocScope_.releaseEarly(); js_ReportOutOfMemory(cx_); } uint32_t numSlots() { - return 1 + (script_->function() ? script_->function()->nargs() : 0) + script_->nfixed(); + return 1 + script_->nfixed() + + (script_->functionNonDelazifying() ? script_->functionNonDelazifying()->nargs() : 0); } uint32_t maximumStackDepth() { return script_->nslots() - script_->nfixed(); } Bytecode& getCode(uint32_t offset) { JS_ASSERT(offset < script_->length()); @@ -2033,18 +2034,18 @@ js::GetPCCountScriptSummary(JSContext *c JSString *str = JS_NewStringCopyZ(cx, script->filename()); if (!str || !(str = StringToSource(cx, str))) return nullptr; buf.append(str); AppendJSONProperty(buf, "line"); NumberValueToStringBuffer(cx, Int32Value(script->lineno()), buf); - if (script->function()) { - JSAtom *atom = script->function()->displayAtom(); + if (script->functionNonDelazifying()) { + JSAtom *atom = script->functionNonDelazifying()->displayAtom(); if (atom) { AppendJSONProperty(buf, "name"); if (!(str = StringToSource(cx, atom))) return nullptr; buf.append(str); } } @@ -2166,17 +2167,17 @@ GetPCCountJSON(JSContext *cx, const Scri const char *name = js_CodeName[op]; AppendJSONProperty(buf, "name"); buf.append('\"'); buf.appendInflated(name, strlen(name)); buf.append('\"'); } { - ExpressionDecompiler ed(cx, script, script->function()); + ExpressionDecompiler ed(cx, script, script->functionDelazifying()); if (!ed.init()) return false; if (!ed.decompilePC(pc)) return false; char *text; if (!ed.getOutput(&text)) return false; AppendJSONProperty(buf, "text"); @@ -2277,17 +2278,17 @@ js::GetPCCountScriptContents(JSContext * return nullptr; } const ScriptAndCounts &sac = (*rt->scriptAndCountsVector)[index]; JSScript *script = sac.script; StringBuffer buf(cx); - if (!script->function() && !script->compileAndGo()) + if (!script->functionNonDelazifying() && !script->compileAndGo()) return buf.finishString(); { AutoCompartment ac(cx, &script->global()); if (!GetPCCountJSON(cx, sac, buf)) return nullptr; }
--- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -2855,17 +2855,17 @@ JSScript::markChildren(JSTracer *trc) MarkValueRange(trc, constarray->length, constarray->vector, "consts"); } if (sourceObject()) { JS_ASSERT(sourceObject()->compartment() == compartment()); MarkObject(trc, &sourceObject_, "sourceObject"); } - if (function()) + if (functionNonDelazifying()) MarkObject(trc, &function_, "function"); if (enclosingScopeOrOriginalFunction_) MarkObject(trc, &enclosingScopeOrOriginalFunction_, "enclosing"); if (IS_GC_MARKING_TRACER(trc)) { compartment()->mark(); @@ -3019,17 +3019,17 @@ js::SetFrameArgumentsObject(JSContext *c if (frame.unaliasedLocal(var).isMagic(JS_OPTIMIZED_ARGUMENTS)) frame.unaliasedLocal(var) = ObjectValue(*argsobj); } } /* static */ bool JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script) { - JS_ASSERT(script->function()); + JS_ASSERT(script->functionNonDelazifying()); JS_ASSERT(script->analyzedArgsUsage()); JS_ASSERT(script->argumentsHasVarBinding()); /* * It is possible that the arguments optimization has already failed, * everything has been fixed up, but there was an outstanding magic value * on the stack that has just now flowed into an apply. In this case, there * is nothing to do; GuardFunApplySpeculation will patch in the real
--- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1181,22 +1181,33 @@ class JSScript : public js::gc::Barriere } static size_t offsetOfBaselineOrIonSkipArgCheck() { return offsetof(JSScript, baselineOrIonSkipArgCheck); } /* * Original compiled function for the script, if it has a function. * nullptr for global and eval scripts. + * The delazifying variant ensures that the function isn't lazy, but can + * only be used under a compilation lock. The non-delazifying variant + * can be used off-thread and without the lock, but must only be used + * after earlier code has called ensureNonLazyCanonicalFunction and + * while the function can't have been relazified. */ - JSFunction *function() const { + inline JSFunction *functionDelazifying() const; + JSFunction *functionNonDelazifying() const { js::AutoThreadSafeAccess ts(this); return function_; } inline void setFunction(JSFunction *fun); + /* + * Takes a compilation lock and de-lazifies the canonical function. Must + * be called before entering code that expects the function to be non-lazy. + */ + inline void ensureNonLazyCanonicalFunction(JSContext *cx); JSFunction *originalFunction() const; void setIsCallsiteClone(JSObject *fun); JSFlatString *sourceData(JSContext *cx); static bool loadSource(JSContext *cx, js::ScriptSource *ss, bool *worked); @@ -1610,17 +1621,18 @@ class LazyScript : public gc::BarrieredC uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column); public: static LazyScript *Create(ExclusiveContext *cx, HandleFunction fun, uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column); - JSFunction *function() const { + inline JSFunction *functionDelazifying(JSContext *cx) const; + JSFunction *functionNonDelazifying() const { return function_; } void initScript(JSScript *script); JSScript *maybeScript() { return script_; }
--- a/js/src/jsscriptinlines.h +++ b/js/src/jsscriptinlines.h @@ -40,25 +40,53 @@ ScriptCounts::destroy(FreeOp *fop) fop->free_(pcCountsVector); fop->delete_(ionCounts); } void SetFrameArgumentsObject(JSContext *cx, AbstractFramePtr frame, HandleScript script, JSObject *argsobj); +inline JSFunction * +LazyScript::functionDelazifying(JSContext *cx) const +{ + if (function_ && !function_->getOrCreateScript(cx)) + return nullptr; + return function_; +} + } // namespace js +inline JSFunction * +JSScript::functionDelazifying() const +{ + js::AutoThreadSafeAccess ts(this); + JS_ASSERT(js::CurrentThreadCanWriteCompilationData()); + if (function_ && function_->isInterpretedLazy()) + function_->setUnlazifiedScript(const_cast<JSScript *>(this)); + return function_; +} + inline void JSScript::setFunction(JSFunction *fun) { JS_ASSERT(fun->isTenured()); function_ = fun; } +inline void +JSScript::ensureNonLazyCanonicalFunction(JSContext *cx) +{ + // Infallibly delazify the canonical script. + if (function_ && function_->isInterpretedLazy()) { + js::AutoLockForCompilation lock(cx); + functionDelazifying(); + } +} + inline JSFunction * JSScript::getFunction(size_t index) { JSFunction *fun = &getObject(index)->as<JSFunction>(); #ifdef DEBUG JS_ASSERT_IF(fun->isNative(), IsAsmJSModuleNative(fun->native())); #endif return fun; @@ -69,18 +97,18 @@ JSScript::getCallerFunction() { JS_ASSERT(savedCallerFun()); return getFunction(0); } inline JSFunction * JSScript::functionOrCallerFunction() { - if (function()) - return function(); + if (functionNonDelazifying()) + return functionNonDelazifying(); if (savedCallerFun()) return getCallerFunction(); return nullptr; } inline js::RegExpObject * JSScript::getRegExp(size_t index) {
--- a/js/src/jsworkers.cpp +++ b/js/src/jsworkers.cpp @@ -585,17 +585,17 @@ CallNewScriptHookForAllScripts(JSContext RootedScript nested(cx, fun->nonLazyScript()); CallNewScriptHookForAllScripts(cx, nested); } } } } // The global new script hook is called on every script that was compiled. - RootedFunction function(cx, script->function()); + RootedFunction function(cx, script->functionNonDelazifying()); CallNewScriptHook(cx, script, function); } JSScript * WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token) { ParseTask *parseTask = nullptr;
--- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -336,17 +336,17 @@ ArgSetter(JSContext *cx, HandleObject ob NormalArgumentsObject &argsobj = obj->as<NormalArgumentsObject>(); RootedScript script(cx, argsobj.containingScript()); if (JSID_IS_INT(id)) { unsigned arg = unsigned(JSID_TO_INT(id)); if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) { argsobj.setElement(cx, arg, vp); - if (arg < script->function()->nargs()) + if (arg < script->functionNonDelazifying()->nargs()) types::TypeScript::SetArgument(cx, script, arg, vp); return true; } } else { JS_ASSERT(JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().callee)); } /*
--- a/js/src/vm/ForkJoin.cpp +++ b/js/src/vm/ForkJoin.cpp @@ -708,17 +708,18 @@ js::ForkJoinOperation::compileForParalle // warmup iterations. while (true) { bool offMainThreadCompilationsInProgress = false; bool gatheringTypeInformation = false; // Walk over the worklist to check on the status of each entry. for (uint32_t i = 0; i < worklist_.length(); i++) { script = worklist_[i]; - fun = script->function(); + script->ensureNonLazyCanonicalFunction(cx_); + fun = script->functionNonDelazifying(); // No baseline script means no type information, hence we // will not be able to compile very well. In such cases, // we continue to run baseline iterations until either (1) // the potential callee *has* a baseline script or (2) the // potential callee's use count stops increasing, // indicating that they are not in fact a callee. if (!script->hasBaselineScript()) {
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -385,16 +385,18 @@ ExecuteState::pushInterpreterFrame(JSCon bool js::RunScript(JSContext *cx, RunState &state) { JS_CHECK_RECURSION(cx, return false); SPSEntryMarker marker(cx->runtime()); + state.script()->ensureNonLazyCanonicalFunction(cx); + #ifdef JS_ION if (jit::IsIonEnabled(cx)) { jit::MethodStatus status = jit::CanEnter(cx, state); if (status == jit::Method_Error) return false; if (status == jit::Method_Compiled) { jit::IonExecStatus status = jit::IonCannon(cx, state); return !IsErrorStatus(status); @@ -1474,29 +1476,29 @@ Interpret(JSContext *cx, RunState &state JS_ASSERT(script->containsPC(REGS.pc)); JS_ASSERT(REGS.stackDepth() <= script->nslots()); /* * To support generator_throw and to catch ignored exceptions, * fail if cx->isExceptionPending() is true. */ if (cx->isExceptionPending()) { - probes::EnterScript(cx, script, script->function(), REGS.fp()); + probes::EnterScript(cx, script, script->functionNonDelazifying(), REGS.fp()); goto error; } } /* State communicated between non-local jumps: */ bool interpReturnOK; if (!activation.entryFrame()->isGeneratorFrame()) { if (!activation.entryFrame()->prologue(cx)) goto error; } else { - probes::EnterScript(cx, script, script->function(), activation.entryFrame()); + probes::EnterScript(cx, script, script->functionNonDelazifying(), activation.entryFrame()); } if (JS_UNLIKELY(cx->compartment()->debugMode())) { JSTrapStatus status = ScriptDebugPrologue(cx, activation.entryFrame(), REGS.pc); switch (status) { case JSTRAP_CONTINUE: break; case JSTRAP_RETURN: ForcedReturn(cx, REGS); @@ -1797,17 +1799,18 @@ CASE(JSOP_RETRVAL) #endif if (JS_UNLIKELY(cx->compartment()->debugMode())) interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK); if (!REGS.fp()->isYielding()) REGS.fp()->epilogue(cx); else - probes::ExitScript(cx, script, script->function(), REGS.fp()->hasPushedSPSFrame()); + probes::ExitScript(cx, script, script->functionNonDelazifying(), + REGS.fp()->hasPushedSPSFrame()); #if defined(JS_ION) jit_return_pop_frame: #endif activation.popInlineFrame(REGS.fp()); SET_SCRIPT(REGS.fp()->script()); @@ -3447,17 +3450,18 @@ DEFAULT() MOZ_ASSUME_UNREACHABLE("Invalid HandleError continuation"); exit: if (JS_UNLIKELY(cx->compartment()->debugMode())) interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK); if (!REGS.fp()->isYielding()) REGS.fp()->epilogue(cx); else - probes::ExitScript(cx, script, script->function(), REGS.fp()->hasPushedSPSFrame()); + probes::ExitScript(cx, script, script->functionNonDelazifying(), + REGS.fp()->hasPushedSPSFrame()); gc::MaybeVerifyBarriers(cx, true); #if JS_TRACE_LOGGING TraceLogging::defaultLogger()->log(TraceLogging::SCRIPT_STOP); #endif #ifdef JS_ION @@ -3603,17 +3607,17 @@ js::DefFunOperation(JSContext *cx, Handl */ RootedFunction fun(cx, funArg); if (fun->isNative() || fun->environment() != scopeChain) { fun = CloneFunctionObjectIfNotSingleton(cx, fun, scopeChain, TenuredObject); if (!fun) return false; } else { JS_ASSERT(script->compileAndGo()); - JS_ASSERT(!script->function()); + JS_ASSERT(!script->functionNonDelazifying()); } /* * We define the function as a property of the variable object and not the * current scope chain even for the case of function expression statements * and functions defined by eval inside let or with blocks. */ RootedObject parent(cx, scopeChain); @@ -3870,20 +3874,21 @@ js::RunOnceScriptPrologue(JSContext *cx, if (!script->hasRunOnce()) { script->setHasRunOnce(); return true; } // Force instantiation of the script's function's type to ensure the flag // is preserved in type information. - if (!script->function()->getType(cx)) + if (!script->functionNonDelazifying()->getType(cx)) return false; - types::MarkTypeObjectFlags(cx, script->function(), types::OBJECT_FLAG_RUNONCE_INVALIDATED); + types::MarkTypeObjectFlags(cx, script->functionNonDelazifying(), + types::OBJECT_FLAG_RUNONCE_INVALIDATED); return true; } bool js::InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleId id, HandleObject val) { JS_ASSERT(val->isCallable());
--- a/js/src/vm/OldDebugAPI.cpp +++ b/js/src/vm/OldDebugAPI.cpp @@ -539,17 +539,18 @@ JS_GetScriptOriginPrincipals(JSScript *s return script->originPrincipals(); } /************************************************************************/ JS_PUBLIC_API(JSFunction *) JS_GetScriptFunction(JSContext *cx, JSScript *script) { - return script->function(); + script->ensureNonLazyCanonicalFunction(cx); + return script->functionNonDelazifying(); } JS_PUBLIC_API(JSObject *) JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj) { return obj->enclosingScope(); }
--- a/js/src/vm/SPSProfiler.h +++ b/js/src/vm/SPSProfiler.h @@ -319,17 +319,17 @@ class SPSInstrumentation /* * Flags entry into a JS function for the first time. Before this is called, * no instrumentation is emitted, but after this instrumentation is emitted. */ bool push(JSContext *cx, JSScript *script, Assembler &masm, Register scratch) { if (!enabled()) return true; const char *string = profiler_->profileString(cx, script, - script->function()); + script->functionNonDelazifying()); if (string == nullptr) return false; masm.spsPushFrame(profiler_, string, script, scratch); setPushed(script); return true; } /*
--- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -36,17 +36,17 @@ static JSObject * InnermostStaticScope(JSScript *script, jsbytecode *pc) { JS_ASSERT(script->containsPC(pc)); JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD); StaticBlockObject *block = script->getBlockScope(pc); if (block) return block; - return script->function(); + return script->functionNonDelazifying(); } Shape * js::ScopeCoordinateToStaticScopeShape(JSScript *script, jsbytecode *pc) { StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc)); ScopeCoordinate sc(pc); while (true) { @@ -2295,24 +2295,24 @@ AnalyzeEntrainedVariablesInScript(JSCont if (!remainingNames.empty()) { Sprinter buf(cx); if (!buf.init()) return false; buf.printf("Script "); - if (JSAtom *name = script->function()->displayAtom()) { + if (JSAtom *name = script->functionNonDelazifying()->displayAtom()) { buf.putString(name); buf.printf(" "); } buf.printf("(%s:%d) has variables entrained by ", script->filename(), script->lineno()); - if (JSAtom *name = innerScript->function()->displayAtom()) { + if (JSAtom *name = innerScript->functionNonDelazifying()->displayAtom()) { buf.putString(name); buf.printf(" "); } buf.printf("(%s:%d) ::", innerScript->filename(), innerScript->lineno()); for (PropertyNameSet::Range r = remainingNames.all(); !r.empty(); r.popFront()) { buf.printf(" "); @@ -2362,17 +2362,17 @@ js::AnalyzeEntrainedVariables(JSContext for (size_t i = 0; i < objects->length; i++) { JSObject *obj = objects->vector[i]; if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) { JSFunction *fun = &obj->as<JSFunction>(); RootedScript innerScript(cx, fun->getOrCreateScript(cx)); if (!innerScript) return false; - if (script->function() && script->function()->isHeavyweight()) { + if (script->functionDelazifying() && script->functionDelazifying()->isHeavyweight()) { if (!AnalyzeEntrainedVariablesInScript(cx, script, innerScript)) return false; } if (!AnalyzeEntrainedVariables(cx, innerScript)) return false; } }
--- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -283,16 +283,18 @@ InterpreterStack::getCallFrame(JSContext JS_ALWAYS_INLINE bool InterpreterStack::pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args, HandleScript script, InitialFrameFlags initial) { RootedFunction callee(cx, &args.callee().as<JSFunction>()); JS_ASSERT(regs.sp == args.end()); JS_ASSERT(callee->nonLazyScript() == script); + script->ensureNonLazyCanonicalFunction(cx); + StackFrame *prev = regs.fp(); jsbytecode *prevpc = regs.pc; Value *prevsp = regs.sp; JS_ASSERT(prev); LifoAlloc::Mark mark = allocator_.mark(); StackFrame::Flags flags = ToFrameFlags(initial);
--- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -259,27 +259,27 @@ StackFrame::prologue(JSContext *cx) RootedObject callee(cx, &this->callee()); JSObject *obj = CreateThisForFunction(cx, callee, useNewType() ? SingletonObject : GenericObject); if (!obj) return false; functionThis() = ObjectValue(*obj); } - probes::EnterScript(cx, script, script->function(), this); + probes::EnterScript(cx, script, script->functionNonDelazifying(), this); return true; } void StackFrame::epilogue(JSContext *cx) { JS_ASSERT(!isYielding()); RootedScript script(cx, this->script()); - probes::ExitScript(cx, script, script->function(), hasPushedSPSFrame()); + probes::ExitScript(cx, script, script->functionNonDelazifying(), hasPushedSPSFrame()); if (isEvalFrame()) { if (isStrictEvalFrame()) { JS_ASSERT_IF(hasCallObj(), scopeChain()->as<CallObject>().isForEval()); if (JS_UNLIKELY(cx->compartment()->debugMode())) DebugScopes::onPopStrictEvalScope(this); } else if (isDirectEvalFrame()) { if (isDebuggerFrame()) @@ -795,17 +795,17 @@ ScriptFrameIter::isGlobalFrame() const break; case SCRIPTED: return interpFrame()->isGlobalFrame(); case JIT: #ifdef JS_ION if (data_.ionFrames_.isBaselineJS()) return data_.ionFrames_.baselineFrame()->isGlobalFrame(); JS_ASSERT(!script()->isForEval()); - return !script()->function(); + return !script()->functionNonDelazifying(); #else break; #endif } MOZ_ASSUME_UNREACHABLE("Unexpected state"); } bool
--- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -1559,17 +1559,17 @@ class ScriptFrameIter Activation *activation() const { return data_.activations_.activation(); } jsbytecode *pc() const { JS_ASSERT(!done()); return data_.pc_; } void updatePcQuadratic(); JSFunction *callee() const; Value calleev() const; unsigned numActualArgs() const; - unsigned numFormalArgs() const { return script()->function()->nargs(); } + unsigned numFormalArgs() const { return script()->functionNonDelazifying()->nargs(); } Value unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const; JSObject *scopeChain() const; CallObject &callObj() const; bool hasArgsObj() const; ArgumentsObject &argsObj() const;