mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-09 11:29:39 +00:00
663 lines
25 KiB
C++
663 lines
25 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef frontend_SharedContext_h
|
|
#define frontend_SharedContext_h
|
|
|
|
#include "jsatom.h"
|
|
#include "jsopcode.h"
|
|
#include "jspubtd.h"
|
|
#include "jsscript.h"
|
|
#include "jstypes.h"
|
|
|
|
#include "builtin/ModuleObject.h"
|
|
#include "frontend/ParseMaps.h"
|
|
#include "frontend/ParseNode.h"
|
|
#include "frontend/TokenStream.h"
|
|
#include "vm/ScopeObject.h"
|
|
|
|
namespace js {
|
|
namespace frontend {
|
|
|
|
// These flags apply to both global and function contexts.
|
|
class AnyContextFlags
|
|
{
|
|
// This class's data is all private and so only visible to these friends.
|
|
friend class SharedContext;
|
|
|
|
// True if "use strict"; appears in the body instead of being inherited.
|
|
bool hasExplicitUseStrict:1;
|
|
|
|
// The (static) bindings of this script need to support dynamic name
|
|
// read/write access. Here, 'dynamic' means dynamic dictionary lookup on
|
|
// the scope chain for a dynamic set of keys. The primary examples are:
|
|
// - direct eval
|
|
// - function::
|
|
// - with
|
|
// since both effectively allow any name to be accessed. Non-examples are:
|
|
// - upvars of nested functions
|
|
// - function statement
|
|
// since the set of assigned name is known dynamically. 'with' could be in
|
|
// the non-example category, provided the set of all free variables within
|
|
// the with block was noted. However, we do not optimize 'with' so, for
|
|
// simplicity, 'with' is treated like eval.
|
|
//
|
|
// Note: access through the arguments object is not considered dynamic
|
|
// binding access since it does not go through the normal name lookup
|
|
// mechanism. This is debatable and could be changed (although care must be
|
|
// taken not to turn off the whole 'arguments' optimization). To answer the
|
|
// more general "is this argument aliased" question, script->needsArgsObj
|
|
// should be tested (see JSScript::argIsAlised).
|
|
//
|
|
bool bindingsAccessedDynamically:1;
|
|
|
|
// Whether this script, or any of its inner scripts contains a debugger
|
|
// statement which could potentially read or write anywhere along the
|
|
// scope chain.
|
|
bool hasDebuggerStatement:1;
|
|
|
|
// A direct eval occurs in the body of the script.
|
|
bool hasDirectEval:1;
|
|
|
|
public:
|
|
AnyContextFlags()
|
|
: hasExplicitUseStrict(false),
|
|
bindingsAccessedDynamically(false),
|
|
hasDebuggerStatement(false),
|
|
hasDirectEval(false)
|
|
{ }
|
|
};
|
|
|
|
class FunctionContextFlags
|
|
{
|
|
// This class's data is all private and so only visible to these friends.
|
|
friend class FunctionBox;
|
|
|
|
// The function or a function that encloses it may define new local names
|
|
// at runtime through means other than calling eval.
|
|
bool mightAliasLocals:1;
|
|
|
|
// This function does something that can extend the set of bindings in its
|
|
// call objects --- it does a direct eval in non-strict code, or includes a
|
|
// function statement (as opposed to a function definition).
|
|
//
|
|
// This flag is *not* inherited by enclosed or enclosing functions; it
|
|
// applies only to the function in whose flags it appears.
|
|
//
|
|
bool hasExtensibleScope:1;
|
|
|
|
// This function refers directly to its name in a way which requires the
|
|
// name to be a separate object on the scope chain.
|
|
bool needsDeclEnvObject:1;
|
|
|
|
// Technically, every function has a binding named 'arguments'. Internally,
|
|
// this binding is only added when 'arguments' is mentioned by the function
|
|
// body. This flag indicates whether 'arguments' has been bound either
|
|
// through implicit use:
|
|
// function f() { return arguments }
|
|
// or explicit redeclaration:
|
|
// function f() { var arguments; return arguments }
|
|
//
|
|
// Note 1: overwritten arguments (function() { arguments = 3 }) will cause
|
|
// this flag to be set but otherwise require no special handling:
|
|
// 'arguments' is just a local variable and uses of 'arguments' will just
|
|
// read the local's current slot which may have been assigned. The only
|
|
// special semantics is that the initial value of 'arguments' is the
|
|
// arguments object (not undefined, like normal locals).
|
|
//
|
|
// Note 2: if 'arguments' is bound as a formal parameter, there will be an
|
|
// 'arguments' in Bindings, but, as the "LOCAL" in the name indicates, this
|
|
// flag will not be set. This is because, as a formal, 'arguments' will
|
|
// have no special semantics: the initial value is unconditionally the
|
|
// actual argument (or undefined if nactual < nformal).
|
|
//
|
|
bool argumentsHasLocalBinding:1;
|
|
|
|
// In many cases where 'arguments' has a local binding (as described above)
|
|
// we do not need to actually create an arguments object in the function
|
|
// prologue: instead we can analyze how 'arguments' is used (using the
|
|
// simple dataflow analysis in analyzeSSA) to determine that uses of
|
|
// 'arguments' can just read from the stack frame directly. However, the
|
|
// dataflow analysis only looks at how JSOP_ARGUMENTS is used, so it will
|
|
// be unsound in several cases. The frontend filters out such cases by
|
|
// setting this flag which eagerly sets script->needsArgsObj to true.
|
|
//
|
|
bool definitelyNeedsArgsObj:1;
|
|
|
|
bool needsHomeObject:1;
|
|
bool isDerivedClassConstructor:1;
|
|
|
|
// Whether this function has a .this binding. If true, we need to emit
|
|
// JSOP_FUNCTIONTHIS in the prologue to initialize it.
|
|
bool hasThisBinding:1;
|
|
|
|
FunctionContextFlags flagsForNestedGeneratorComprehensionLambda() const {
|
|
FunctionContextFlags flags;
|
|
flags.mightAliasLocals = mightAliasLocals;
|
|
flags.hasExtensibleScope = false;
|
|
flags.needsDeclEnvObject = false;
|
|
flags.argumentsHasLocalBinding = false;
|
|
flags.definitelyNeedsArgsObj = false;
|
|
flags.needsHomeObject = false;
|
|
flags.isDerivedClassConstructor = false;
|
|
flags.hasThisBinding = false;
|
|
return flags;
|
|
}
|
|
|
|
public:
|
|
FunctionContextFlags()
|
|
: mightAliasLocals(false),
|
|
hasExtensibleScope(false),
|
|
needsDeclEnvObject(false),
|
|
argumentsHasLocalBinding(false),
|
|
definitelyNeedsArgsObj(false),
|
|
needsHomeObject(false),
|
|
isDerivedClassConstructor(false),
|
|
hasThisBinding(false)
|
|
{ }
|
|
};
|
|
|
|
// List of directives that may be encountered in a Directive Prologue (ES5 15.1).
|
|
class Directives
|
|
{
|
|
bool strict_;
|
|
bool asmJS_;
|
|
|
|
public:
|
|
explicit Directives(bool strict) : strict_(strict), asmJS_(false) {}
|
|
template <typename ParseHandler> explicit Directives(ParseContext<ParseHandler>* parent);
|
|
|
|
void setStrict() { strict_ = true; }
|
|
bool strict() const { return strict_; }
|
|
|
|
void setAsmJS() { asmJS_ = true; }
|
|
bool asmJS() const { return asmJS_; }
|
|
|
|
Directives& operator=(Directives rhs) {
|
|
strict_ = rhs.strict_;
|
|
asmJS_ = rhs.asmJS_;
|
|
return *this;
|
|
}
|
|
bool operator==(const Directives& rhs) const {
|
|
return strict_ == rhs.strict_ && asmJS_ == rhs.asmJS_;
|
|
}
|
|
bool operator!=(const Directives& rhs) const {
|
|
return !(*this == rhs);
|
|
}
|
|
};
|
|
|
|
// The kind of this-binding for the current scope. Note that arrow functions
|
|
// (and generator expression lambdas) have a lexical this-binding so their
|
|
// ThisBinding is the same as the ThisBinding of their enclosing scope and can
|
|
// be any value.
|
|
enum class ThisBinding { Global, Function, Module };
|
|
|
|
/*
|
|
* The struct SharedContext is part of the current parser context (see
|
|
* ParseContext). It stores information that is reused between the parser and
|
|
* the bytecode emitter. Note however, that this information is not shared
|
|
* between the two; they simply reuse the same data structure.
|
|
*/
|
|
class SharedContext
|
|
{
|
|
public:
|
|
ExclusiveContext* const context;
|
|
AnyContextFlags anyCxFlags;
|
|
bool strictScript;
|
|
bool localStrict;
|
|
bool extraWarnings;
|
|
|
|
private:
|
|
ThisBinding thisBinding_;
|
|
|
|
bool allowNewTarget_;
|
|
bool allowSuperProperty_;
|
|
bool allowSuperCall_;
|
|
bool inWith_;
|
|
bool needsThisTDZChecks_;
|
|
bool superScopeAlreadyNeedsHomeObject_;
|
|
|
|
public:
|
|
SharedContext(ExclusiveContext* cx, Directives directives,
|
|
bool extraWarnings)
|
|
: context(cx),
|
|
anyCxFlags(),
|
|
strictScript(directives.strict()),
|
|
localStrict(false),
|
|
extraWarnings(extraWarnings),
|
|
thisBinding_(ThisBinding::Global),
|
|
allowNewTarget_(false),
|
|
allowSuperProperty_(false),
|
|
allowSuperCall_(false),
|
|
inWith_(false),
|
|
needsThisTDZChecks_(false),
|
|
superScopeAlreadyNeedsHomeObject_(false)
|
|
{ }
|
|
|
|
// The unfortunate reason that staticScope() is a virtual is because
|
|
// GlobalSharedContext and FunctionBox have different lifetimes.
|
|
// GlobalSharedContexts are stack allocated and thus may use RootedObject
|
|
// 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);
|
|
void computeThisBinding(JSObject* staticScope);
|
|
|
|
virtual ObjectBox* toObjectBox() { return nullptr; }
|
|
bool isObjectBox() { return toObjectBox() != nullptr; }
|
|
bool isFunctionBox() { return isObjectBox() && toObjectBox()->isFunctionBox(); }
|
|
inline FunctionBox* asFunctionBox();
|
|
bool isModuleBox() { return isObjectBox() && toObjectBox()->isModuleBox(); }
|
|
inline ModuleBox* asModuleBox();
|
|
bool isGlobalContext() { return !toObjectBox(); }
|
|
|
|
ThisBinding thisBinding() const { return thisBinding_; }
|
|
|
|
bool allowNewTarget() const { return allowNewTarget_; }
|
|
bool allowSuperProperty() const { return allowSuperProperty_; }
|
|
bool allowSuperCall() const { return allowSuperCall_; }
|
|
bool inWith() const { return inWith_; }
|
|
bool needsThisTDZChecks() const { return needsThisTDZChecks_; }
|
|
|
|
void markSuperScopeNeedsHomeObject();
|
|
|
|
bool hasExplicitUseStrict() const { return anyCxFlags.hasExplicitUseStrict; }
|
|
bool bindingsAccessedDynamically() const { return anyCxFlags.bindingsAccessedDynamically; }
|
|
bool hasDebuggerStatement() const { return anyCxFlags.hasDebuggerStatement; }
|
|
bool hasDirectEval() const { return anyCxFlags.hasDirectEval; }
|
|
|
|
void setExplicitUseStrict() { anyCxFlags.hasExplicitUseStrict = true; }
|
|
void setBindingsAccessedDynamically() { anyCxFlags.bindingsAccessedDynamically = true; }
|
|
void setHasDebuggerStatement() { anyCxFlags.hasDebuggerStatement = true; }
|
|
void setHasDirectEval() { anyCxFlags.hasDirectEval = true; }
|
|
|
|
inline bool allLocalsAliased();
|
|
|
|
bool strict() const {
|
|
return strictScript || localStrict;
|
|
}
|
|
bool setLocalStrictMode(bool strict) {
|
|
bool retVal = localStrict;
|
|
localStrict = strict;
|
|
return retVal;
|
|
}
|
|
|
|
// JSOPTION_EXTRA_WARNINGS warnings or strict mode errors.
|
|
bool needStrictChecks() const {
|
|
return strict() || extraWarnings;
|
|
}
|
|
|
|
bool isDotVariable(JSAtom* atom) const {
|
|
return atom == context->names().dotGenerator || atom == context->names().dotThis;
|
|
}
|
|
};
|
|
|
|
class MOZ_STACK_CLASS GlobalSharedContext : public SharedContext
|
|
{
|
|
Rooted<ScopeObject*> staticScope_;
|
|
|
|
public:
|
|
GlobalSharedContext(ExclusiveContext* cx, ScopeObject* staticScope, Directives directives,
|
|
bool extraWarnings, JSFunction* maybeEvalCaller = nullptr)
|
|
: SharedContext(cx, directives, extraWarnings),
|
|
staticScope_(cx, staticScope)
|
|
{
|
|
computeAllowSyntax(staticScope);
|
|
computeInWith(staticScope);
|
|
|
|
// If we're executing a Debugger eval-in-frame, staticScope is always a
|
|
// non-function scope, so we have to compute our ThisBinding based on
|
|
// the actual callee.
|
|
if (maybeEvalCaller)
|
|
computeThisBinding(maybeEvalCaller);
|
|
else
|
|
computeThisBinding(staticScope);
|
|
}
|
|
|
|
JSObject* staticScope() const override { return staticScope_; }
|
|
};
|
|
|
|
class FunctionBox : public ObjectBox, public SharedContext
|
|
{
|
|
public:
|
|
Bindings bindings; /* bindings for this function */
|
|
JSObject* enclosingStaticScope_;
|
|
uint32_t bufStart;
|
|
uint32_t bufEnd;
|
|
uint32_t startLine;
|
|
uint32_t startColumn;
|
|
uint16_t length;
|
|
|
|
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
|
|
uint8_t asyncKindBits_; /* The FunctionAsyncKing of this function. */
|
|
bool inGenexpLambda:1; /* lambda from generator expression */
|
|
bool hasDestructuringArgs:1; /* arguments list contains destructuring expression */
|
|
bool useAsm:1; /* see useAsmOrInsideUseAsm */
|
|
bool insideUseAsm:1; /* see useAsmOrInsideUseAsm */
|
|
bool wasEmitted:1; /* Bytecode has been emitted for this function. */
|
|
|
|
// Fields for use in heuristics.
|
|
bool usesArguments:1; /* contains a free use of 'arguments' */
|
|
bool usesApply:1; /* contains an f.apply() call */
|
|
bool usesThis:1; /* contains 'this' */
|
|
bool usesReturn:1; /* contains a 'return' statement */
|
|
|
|
FunctionContextFlags funCxFlags;
|
|
|
|
template <typename ParseHandler>
|
|
FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunction* fun,
|
|
JSObject* enclosingStaticScope, ParseContext<ParseHandler>* pc,
|
|
Directives directives, bool extraWarnings, GeneratorKind generatorKind,
|
|
FunctionAsyncKind asyncKind);
|
|
|
|
ObjectBox* toObjectBox() override { return this; }
|
|
JSFunction* function() const { return &object->as<JSFunction>(); }
|
|
JSObject* staticScope() const override { return function(); }
|
|
JSObject* enclosingStaticScope() const { return enclosingStaticScope_; }
|
|
|
|
bool isLikelyConstructorWrapper() const {
|
|
return usesArguments && usesApply && usesThis && !usesReturn;
|
|
}
|
|
|
|
GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
|
|
bool isGenerator() const { return generatorKind() != NotGenerator; }
|
|
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
|
|
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
|
|
FunctionAsyncKind asyncKind() const { return AsyncKindFromBits(asyncKindBits_); }
|
|
bool isAsync() const { return asyncKind() == AsyncFunction; }
|
|
bool isArrow() const { return function()->isArrow(); }
|
|
|
|
void setGeneratorKind(GeneratorKind kind) {
|
|
// A generator kind can be set at initialization, or when "yield" is
|
|
// first seen. In both cases the transition can only happen from
|
|
// NotGenerator.
|
|
MOZ_ASSERT(!isGenerator());
|
|
generatorKindBits_ = GeneratorKindAsBits(kind);
|
|
}
|
|
|
|
bool mightAliasLocals() const { return funCxFlags.mightAliasLocals; }
|
|
bool hasExtensibleScope() const { return funCxFlags.hasExtensibleScope; }
|
|
bool needsDeclEnvObject() const { return funCxFlags.needsDeclEnvObject; }
|
|
bool hasThisBinding() const { return funCxFlags.hasThisBinding; }
|
|
bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; }
|
|
bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; }
|
|
bool needsHomeObject() const { return funCxFlags.needsHomeObject; }
|
|
bool isDerivedClassConstructor() const { return funCxFlags.isDerivedClassConstructor; }
|
|
|
|
void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; }
|
|
void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; }
|
|
void setNeedsDeclEnvObject() { funCxFlags.needsDeclEnvObject = true; }
|
|
void setHasThisBinding() { funCxFlags.hasThisBinding = true; }
|
|
void setArgumentsHasLocalBinding() { funCxFlags.argumentsHasLocalBinding = true; }
|
|
void setDefinitelyNeedsArgsObj() { MOZ_ASSERT(funCxFlags.argumentsHasLocalBinding);
|
|
funCxFlags.definitelyNeedsArgsObj = true; }
|
|
void setNeedsHomeObject() { MOZ_ASSERT(function()->allowSuperProperty());
|
|
funCxFlags.needsHomeObject = true; }
|
|
void setDerivedClassConstructor() { MOZ_ASSERT(function()->isClassConstructor());
|
|
funCxFlags.isDerivedClassConstructor = true; }
|
|
|
|
FunctionContextFlags flagsForNestedGeneratorComprehensionLambda() const {
|
|
return funCxFlags.flagsForNestedGeneratorComprehensionLambda();
|
|
}
|
|
|
|
bool hasDefaults() const {
|
|
return length != function()->nargs() - function()->hasRest();
|
|
}
|
|
|
|
bool hasMappedArgsObj() const {
|
|
return !strict() && !function()->hasRest() && !hasDefaults() && !hasDestructuringArgs;
|
|
}
|
|
|
|
// Return whether this or an enclosing function is being parsed and
|
|
// validated as asm.js. Note: if asm.js validation fails, this will be false
|
|
// while the function is being reparsed. This flag can be used to disable
|
|
// certain parsing features that are necessary in general, but unnecessary
|
|
// for validated asm.js.
|
|
bool useAsmOrInsideUseAsm() const {
|
|
return useAsm || insideUseAsm;
|
|
}
|
|
|
|
void setStart(const TokenStream& tokenStream) {
|
|
bufStart = tokenStream.currentToken().pos.begin;
|
|
startLine = tokenStream.getLineno();
|
|
startColumn = tokenStream.getColumn();
|
|
}
|
|
|
|
bool needsCallObject()
|
|
{
|
|
// Note: this should be kept in sync with JSFunction::needsCallObject().
|
|
return bindings.hasAnyAliasedBindings() ||
|
|
hasExtensibleScope() ||
|
|
needsDeclEnvObject() ||
|
|
needsHomeObject() ||
|
|
isDerivedClassConstructor() ||
|
|
isGenerator();
|
|
}
|
|
};
|
|
|
|
class ModuleBox : public ObjectBox, public SharedContext
|
|
{
|
|
public:
|
|
Bindings bindings;
|
|
TraceableVector<JSAtom*> exportNames;
|
|
|
|
template <typename ParseHandler>
|
|
ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
|
|
ParseContext<ParseHandler>* pc);
|
|
|
|
ObjectBox* toObjectBox() override { return this; }
|
|
ModuleObject* module() const { return &object->as<ModuleObject>(); }
|
|
JSObject* staticScope() const override { return module(); }
|
|
};
|
|
|
|
inline FunctionBox*
|
|
SharedContext::asFunctionBox()
|
|
{
|
|
MOZ_ASSERT(isFunctionBox());
|
|
return static_cast<FunctionBox*>(this);
|
|
}
|
|
|
|
inline ModuleBox*
|
|
SharedContext::asModuleBox()
|
|
{
|
|
MOZ_ASSERT(isModuleBox());
|
|
return static_cast<ModuleBox*>(this);
|
|
}
|
|
|
|
|
|
// In generators, we treat all locals as aliased so that they get stored on the
|
|
// heap. This way there is less information to copy off the stack when
|
|
// suspending, and back on when resuming. It also avoids the need to create and
|
|
// invalidate DebugScope proxies for unaliased locals in a generator frame, as
|
|
// the generator frame will be copied out to the heap and released only by GC.
|
|
inline bool
|
|
SharedContext::allLocalsAliased()
|
|
{
|
|
return bindingsAccessedDynamically() || (isFunctionBox() && asFunctionBox()->isGenerator());
|
|
}
|
|
|
|
|
|
/*
|
|
* NB: If you add a new type of statement that is a scope, add it between
|
|
* STMT_WITH and STMT_CATCH, or you will break StmtInfoBase::linksScope. If you
|
|
* add a non-looping statement type, add it before STMT_DO_LOOP or you will
|
|
* break StmtInfoBase::isLoop().
|
|
*
|
|
* Also remember to keep the statementName array in BytecodeEmitter.cpp in
|
|
* sync.
|
|
*/
|
|
enum class StmtType : uint16_t {
|
|
LABEL, /* labeled statement: L: s */
|
|
IF, /* if (then) statement */
|
|
ELSE, /* else clause of if statement */
|
|
SEQ, /* synthetic sequence of statements */
|
|
BLOCK, /* compound statement: { s1[;... sN] } */
|
|
SWITCH, /* switch statement */
|
|
WITH, /* with statement */
|
|
CATCH, /* catch block */
|
|
TRY, /* try block */
|
|
FINALLY, /* finally block */
|
|
SUBROUTINE, /* gosub-target subroutine body */
|
|
DO_LOOP, /* do/while loop statement */
|
|
FOR_LOOP, /* for loop statement */
|
|
FOR_IN_LOOP, /* for/in loop statement */
|
|
FOR_OF_LOOP, /* for/of loop statement */
|
|
WHILE_LOOP, /* while loop statement */
|
|
SPREAD, /* spread operator (pseudo for/of) */
|
|
LIMIT
|
|
};
|
|
|
|
/*
|
|
* A comment on the encoding of the js::StmtType enum and StmtInfoBase
|
|
* type-testing methods:
|
|
*
|
|
* StmtInfoBase::maybeScope() tells whether a statement type is always, or may
|
|
* become, a lexical scope. It therefore includes block and switch (the two
|
|
* low-numbered "maybe" scope types) and excludes with (with has dynamic scope
|
|
* pending the "reformed with" in ES4/JS2). It includes all try-catch-finally
|
|
* types, which are high-numbered maybe-scope types.
|
|
*
|
|
* StmtInfoBase::linksScope() tells whether a js::StmtInfo{PC,BCE} of the given
|
|
* type eagerly links to other scoping statement info records. It excludes the
|
|
* two early "maybe" types, block and switch, as well as the try and both
|
|
* finally types, since try and the other trailing maybe-scope types don't need
|
|
* block scope unless they contain let declarations.
|
|
*
|
|
* We treat WITH as a static scope because it prevents lexical binding from
|
|
* continuing further up the static scope chain. With the lost "reformed with"
|
|
* proposal for ES4, we would be able to model it statically, too.
|
|
*/
|
|
|
|
// StmtInfoPC is used by the Parser. StmtInfoBCE is used by the
|
|
// BytecodeEmitter. The two types have some overlap, encapsulated by
|
|
// StmtInfoBase.
|
|
|
|
struct StmtInfoBase
|
|
{
|
|
// Statement type (StmtType).
|
|
StmtType type;
|
|
|
|
// True if type is StmtType::BLOCK, StmtType::TRY, StmtType::SWITCH, or
|
|
// StmtType::FINALLY and the block contains at least one let-declaration,
|
|
// or if type is StmtType::CATCH.
|
|
bool isBlockScope:1;
|
|
|
|
// for (let ...) induced block scope
|
|
bool isForLetBlock:1;
|
|
|
|
// Block label.
|
|
RootedAtom label;
|
|
|
|
// Compile-time scope chain node for this scope.
|
|
Rooted<NestedScopeObject*> staticScope;
|
|
|
|
explicit StmtInfoBase(ExclusiveContext* cx)
|
|
: isBlockScope(false), isForLetBlock(false),
|
|
label(cx), staticScope(cx)
|
|
{}
|
|
|
|
bool maybeScope() const {
|
|
return StmtType::BLOCK <= type && type <= StmtType::SUBROUTINE &&
|
|
type != StmtType::WITH;
|
|
}
|
|
|
|
bool linksScope() const {
|
|
return !!staticScope;
|
|
}
|
|
|
|
bool canBeBlockScope() {
|
|
return type == StmtType::BLOCK ||
|
|
type == StmtType::SWITCH ||
|
|
type == StmtType::TRY ||
|
|
type == StmtType::FINALLY ||
|
|
type == StmtType::CATCH;
|
|
}
|
|
|
|
StaticBlockObject& staticBlock() const {
|
|
MOZ_ASSERT(staticScope);
|
|
MOZ_ASSERT(isBlockScope);
|
|
return staticScope->as<StaticBlockObject>();
|
|
}
|
|
|
|
bool isLoop() const {
|
|
return type >= StmtType::DO_LOOP;
|
|
}
|
|
|
|
bool isTrying() const {
|
|
return StmtType::TRY <= type && type <= StmtType::SUBROUTINE;
|
|
}
|
|
};
|
|
|
|
template <class StmtInfo>
|
|
class MOZ_STACK_CLASS StmtInfoStack
|
|
{
|
|
// Top of the stack.
|
|
StmtInfo* innermostStmt_;
|
|
|
|
// Top scope statement with a nested scope.
|
|
StmtInfo* innermostScopeStmt_;
|
|
|
|
public:
|
|
explicit StmtInfoStack(ExclusiveContext* cx)
|
|
: innermostStmt_(nullptr),
|
|
innermostScopeStmt_(nullptr)
|
|
{ }
|
|
|
|
StmtInfo* innermost() const { return innermostStmt_; }
|
|
StmtInfo* innermostScopeStmt() const { return innermostScopeStmt_; }
|
|
StmtInfo* innermostNonLabel() const {
|
|
StmtInfo* stmt = innermost();
|
|
while (stmt && stmt->type == StmtType::LABEL)
|
|
stmt = stmt->enclosing;
|
|
return stmt;
|
|
}
|
|
|
|
void push(StmtInfo* stmt, StmtType type) {
|
|
stmt->type = type;
|
|
stmt->isBlockScope = false;
|
|
stmt->isForLetBlock = false;
|
|
stmt->label = nullptr;
|
|
stmt->staticScope = nullptr;
|
|
stmt->enclosing = innermostStmt_;
|
|
stmt->enclosingScope = nullptr;
|
|
innermostStmt_ = stmt;
|
|
}
|
|
|
|
void pushNestedScope(StmtInfo* stmt, StmtType type, NestedScopeObject& staticScope) {
|
|
push(stmt, type);
|
|
linkAsInnermostScopeStmt(stmt, staticScope);
|
|
}
|
|
|
|
void pop() {
|
|
StmtInfo* stmt = innermostStmt_;
|
|
innermostStmt_ = stmt->enclosing;
|
|
if (stmt->linksScope())
|
|
innermostScopeStmt_ = stmt->enclosingScope;
|
|
}
|
|
|
|
void linkAsInnermostScopeStmt(StmtInfo* stmt, NestedScopeObject& staticScope) {
|
|
MOZ_ASSERT(stmt != innermostScopeStmt_);
|
|
MOZ_ASSERT(!stmt->enclosingScope);
|
|
stmt->enclosingScope = innermostScopeStmt_;
|
|
innermostScopeStmt_ = stmt;
|
|
stmt->staticScope = &staticScope;
|
|
}
|
|
|
|
void makeInnermostLexicalScope(StaticBlockObject& blockObj) {
|
|
MOZ_ASSERT(!innermostStmt_->isBlockScope);
|
|
MOZ_ASSERT(innermostStmt_->canBeBlockScope());
|
|
innermostStmt_->isBlockScope = true;
|
|
linkAsInnermostScopeStmt(innermostStmt_, blockObj);
|
|
}
|
|
};
|
|
|
|
} // namespace frontend
|
|
|
|
} // namespace js
|
|
|
|
#endif /* frontend_SharedContext_h */
|