summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/SharedContext.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/SharedContext.h')
-rw-r--r--js/src/frontend/SharedContext.h639
1 files changed, 639 insertions, 0 deletions
diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h
new file mode 100644
index 000000000..39df47c20
--- /dev/null
+++ b/js/src/frontend/SharedContext.h
@@ -0,0 +1,639 @@
+/* -*- 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 "ds/InlineTable.h"
+#include "frontend/ParseNode.h"
+#include "frontend/TokenStream.h"
+#include "vm/EnvironmentObject.h"
+
+namespace js {
+namespace frontend {
+
+enum class StatementKind : uint8_t
+{
+ Label,
+ Block,
+ If,
+ Switch,
+ With,
+ Catch,
+ Try,
+ Finally,
+ ForLoopLexicalHead,
+ ForLoop,
+ ForInLoop,
+ ForOfLoop,
+ DoLoop,
+ WhileLoop,
+
+ // Used only by BytecodeEmitter.
+ Spread
+};
+
+static inline bool
+StatementKindIsLoop(StatementKind kind)
+{
+ return kind == StatementKind::ForLoop ||
+ kind == StatementKind::ForInLoop ||
+ kind == StatementKind::ForOfLoop ||
+ kind == StatementKind::DoLoop ||
+ kind == StatementKind::WhileLoop ||
+ kind == StatementKind::Spread;
+}
+
+static inline bool
+StatementKindIsUnlabeledBreakTarget(StatementKind kind)
+{
+ return StatementKindIsLoop(kind) || kind == StatementKind::Switch;
+}
+
+// A base class for nestable structures in the frontend, such as statements
+// and scopes.
+template <typename Concrete>
+class MOZ_STACK_CLASS Nestable
+{
+ Concrete** stack_;
+ Concrete* enclosing_;
+
+ protected:
+ explicit Nestable(Concrete** stack)
+ : stack_(stack),
+ enclosing_(*stack)
+ {
+ *stack_ = static_cast<Concrete*>(this);
+ }
+
+ // These method are protected. Some derived classes, such as ParseContext,
+ // do not expose the ability to walk the stack.
+ Concrete* enclosing() const {
+ return enclosing_;
+ }
+
+ template <typename Predicate /* (Concrete*) -> bool */>
+ static Concrete* findNearest(Concrete* it, Predicate predicate) {
+ while (it && !predicate(it))
+ it = it->enclosing();
+ return it;
+ }
+
+ template <typename T>
+ static T* findNearest(Concrete* it) {
+ while (it && !it->template is<T>())
+ it = it->enclosing();
+ return it ? &it->template as<T>() : nullptr;
+ }
+
+ template <typename T, typename Predicate /* (T*) -> bool */>
+ static T* findNearest(Concrete* it, Predicate predicate) {
+ while (it && (!it->template is<T>() || !predicate(&it->template as<T>())))
+ it = it->enclosing();
+ return it ? &it->template as<T>() : nullptr;
+ }
+
+ public:
+ ~Nestable() {
+ MOZ_ASSERT(*stack_ == static_cast<Concrete*>(this));
+ *stack_ = enclosing_;
+ }
+};
+
+// 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.
+ //
+ // 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::argIsAliased).
+ 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;
+
+ // 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;
+
+ // 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;
+
+ // Whether this function has nested functions.
+ bool hasInnerFunctions:1;
+
+ public:
+ FunctionContextFlags()
+ : hasExtensibleScope(false),
+ argumentsHasLocalBinding(false),
+ definitelyNeedsArgsObj(false),
+ needsHomeObject(false),
+ isDerivedClassConstructor(false),
+ hasThisBinding(false),
+ hasInnerFunctions(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) {}
+ explicit Directives(ParseContext* 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 };
+
+class GlobalSharedContext;
+class EvalSharedContext;
+class ModuleSharedContext;
+
+/*
+ * 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.
+ */
+class SharedContext
+{
+ public:
+ ExclusiveContext* const context;
+ AnyContextFlags anyCxFlags;
+ bool strictScript;
+ bool localStrict;
+ bool extraWarnings;
+
+ protected:
+ enum class Kind {
+ ObjectBox,
+ Global,
+ Eval,
+ Module
+ };
+
+ Kind kind_;
+
+ ThisBinding thisBinding_;
+
+ bool allowNewTarget_;
+ bool allowSuperProperty_;
+ bool allowSuperCall_;
+ bool inWith_;
+ bool needsThisTDZChecks_;
+
+ void computeAllowSyntax(Scope* scope);
+ void computeInWith(Scope* scope);
+ void computeThisBinding(Scope* scope);
+
+ public:
+ SharedContext(ExclusiveContext* cx, Kind kind, Directives directives, bool extraWarnings)
+ : context(cx),
+ anyCxFlags(),
+ strictScript(directives.strict()),
+ localStrict(false),
+ extraWarnings(extraWarnings),
+ kind_(kind),
+ thisBinding_(ThisBinding::Global),
+ allowNewTarget_(false),
+ allowSuperProperty_(false),
+ allowSuperCall_(false),
+ inWith_(false),
+ needsThisTDZChecks_(false)
+ { }
+
+ // If this is the outermost SharedContext, the Scope that encloses
+ // it. Otherwise nullptr.
+ virtual Scope* compilationEnclosingScope() const = 0;
+
+ virtual ObjectBox* toObjectBox() { return nullptr; }
+ bool isObjectBox() { return toObjectBox(); }
+ bool isFunctionBox() { return isObjectBox() && toObjectBox()->isFunctionBox(); }
+ inline FunctionBox* asFunctionBox();
+ bool isModuleContext() { return kind_ == Kind::Module; }
+ inline ModuleSharedContext* asModuleContext();
+ bool isGlobalContext() { return kind_ == Kind::Global; }
+ inline GlobalSharedContext* asGlobalContext();
+ bool isEvalContext() { return kind_ == Kind::Eval; }
+ inline EvalSharedContext* asEvalContext();
+
+ 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_; }
+
+ 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 allBindingsClosedOver();
+
+ 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
+{
+ ScopeKind scopeKind_;
+
+ public:
+ Rooted<GlobalScope::Data*> bindings;
+
+ GlobalSharedContext(ExclusiveContext* cx, ScopeKind scopeKind, Directives directives,
+ bool extraWarnings)
+ : SharedContext(cx, Kind::Global, directives, extraWarnings),
+ scopeKind_(scopeKind),
+ bindings(cx)
+ {
+ MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
+ thisBinding_ = ThisBinding::Global;
+ }
+
+ Scope* compilationEnclosingScope() const override {
+ return nullptr;
+ }
+
+ ScopeKind scopeKind() const {
+ return scopeKind_;
+ }
+};
+
+inline GlobalSharedContext*
+SharedContext::asGlobalContext()
+{
+ MOZ_ASSERT(isGlobalContext());
+ return static_cast<GlobalSharedContext*>(this);
+}
+
+class MOZ_STACK_CLASS EvalSharedContext : public SharedContext
+{
+ RootedScope enclosingScope_;
+
+ public:
+ Rooted<EvalScope::Data*> bindings;
+
+ EvalSharedContext(ExclusiveContext* cx, JSObject* enclosingEnv, Scope* enclosingScope,
+ Directives directives, bool extraWarnings);
+
+ Scope* compilationEnclosingScope() const override {
+ return enclosingScope_;
+ }
+};
+
+inline EvalSharedContext*
+SharedContext::asEvalContext()
+{
+ MOZ_ASSERT(isEvalContext());
+ return static_cast<EvalSharedContext*>(this);
+}
+
+class FunctionBox : public ObjectBox, public SharedContext
+{
+ // The parser handles tracing the fields below via the ObjectBox linked
+ // list.
+
+ Scope* enclosingScope_;
+
+ // Names from the named lambda scope, if a named lambda.
+ LexicalScope::Data* namedLambdaBindings_;
+
+ // Names from the function scope.
+ FunctionScope::Data* functionScopeBindings_;
+
+ // Names from the extra 'var' scope of the function, if the parameter list
+ // has expressions.
+ VarScope::Data* extraVarScopeBindings_;
+
+ void initWithEnclosingScope(Scope* enclosingScope);
+
+ public:
+ ParseNode* functionNode; /* back pointer used by asm.js for error messages */
+ 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 isGenexpLambda:1; /* lambda from generator expression */
+ bool hasDestructuringArgs:1; /* parameter list contains destructuring expression */
+ bool hasParameterExprs:1; /* parameter list contains expressions */
+ bool hasDirectEvalInParameterExpr:1; /* parameter list contains direct eval */
+ bool hasDuplicateParameters:1; /* parameter list contains duplicate names */
+ bool useAsm:1; /* see useAsmOrInsideUseAsm */
+ bool insideUseAsm:1; /* see useAsmOrInsideUseAsm */
+ bool isAnnexB:1; /* need to emit a synthesized Annex B assignment */
+ bool wasEmitted:1; /* Bytecode has been emitted for this function. */
+
+ // Fields for use in heuristics.
+ bool declaredArguments:1; /* the Parser declared 'arguments' */
+ 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;
+
+ FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, JSFunction* fun,
+ Directives directives, bool extraWarnings, GeneratorKind generatorKind,
+ FunctionAsyncKind asyncKind);
+
+ MutableHandle<LexicalScope::Data*> namedLambdaBindings() {
+ MOZ_ASSERT(context->compartment()->runtimeFromAnyThread()->keepAtoms());
+ return MutableHandle<LexicalScope::Data*>::fromMarkedLocation(&namedLambdaBindings_);
+ }
+
+ MutableHandle<FunctionScope::Data*> functionScopeBindings() {
+ MOZ_ASSERT(context->compartment()->runtimeFromAnyThread()->keepAtoms());
+ return MutableHandle<FunctionScope::Data*>::fromMarkedLocation(&functionScopeBindings_);
+ }
+
+ MutableHandle<VarScope::Data*> extraVarScopeBindings() {
+ MOZ_ASSERT(context->compartment()->runtimeFromAnyThread()->keepAtoms());
+ return MutableHandle<VarScope::Data*>::fromMarkedLocation(&extraVarScopeBindings_);
+ }
+
+ void initFromLazyFunction();
+ void initStandaloneFunction(Scope* enclosingScope);
+ void initWithEnclosingParseContext(ParseContext* enclosing, FunctionSyntaxKind kind);
+
+ ObjectBox* toObjectBox() override { return this; }
+ JSFunction* function() const { return &object->as<JSFunction>(); }
+
+ Scope* compilationEnclosingScope() const override {
+ // This method is used to distinguish the outermost SharedContext. If
+ // a FunctionBox is the outermost SharedContext, it must be a lazy
+ // function.
+ MOZ_ASSERT_IF(function()->isInterpretedLazy(),
+ enclosingScope_ == function()->lazyScript()->enclosingScope());
+ return enclosingScope_;
+ }
+
+ bool needsCallObjectRegardlessOfBindings() const {
+ return hasExtensibleScope() ||
+ needsHomeObject() ||
+ isDerivedClassConstructor() ||
+ isGenerator();
+ }
+
+ bool hasExtraBodyVarScope() const {
+ return hasParameterExprs &&
+ (extraVarScopeBindings_ ||
+ needsExtraBodyVarEnvironmentRegardlessOfBindings());
+ }
+
+ bool needsExtraBodyVarEnvironmentRegardlessOfBindings() const {
+ MOZ_ASSERT(hasParameterExprs);
+ return hasExtensibleScope() || isGenerator();
+ }
+
+ 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 hasExtensibleScope() const { return funCxFlags.hasExtensibleScope; }
+ 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; }
+ bool hasInnerFunctions() const { return funCxFlags.hasInnerFunctions; }
+
+ void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = 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; }
+ void setHasInnerFunctions() { funCxFlags.hasInnerFunctions = true; }
+
+ bool hasSimpleParameterList() const {
+ return !function()->hasRest() && !hasParameterExprs && !hasDestructuringArgs;
+ }
+
+ bool hasMappedArgsObj() const {
+ return !strict() && hasSimpleParameterList();
+ }
+
+ // 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;
+ tokenStream.srcCoords.lineNumAndColumnIndex(bufStart, &startLine, &startColumn);
+ }
+
+ void trace(JSTracer* trc) override;
+};
+
+inline FunctionBox*
+SharedContext::asFunctionBox()
+{
+ MOZ_ASSERT(isFunctionBox());
+ return static_cast<FunctionBox*>(this);
+}
+
+class MOZ_STACK_CLASS ModuleSharedContext : public SharedContext
+{
+ RootedModuleObject module_;
+ RootedScope enclosingScope_;
+
+ public:
+ Rooted<ModuleScope::Data*> bindings;
+ ModuleBuilder& builder;
+
+ ModuleSharedContext(ExclusiveContext* cx, ModuleObject* module, Scope* enclosingScope,
+ ModuleBuilder& builder);
+
+ HandleModuleObject module() const { return module_; }
+ Scope* compilationEnclosingScope() const override { return enclosingScope_; }
+};
+
+inline ModuleSharedContext*
+SharedContext::asModuleContext()
+{
+ MOZ_ASSERT(isModuleContext());
+ return static_cast<ModuleSharedContext*>(this);
+}
+
+// In generators, we treat all bindings as closed 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::allBindingsClosedOver()
+{
+ return bindingsAccessedDynamically() || (isFunctionBox() && asFunctionBox()->isGenerator());
+}
+
+} // namespace frontend
+} // namespace js
+
+#endif /* frontend_SharedContext_h */