Bug 1179063 - Hook up FunctionBox directly to the JSFunction being parsed to avoid allocating extra static scopes. (r=efaust)

This commit is contained in:
Shu-yu Guo 2015-08-19 18:42:56 -07:00
parent 3d6fedf828
commit 56a92a5a73
11 changed files with 109 additions and 146 deletions

View File

@ -110,16 +110,6 @@ struct frontend::LoopStmtInfo : public StmtInfoBCE
}
};
void
FunctionBox::switchStaticScopeToFunction()
{
if (staticScope_->is<StaticFunctionBoxScopeObject>()) {
MOZ_ASSERT(staticScope_->as<StaticFunctionBoxScopeObject>().functionBox() == this);
staticScope_ = function();
}
MOZ_ASSERT(staticScope_ == function());
}
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
Parser<FullParseHandler>* parser, SharedContext* sc,
HandleScript script, Handle<LazyScript*> lazyScript,

View File

@ -1160,8 +1160,8 @@ ObjectBox::trace(JSTracer* trc)
if (box->isFunctionBox()) {
FunctionBox* funbox = box->asFunctionBox();
funbox->bindings.trace(trc);
if (funbox->staticScope_)
TraceRoot(trc, &funbox->staticScope_, "funbox-staticScope");
if (funbox->enclosingStaticScope_)
TraceRoot(trc, &funbox->enclosingStaticScope_, "funbox-enclosingStaticScope");
}
box = box->traceLink;
}

View File

@ -48,6 +48,22 @@ using mozilla::Maybe;
using JS::AutoGCRooter;
JSFunction::AutoParseUsingFunctionBox::AutoParseUsingFunctionBox(ExclusiveContext* cx,
frontend::FunctionBox* funbox)
: fun_(cx, funbox->function()),
oldEnv_(cx, fun_->environment())
{
fun_->setFunctionBox(funbox);
funbox->computeAllowSyntax(fun_);
funbox->computeInWith(fun_);
}
JSFunction::AutoParseUsingFunctionBox::~AutoParseUsingFunctionBox()
{
fun_->unsetFunctionBox();
fun_->setEnvironment(oldEnv_);
}
namespace js {
namespace frontend {
@ -613,12 +629,12 @@ Parser<ParseHandler>::newObjectBox(JSObject* obj)
template <typename ParseHandler>
FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunction* fun,
JSObject* staticScope, ParseContext<ParseHandler>* outerpc,
JSObject* enclosingStaticScope, ParseContext<ParseHandler>* outerpc,
Directives directives, bool extraWarnings, GeneratorKind generatorKind)
: ObjectBox(fun, traceListHead),
SharedContext(cx, directives, extraWarnings),
bindings(),
staticScope_(staticScope),
enclosingStaticScope_(enclosingStaticScope),
bufStart(0),
bufEnd(0),
length(0),
@ -636,22 +652,17 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunct
// baked into JIT code, so they must be allocated tenured. They are held by
// the JSScript so cannot be collected during a minor GC anyway.
MOZ_ASSERT(fun->isTenured());
if (staticScope->is<StaticFunctionBoxScopeObject>())
staticScope->as<StaticFunctionBoxScopeObject>().setFunctionBox(this);
computeAllowSyntax(staticScope);
computeInWith(staticScope);
}
template <typename ParseHandler>
FunctionBox*
Parser<ParseHandler>::newFunctionBoxWithScope(Node fn, JSFunction* fun,
ParseContext<ParseHandler>* outerpc,
Directives inheritedDirectives,
GeneratorKind generatorKind,
JSObject* staticScope)
Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun,
ParseContext<ParseHandler>* outerpc,
Directives inheritedDirectives,
GeneratorKind generatorKind,
JSObject* enclosingStaticScope)
{
MOZ_ASSERT_IF(outerpc, enclosingStaticScope == outerpc->innermostStaticScope());
MOZ_ASSERT(fun);
/*
@ -662,9 +673,8 @@ Parser<ParseHandler>::newFunctionBoxWithScope(Node fn, JSFunction* fun,
* function.
*/
FunctionBox* funbox =
alloc.new_<FunctionBox>(context, traceListHead, fun, staticScope, outerpc,
inheritedDirectives,
options().extraWarningsOption,
alloc.new_<FunctionBox>(context, traceListHead, fun, enclosingStaticScope, outerpc,
inheritedDirectives, options().extraWarningsOption,
generatorKind);
if (!funbox) {
ReportOutOfMemory(context);
@ -678,29 +688,6 @@ Parser<ParseHandler>::newFunctionBoxWithScope(Node fn, JSFunction* fun,
return funbox;
}
template <typename ParseHandler>
FunctionBox*
Parser<ParseHandler>::newFunctionBox(Node fn, HandleFunction fun,
ParseContext<ParseHandler>* outerpc,
Directives inheritedDirectives,
GeneratorKind generatorKind,
HandleObject enclosingStaticScope)
{
MOZ_ASSERT_IF(outerpc, enclosingStaticScope == outerpc->innermostStaticScope());
StaticFunctionBoxScopeObject* scope =
StaticFunctionBoxScopeObject::create(context, enclosingStaticScope);
if (!scope)
return nullptr;
FunctionBox* funbox = newFunctionBoxWithScope(fn, fun, outerpc, inheritedDirectives,
generatorKind, scope);
if (!funbox)
return nullptr;
return funbox;
}
template <typename ParseHandler>
void
Parser<ParseHandler>::trace(JSTracer* trc)

View File

@ -106,6 +106,11 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
Node maybeFunction; /* sc->isFunctionBox, the pn where pn->pn_funbox == sc */
// If sc->isFunctionBox(), this is used to temporarily link up the
// FunctionBox with the JSFunction so the static scope chain may be walked
// without a JSScript.
mozilla::Maybe<JSFunction::AutoParseUsingFunctionBox> parseUsingFunctionBox;
// lastYieldOffset stores the offset of the last yield that was parsed.
// NoYieldOffset is its initial value.
static const uint32_t NoYieldOffset = UINT32_MAX;
@ -270,6 +275,8 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
inDeclDestructuring(false)
{
prs->pc = this;
if (sc->isFunctionBox())
parseUsingFunctionBox.emplace(prs->context, sc->asFunctionBox());
}
~ParseContext();
@ -463,17 +470,10 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
* cx->tempLifoAlloc.
*/
ObjectBox* newObjectBox(JSObject* obj);
FunctionBox* newFunctionBoxWithScope(Node fn, JSFunction* fun,
ParseContext<ParseHandler>* outerpc,
Directives directives, GeneratorKind generatorKind,
JSObject* staticScope);
private:
FunctionBox* newFunctionBox(Node fn, HandleFunction fun, ParseContext<ParseHandler>* outerpc,
FunctionBox* newFunctionBox(Node fn, JSFunction* fun, ParseContext<ParseHandler>* outerpc,
Directives directives, GeneratorKind generatorKind,
HandleObject enclosingStaticScope);
JSObject* enclosingStaticScope);
public:
// Use when the funbox is the outermost.
FunctionBox* newFunctionBox(Node fn, HandleFunction fun, Directives directives,
GeneratorKind generatorKind, HandleObject enclosingStaticScope)

View File

@ -191,10 +191,6 @@ class SharedContext
bool inWith_;
bool superScopeAlreadyNeedsHomeObject_;
protected:
void computeAllowSyntax(JSObject* staticScope);
void computeInWith(JSObject* staticScope);
public:
SharedContext(ExclusiveContext* cx, Directives directives,
bool extraWarnings)
@ -215,6 +211,8 @@ class SharedContext
// for the static scope. FunctionBoxes are LifoAlloc'd and need to
// manually trace their static scope.
virtual JSObject* staticScope() const = 0;
void computeAllowSyntax(JSObject* staticScope);
void computeInWith(JSObject* staticScope);
virtual ObjectBox* toObjectBox() { return nullptr; }
inline bool isFunctionBox() { return toObjectBox() && toObjectBox()->isFunctionBox(); }
@ -277,7 +275,7 @@ class FunctionBox : public ObjectBox, public SharedContext
{
public:
Bindings bindings; /* bindings for this function */
JSObject* staticScope_;
JSObject* enclosingStaticScope_;
uint32_t bufStart;
uint32_t bufEnd;
uint32_t startLine;
@ -285,7 +283,6 @@ class FunctionBox : public ObjectBox, public SharedContext
uint16_t length;
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
bool inWith_:1; /* some enclosing scope is a with-statement */
bool inGenexpLambda:1; /* lambda from generator expression */
bool hasDestructuringArgs:1; /* arguments list contains destructuring expression */
bool useAsm:1; /* see useAsmOrInsideUseAsm */
@ -300,13 +297,13 @@ class FunctionBox : public ObjectBox, public SharedContext
template <typename ParseHandler>
FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunction* fun,
JSObject* staticScope, ParseContext<ParseHandler>* pc,
JSObject* enclosingStaticScope, ParseContext<ParseHandler>* pc,
Directives directives, bool extraWarnings, GeneratorKind generatorKind);
ObjectBox* toObjectBox() override { return this; }
JSFunction* function() const { return &object->as<JSFunction>(); }
JSObject* staticScope() const override { return staticScope_; }
void switchStaticScopeToFunction();
JSObject* staticScope() const override { return function(); }
JSObject* enclosingStaticScope() const { return enclosingStaticScope_; }
GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
bool isGenerator() const { return generatorKind() != NotGenerator; }

View File

@ -687,12 +687,12 @@ JSFunction::trace(JSTracer* trc)
// Functions can be be marked as interpreted despite having no script
// yet at some points when parsing, and can be lazy with no lazy script
// for self-hosted code.
if (hasScript() && u.i.s.script_)
if (hasScript() && !hasUncompiledScript())
TraceManuallyBarrieredEdge(trc, &u.i.s.script_, "script");
else if (isInterpretedLazy() && u.i.s.lazy_)
TraceManuallyBarrieredEdge(trc, &u.i.s.lazy_, "lazyScript");
if (u.i.env_)
if (!isBeingParsed() && u.i.env_)
TraceManuallyBarrieredEdge(trc, &u.i.env_, "fun_environment");
}
}

View File

@ -16,6 +16,11 @@
#include "jstypes.h"
namespace js {
namespace frontend {
class FunctionBox;
}
class FunctionExtended;
typedef JSNative Native;
@ -57,9 +62,11 @@ class JSFunction : public js::NativeObject
INTERPRETED_LAZY = 0x0200, /* function is interpreted but doesn't have a script yet */
RESOLVED_LENGTH = 0x0400, /* f.length has been resolved (see fun_resolve). */
RESOLVED_NAME = 0x0800, /* f.name has been resolved (see fun_resolve). */
BEING_PARSED = 0x1000, /* function is currently being parsed; has
a funbox in place of an environment */
FUNCTION_KIND_SHIFT = 12,
FUNCTION_KIND_MASK = 0xf << FUNCTION_KIND_SHIFT,
FUNCTION_KIND_SHIFT = 13,
FUNCTION_KIND_MASK = 0x7 << FUNCTION_KIND_SHIFT,
ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT,
ARROW_KIND = Arrow << FUNCTION_KIND_SHIFT,
@ -93,6 +100,18 @@ class JSFunction : public js::NativeObject
static_assert(((FunctionKindLimit - 1) << FUNCTION_KIND_SHIFT) <= FUNCTION_KIND_MASK,
"FunctionKind doesn't fit into flags_");
// Implemented in Parser.cpp. Used so the static scope chain may be walked
// in Parser without a JSScript.
class MOZ_STACK_CLASS AutoParseUsingFunctionBox
{
js::RootedFunction fun_;
js::RootedObject oldEnv_;
public:
AutoParseUsingFunctionBox(js::ExclusiveContext* cx, js::frontend::FunctionBox* funbox);
~AutoParseUsingFunctionBox();
};
private:
uint16_t nargs_; /* number of formal arguments
(including defaults and the rest parameter unlike f.length) */
@ -111,8 +130,11 @@ class JSFunction : public js::NativeObject
use the accessor! */
js::LazyScript* lazy_; /* lazily compiled script, or nullptr */
} s;
JSObject* env_; /* environment for new activations;
use the accessor! */
union {
JSObject* env_; /* environment for new activations;
use the accessor! */
js::frontend::FunctionBox* funbox_; /* the function box when parsing */
};
} i;
void* nativeOrScript;
} u;
@ -123,6 +145,7 @@ class JSFunction : public js::NativeObject
/* Call objects must be created for each invocation of a heavyweight function. */
bool isHeavyweight() const {
MOZ_ASSERT(!isInterpretedLazy());
MOZ_ASSERT(!isBeingParsed());
if (isNative())
return false;
@ -165,6 +188,7 @@ class JSFunction : public js::NativeObject
bool hasRest() const { return flags() & HAS_REST; }
bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; }
bool hasScript() const { return flags() & INTERPRETED; }
bool isBeingParsed() const { return flags() & BEING_PARSED; }
// Arrow functions store their lexical |this| in the first extended slot.
bool isArrow() const { return kind() == Arrow; }
@ -288,20 +312,34 @@ class JSFunction : public js::NativeObject
* activations (stack frames) of the function.
*/
JSObject* environment() const {
MOZ_ASSERT(isInterpreted());
MOZ_ASSERT(isInterpreted() && !isBeingParsed());
return u.i.env_;
}
void setEnvironment(JSObject* obj) {
MOZ_ASSERT(isInterpreted());
MOZ_ASSERT(isInterpreted() && !isBeingParsed());
*(js::HeapPtrObject*)&u.i.env_ = obj;
}
void initEnvironment(JSObject* obj) {
MOZ_ASSERT(isInterpreted());
MOZ_ASSERT(isInterpreted() && !isBeingParsed());
((js::HeapPtrObject*)&u.i.env_)->init(obj);
}
private:
void setFunctionBox(js::frontend::FunctionBox* funbox) {
MOZ_ASSERT(isInterpreted());
flags_ |= BEING_PARSED;
u.i.funbox_ = funbox;
}
void unsetFunctionBox() {
MOZ_ASSERT(isBeingParsed());
flags_ &= ~BEING_PARSED;
u.i.funbox_ = nullptr;
}
public:
static inline size_t offsetOfNargs() { return offsetof(JSFunction, nargs_); }
static inline size_t offsetOfFlags() { return offsetof(JSFunction, flags_); }
static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); }
@ -399,6 +437,11 @@ class JSFunction : public js::NativeObject
return u.i.s.lazy_;
}
js::frontend::FunctionBox* functionBox() const {
MOZ_ASSERT(isBeingParsed());
return u.i.funbox_;
}
js::GeneratorKind generatorKind() const {
if (!isInterpreted())
return js::NotGenerator;

View File

@ -2650,10 +2650,6 @@ JSScript::linkToFunctionFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScrip
fun->setUnlazifiedScript(script);
else
fun->setScript(script);
// Switch the static scope over to the JSFunction now that we have a
// JSScript.
funbox->switchStaticScopeToFunction();
}
/* static */ bool

View File

@ -80,11 +80,13 @@ StaticScopeIter<allowGC>::operator++(int)
obj = obj->template as<StaticEvalObject>().enclosingScopeForStaticScopeIter();
} else if (obj->template is<StaticNonSyntacticScopeObjects>()) {
obj = obj->template as<StaticNonSyntacticScopeObjects>().enclosingScopeForStaticScopeIter();
} else if (obj->template is<StaticFunctionBoxScopeObject>()) {
obj = obj->template as<StaticFunctionBoxScopeObject>().enclosingScopeForStaticScopeIter();
} else if (onNamedLambda || !obj->template as<JSFunction>().isNamedLambda()) {
onNamedLambda = false;
obj = obj->template as<JSFunction>().nonLazyScript()->enclosingStaticScope();
JSFunction& fun = obj->template as<JSFunction>();
if (fun.isBeingParsed())
obj = fun.functionBox()->enclosingStaticScope();
else
obj = fun.nonLazyScript()->enclosingStaticScope();
} else {
onNamedLambda = true;
}
@ -96,10 +98,12 @@ template <AllowGC allowGC>
inline bool
StaticScopeIter<allowGC>::hasSyntacticDynamicScopeObject() const
{
if (obj->template is<JSFunction>())
return obj->template as<JSFunction>().isHeavyweight();
if (obj->template is<StaticFunctionBoxScopeObject>())
return obj->template as<StaticFunctionBoxScopeObject>().functionBox()->isHeavyweight();
if (obj->template is<JSFunction>()) {
JSFunction& fun = obj->template as<JSFunction>();
if (fun.isBeingParsed())
return fun.functionBox()->isHeavyweight();
return fun.isHeavyweight();
}
if (obj->template is<StaticBlockObject>())
return obj->template as<StaticBlockObject>().needsClone();
if (obj->template is<StaticWithObject>())
@ -183,8 +187,6 @@ inline JSFunction&
StaticScopeIter<allowGC>::fun() const
{
MOZ_ASSERT(type() == Function);
if (maybeFunctionBox())
return *maybeFunctionBox()->function();
return obj->template as<JSFunction>();
}
@ -193,8 +195,8 @@ inline frontend::FunctionBox*
StaticScopeIter<allowGC>::maybeFunctionBox() const
{
MOZ_ASSERT(type() == Function);
if (obj->template is<StaticFunctionBoxScopeObject>())
return obj->template as<StaticFunctionBoxScopeObject>().functionBox();
if (fun().isBeingParsed())
return fun().functionBox();
return nullptr;
}

View File

@ -593,25 +593,6 @@ const Class NonSyntacticVariablesObject::class_ = {
JSCLASS_IS_ANONYMOUS
};
/* static */ StaticFunctionBoxScopeObject*
StaticFunctionBoxScopeObject::create(ExclusiveContext* cx, HandleObject enclosing)
{
StaticFunctionBoxScopeObject* obj =
NewObjectWithNullTaggedProto<StaticFunctionBoxScopeObject>(cx, GenericObject,
BaseShape::DELEGATE);
if (!obj)
return nullptr;
obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(enclosing));
return obj;
}
const Class StaticFunctionBoxScopeObject::class_ = {
"StaticFunctionBoxScopeObject",
JSCLASS_HAS_RESERVED_SLOTS(StaticFunctionBoxScopeObject::RESERVED_SLOTS) |
JSCLASS_IS_ANONYMOUS
};
/*****************************************************************************/
/* static */ ClonedBlockObject*
@ -2612,7 +2593,7 @@ js::DumpStaticScopeChain(JSObject* staticScope)
for (StaticScopeIter<NoGC> ssi(staticScope); !ssi.done(); ssi++) {
switch (ssi.type()) {
case StaticScopeIter<NoGC>::Function:
if (ssi.maybeFunctionBox())
if (ssi.fun().isBeingParsed())
fprintf(stdout, "funbox [%p fun=%p]", ssi.maybeFunctionBox(), &ssi.fun());
else
fprintf(stdout, "function [%p]", &ssi.fun());

View File

@ -26,7 +26,6 @@ class FunctionBox;
class StaticWithObject;
class StaticEvalObject;
class StaticNonSyntacticScopeObjects;
class StaticFunctionBoxScopeObject;
/*****************************************************************************/
@ -59,10 +58,6 @@ class StaticFunctionBoxScopeObject;
* StaticNonSyntacticScopeObjects
* Signals presence of "polluting" scope objects. Used by Gecko.
*
* StaticFunctionBoxScopeObject
* Stands in for JSFunctions in the Parser, before their JSScripts
* are compiled.
*
* There is an additional scope for named lambdas without a static scope
* object. E.g., in:
*
@ -83,7 +78,6 @@ class StaticScopeIter
obj->is<StaticWithObject>() ||
obj->is<StaticEvalObject>() ||
obj->is<StaticNonSyntacticScopeObjects>() ||
obj->is<StaticFunctionBoxScopeObject>() ||
obj->is<JSFunction>();
}
@ -459,33 +453,6 @@ class NonSyntacticVariablesObject : public ScopeObject
static NonSyntacticVariablesObject* create(JSContext* cx, Handle<GlobalObject*> global);
};
// Function static scopes that wrap around FunctionBoxes used in the Parser,
// before a JSScript has been created.
class StaticFunctionBoxScopeObject : public ScopeObject
{
static const unsigned FUNCTION_BOX_SLOT = 1;
public:
static const unsigned RESERVED_SLOTS = 2;
static const Class class_;
static StaticFunctionBoxScopeObject* create(ExclusiveContext* cx,
HandleObject enclosing);
void setFunctionBox(frontend::FunctionBox* funbox) {
setReservedSlot(FUNCTION_BOX_SLOT, PrivateValue(funbox));
}
frontend::FunctionBox* functionBox() {
return reinterpret_cast<frontend::FunctionBox*>(
getReservedSlot(FUNCTION_BOX_SLOT).toPrivate());
}
JSObject* enclosingScopeForStaticScopeIter() {
return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
}
};
class NestedScopeObject : public ScopeObject
{
public: