diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 4e4b4e0de1f5..51845c3ba1d3 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -87,7 +87,6 @@ class MOZ_STACK_CLASS BytecodeCompiler bool maybeSetSourceMapFromOptions(); bool emitFinalReturn(); bool initGlobalBindings(ParseContext& pc); - void markFunctionsWithinEvalScript(); bool maybeCompleteCompressSource(); AutoCompilationTraceLogger traceLogger; @@ -519,29 +518,6 @@ BytecodeCompiler::initGlobalBindings(ParseContext& pc) return true; } -void -BytecodeCompiler::markFunctionsWithinEvalScript() -{ - // Mark top level functions in an eval script as being within an eval. - - if (!script->hasObjects()) - return; - - ObjectArray* objects = script->objects(); - size_t start = script->innerObjectsStart(); - - for (size_t i = start; i < objects->length; i++) { - JSObject* obj = objects->vector[i]; - if (obj->is()) { - JSFunction* fun = &obj->as(); - if (fun->hasScript()) - fun->nonLazyScript()->setDirectlyInsideEval(); - else if (fun->isInterpretedLazy()) - fun->lazyScript()->setDirectlyInsideEval(); - } - } -} - bool BytecodeCompiler::maybeCompleteCompressSource() { @@ -621,12 +597,6 @@ BytecodeCompiler::compileScript(HandleObject scopeChain, HandleScript evalCaller return nullptr; } - // Note that this marking must happen before we tell Debugger - // about the new script, in case Debugger delazifies the script's - // inner functions. - if (options.forEval) - markFunctionsWithinEvalScript(); - emitter->tellDebuggerAboutCompiledScript(cx); if (!maybeCompleteCompressSource()) @@ -790,8 +760,6 @@ frontend::CompileLazyFunction(JSContext* cx, Handle lazy, const cha script->bindings = pn->pn_funbox->bindings; - if (lazy->directlyInsideEval()) - script->setDirectlyInsideEval(); if (lazy->usesArgumentsApplyAndThis()) script->setUsesArgumentsApplyAndThis(); if (lazy->hasBeenCloned()) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 70ed41533c30..de87a3d8a7ed 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -13,6 +13,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/DebugOnly.h" #include "mozilla/FloatingPoint.h" +#include "mozilla/Maybe.h" #include "mozilla/PodOperations.h" #include "mozilla/UniquePtr.h" @@ -48,6 +49,8 @@ using namespace js; using namespace js::gc; using namespace js::frontend; +using mozilla::Maybe; +using mozilla::Some; using mozilla::DebugOnly; using mozilla::NumberIsInt32; using mozilla::PodCopy; @@ -1502,57 +1505,65 @@ BytecodeEmitter::tryConvertFreeName(ParseNode* pn) return true; } - size_t hops = 0; + // Walk the static scope chain and look for an aliased binding with + // the name pn->pn_atom. + uint32_t hops = 0; + Maybe slot; FunctionBox* funbox = sc->asFunctionBox(); - if (funbox->hasExtensibleScope()) - return false; - if (funbox->function()->isNamedLambda() && funbox->function()->atom() == pn->pn_atom) - return false; - if (funbox->isHeavyweight()) { - hops++; - if (funbox->function()->isNamedLambda()) - hops++; - } - if (script->directlyInsideEval()) - return false; - RootedObject outerScope(cx, script->enclosingStaticScope()); - for (StaticScopeIter ssi(cx, outerScope); !ssi.done(); ssi++) { - if (ssi.type() != StaticScopeIter::Function) { - if (ssi.type() == StaticScopeIter::Block) { - // Use generic ops if a catch block is encountered. - return false; - } - if (ssi.hasSyntacticDynamicScopeObject()) - hops++; + PropertyName* name = pn->pn_atom->asPropertyName(); + for (StaticScopeIter ssi(funbox->staticScope()); !ssi.done(); ssi++) { + // Don't optimize names through eval. + if (ssi.type() == StaticScopeIter::Eval) + return false; + + if (!ssi.hasSyntacticDynamicScopeObject()) continue; - } - RootedScript script(cx, ssi.funScript()); - if (script->functionNonDelazifying()->atom() == pn->pn_atom) - return false; - if (ssi.hasSyntacticDynamicScopeObject()) { - uint32_t slot; - if (lookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot, pn)) { - JSOp op; - switch (pn->getOp()) { - case JSOP_GETNAME: op = JSOP_GETALIASEDVAR; break; - case JSOP_SETNAME: op = JSOP_SETALIASEDVAR; break; - default: return false; + + // Look up for name in function and block scopes. + if (ssi.type() == StaticScopeIter::Function) { + RootedScript funScript(cx, ssi.funScript()); + if (funScript->funHasExtensibleScope() || ssi.fun().atom() == pn->pn_atom) + return false; + + // Skip the current function, since we're trying to convert a + // free name. + if (script != funScript) { + uint32_t slot_; + if (lookupAliasedName(funScript, name, &slot_, pn)) { + slot = Some(slot_); + break; } - - pn->setOp(op); - MOZ_ALWAYS_TRUE(pn->pn_scopecoord.set(parser->tokenStream, hops, slot)); - return true; } - hops++; + } else if (ssi.type() == StaticScopeIter::Block) { + RootedShape shape(cx, ssi.block().lookupAliasedName(name)); + if (shape) { + // Don't optimize setting a 'const' binding. Let the slow + // path do the error checking. + if (!shape->writable() && pn->getOp() == JSOP_SETNAME) + return false; + slot = Some(shape->slot()); + pn->pn_dflags |= PND_LEXICAL; + break; + } + } else { + MOZ_ASSERT(ssi.type() != StaticScopeIter::With); } - // If this walk up and check for directlyInsideEval is ever removed, - // we'll need to adjust CompileLazyFunction to better communicate - // whether we're inside eval to the BytecodeEmitter. For now, this - // walk is why CompileLazyFunction can claim that it's never inside - // eval. - if (script->funHasExtensibleScope() || script->directlyInsideEval()) - return false; + hops++; + } + + // If we found a scope binding name, convert the name op to an aliased + // var op. + if (slot.isSome()) { + JSOp op; + switch (pn->getOp()) { + case JSOP_GETNAME: op = JSOP_GETALIASEDVAR; break; + case JSOP_SETNAME: op = JSOP_SETALIASEDVAR; break; + default: return false; + } + pn->setOp(op); + MOZ_ALWAYS_TRUE(pn->pn_scopecoord.set(parser->tokenStream, hops, *slot)); + return true; } } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 19d96b6a8fcf..48d38b8a8302 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -3968,7 +3968,6 @@ LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun, p.bindingsAccessedDynamically = false; p.hasDebuggerStatement = false; p.hasDirectEval = false; - p.directlyInsideEval = false; p.usesArgumentsApplyAndThis = false; p.isDerivedClassConstructor = false; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index e9497a0dc3d8..27996a9e3f99 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1098,9 +1098,6 @@ class JSScript : public js::gc::TenuredCell // Script came from eval(), and is in eval cache. bool isCachedEval_:1; - // Set for functions defined at the top level within an 'eval' script. - bool directlyInsideEval_:1; - // 'this', 'arguments' and f.apply() are used. This is likely to be a wrapper. bool usesArgumentsApplyAndThis_:1; @@ -1334,7 +1331,6 @@ class JSScript : public js::gc::TenuredCell bool isActiveEval() const { return isActiveEval_; } bool isCachedEval() const { return isCachedEval_; } - bool directlyInsideEval() const { return directlyInsideEval_; } void cacheForEval() { MOZ_ASSERT(isActiveEval() && !isCachedEval()); @@ -1353,7 +1349,6 @@ class JSScript : public js::gc::TenuredCell } void setActiveEval() { isActiveEval_ = true; } - void setDirectlyInsideEval() { directlyInsideEval_ = true; } bool usesArgumentsApplyAndThis() const { return usesArgumentsApplyAndThis_; @@ -2064,7 +2059,6 @@ class LazyScript : public gc::TenuredCell uint32_t bindingsAccessedDynamically : 1; uint32_t hasDebuggerStatement : 1; uint32_t hasDirectEval : 1; - uint32_t directlyInsideEval : 1; uint32_t usesArgumentsApplyAndThis : 1; uint32_t hasBeenCloned : 1; uint32_t treatAsRunOnce : 1; @@ -2213,13 +2207,6 @@ class LazyScript : public gc::TenuredCell p_.hasDirectEval = true; } - bool directlyInsideEval() const { - return p_.directlyInsideEval; - } - void setDirectlyInsideEval() { - p_.directlyInsideEval = true; - } - bool usesArgumentsApplyAndThis() const { return p_.usesArgumentsApplyAndThis; } diff --git a/js/src/vm/ScopeObject-inl.h b/js/src/vm/ScopeObject-inl.h index 620ca92553e7..c4b5449f0b63 100644 --- a/js/src/vm/ScopeObject-inl.h +++ b/js/src/vm/ScopeObject-inl.h @@ -70,13 +70,6 @@ CallObject::initAliasedLexicalsToThrowOnTouch(JSScript* script) initRemainingSlotsToUninitializedLexicals(script->bindings.aliasedBodyLevelLexicalBegin()); } -template -inline bool -StaticScopeIter::done() const -{ - return !obj; -} - template inline void StaticScopeIter::operator++(int) diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index f18990b80b3d..1d4520dada9c 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -687,6 +687,19 @@ StaticBlockObject::create(ExclusiveContext* cx) return NewObjectWithNullTaggedProto(cx, TenuredObject, BaseShape::DELEGATE); } +Shape* +StaticBlockObject::lookupAliasedName(PropertyName* name) +{ + Shape::Range r(lastProperty()); + while (!r.empty()) { + jsid id = r.front().propidRaw(); + if (JSID_TO_ATOM(id)->asPropertyName() == name && isAliased(shapeToIndex(r.front()))) + return &r.front(); + r.popFront(); + } + return nullptr; +} + /* static */ Shape* StaticBlockObject::addVar(ExclusiveContext* cx, Handle block, HandleId id, bool constant, unsigned index, bool* redeclared) diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 4713d599b1ad..12c83926ceab 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -114,7 +114,7 @@ class StaticScopeIter "used in NoGC code"); } - bool done() const; + bool done() const { return !obj; } void operator++(int); JSObject* staticScope() const { MOZ_ASSERT(!done()); return obj; } @@ -653,6 +653,9 @@ class StaticBlockObject : public BlockObject return slotValue(i).isTrue(); } + // Look up if the block has an aliased binding named |name|. + Shape* lookupAliasedName(PropertyName* name); + /* * A static block object is cloned (when entering the block) iff some * variable of the block isAliased.