summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/SharedContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/frontend/SharedContext.cpp492
1 files changed, 492 insertions, 0 deletions
diff --git a/js/src/frontend/SharedContext.cpp b/js/src/frontend/SharedContext.cpp
new file mode 100644
index 0000000000..4ebbd03361
--- /dev/null
+++ b/js/src/frontend/SharedContext.cpp
@@ -0,0 +1,492 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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/. */
+
+#include "frontend/SharedContext.h"
+
+#include "mozilla/RefPtr.h"
+
+#include "frontend/AbstractScopePtr.h"
+#include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind
+#include "frontend/ModuleSharedContext.h"
+#include "vm/FunctionFlags.h" // js::FunctionFlags
+#include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind
+#include "vm/JSScript.h" // js::FillImmutableFlagsFromCompileOptionsForTopLevel, js::FillImmutableFlagsFromCompileOptionsForFunction
+#include "vm/StencilEnums.h" // ImmutableScriptFlagsEnum
+#include "wasm/AsmJS.h"
+#include "wasm/WasmModule.h"
+
+#include "frontend/ParseContext-inl.h"
+#include "vm/EnvironmentObject-inl.h"
+
+namespace js {
+namespace frontend {
+
+SharedContext::SharedContext(JSContext* cx, Kind kind,
+ CompilationStencil& stencil, Directives directives,
+ SourceExtent extent)
+ : cx_(cx),
+ stencil_(stencil),
+ extent_(extent),
+ allowNewTarget_(false),
+ allowSuperProperty_(false),
+ allowSuperCall_(false),
+ allowArguments_(true),
+ inWith_(false),
+ inClass_(false),
+ localStrict(false),
+ hasExplicitUseStrict_(false),
+ isScriptFieldCopiedToStencil(false) {
+ // Compute the script kind "input" flags.
+ if (kind == Kind::FunctionBox) {
+ setFlag(ImmutableFlags::IsFunction);
+ } else if (kind == Kind::Module) {
+ MOZ_ASSERT(!stencil.input.options.nonSyntacticScope);
+ setFlag(ImmutableFlags::IsModule);
+ } else if (kind == Kind::Eval) {
+ setFlag(ImmutableFlags::IsForEval);
+ } else {
+ MOZ_ASSERT(kind == Kind::Global);
+ }
+
+ // Note: This is a mix of transitive and non-transitive options.
+ const JS::ReadOnlyCompileOptions& options = stencil.input.options;
+
+ // Initialize the transitive "input" flags. These are applied to all
+ // SharedContext in this compilation and generally cannot be determined from
+ // the source text alone.
+ if (isTopLevelContext()) {
+ js::FillImmutableFlagsFromCompileOptionsForTopLevel(options,
+ immutableFlags_);
+ } else {
+ js::FillImmutableFlagsFromCompileOptionsForFunction(options,
+ immutableFlags_);
+ }
+
+ // Initialize the strict flag. This may be updated by the parser as we observe
+ // further directives in the body.
+ setFlag(ImmutableFlags::Strict, directives.strict());
+}
+
+void ScopeContext::computeThisEnvironment(Scope* scope) {
+ uint32_t envCount = 0;
+ for (ScopeIter si(scope); si; si++) {
+ if (si.kind() == ScopeKind::Function) {
+ JSFunction* fun = si.scope()->as<FunctionScope>().canonicalFunction();
+
+ // Arrow function inherit the "this" environment of the enclosing script,
+ // so continue ignore them.
+ if (!fun->isArrow()) {
+ allowNewTarget = true;
+
+ if (fun->allowSuperProperty()) {
+ allowSuperProperty = true;
+ enclosingThisEnvironmentHops = envCount;
+ }
+
+ if (fun->isClassConstructor()) {
+ memberInitializers =
+ mozilla::Some(fun->baseScript()->getMemberInitializers());
+ MOZ_ASSERT(memberInitializers->valid);
+ }
+
+ if (fun->isDerivedClassConstructor()) {
+ allowSuperCall = true;
+ }
+
+ if (fun->isFieldInitializer()) {
+ allowArguments = false;
+ }
+
+ // Found the effective "this" environment, so stop.
+ return;
+ }
+ }
+
+ if (si.scope()->hasEnvironment()) {
+ envCount++;
+ }
+ }
+}
+
+void ScopeContext::computeThisBinding(Scope* scope) {
+ // Inspect the scope-chain.
+ for (ScopeIter si(scope); si; si++) {
+ if (si.kind() == ScopeKind::Module) {
+ thisBinding = ThisBinding::Module;
+ return;
+ }
+
+ if (si.kind() == ScopeKind::Function) {
+ JSFunction* fun = si.scope()->as<FunctionScope>().canonicalFunction();
+
+ // Arrow functions don't have their own `this` binding.
+ if (fun->isArrow()) {
+ continue;
+ }
+
+ // Derived class constructors (and their nested arrow functions and evals)
+ // use ThisBinding::DerivedConstructor, which ensures TDZ checks happen
+ // when accessing |this|.
+ if (fun->isDerivedClassConstructor()) {
+ thisBinding = ThisBinding::DerivedConstructor;
+ } else {
+ thisBinding = ThisBinding::Function;
+ }
+
+ return;
+ }
+ }
+
+ thisBinding = ThisBinding::Global;
+}
+
+void ScopeContext::computeInScope(Scope* scope) {
+ for (ScopeIter si(scope); si; si++) {
+ if (si.kind() == ScopeKind::ClassBody) {
+ inClass = true;
+ }
+
+ if (si.kind() == ScopeKind::With) {
+ inWith = true;
+ }
+ }
+}
+
+/* static */
+Scope* ScopeContext::determineEffectiveScope(Scope* scope,
+ JSObject* environment) {
+ // If the scope-chain is non-syntactic, we may still determine a more precise
+ // effective-scope to use instead.
+ if (environment && scope->hasOnChain(ScopeKind::NonSyntactic)) {
+ JSObject* env = environment;
+ while (env) {
+ // Look at target of any DebugEnvironmentProxy, but be sure to use
+ // enclosingEnvironment() of the proxy itself.
+ JSObject* unwrapped = env;
+ if (env->is<DebugEnvironmentProxy>()) {
+ unwrapped = &env->as<DebugEnvironmentProxy>().environment();
+ }
+
+ if (unwrapped->is<CallObject>()) {
+ JSFunction* callee = &unwrapped->as<CallObject>().callee();
+ return callee->nonLazyScript()->bodyScope();
+ }
+
+ env = env->enclosingEnvironment();
+ }
+ }
+
+ return scope;
+}
+
+GlobalSharedContext::GlobalSharedContext(JSContext* cx, ScopeKind scopeKind,
+ CompilationStencil& stencil,
+ Directives directives,
+ SourceExtent extent)
+ : SharedContext(cx, Kind::Global, stencil, directives, extent),
+ scopeKind_(scopeKind),
+ bindings(nullptr) {
+ MOZ_ASSERT(scopeKind == ScopeKind::Global ||
+ scopeKind == ScopeKind::NonSyntactic);
+ MOZ_ASSERT(thisBinding_ == ThisBinding::Global);
+}
+
+EvalSharedContext::EvalSharedContext(JSContext* cx, CompilationStencil& stencil,
+ CompilationState& compilationState,
+ SourceExtent extent)
+ : SharedContext(cx, Kind::Eval, stencil, compilationState.directives,
+ extent),
+ bindings(nullptr) {
+ // Eval inherits syntax and binding rules from enclosing environment.
+ allowNewTarget_ = compilationState.scopeContext.allowNewTarget;
+ allowSuperProperty_ = compilationState.scopeContext.allowSuperProperty;
+ allowSuperCall_ = compilationState.scopeContext.allowSuperCall;
+ allowArguments_ = compilationState.scopeContext.allowArguments;
+ thisBinding_ = compilationState.scopeContext.thisBinding;
+ inWith_ = compilationState.scopeContext.inWith;
+}
+
+SuspendableContext::SuspendableContext(JSContext* cx, Kind kind,
+ CompilationStencil& stencil,
+ Directives directives,
+ SourceExtent extent, bool isGenerator,
+ bool isAsync)
+ : SharedContext(cx, kind, stencil, directives, extent) {
+ setFlag(ImmutableFlags::IsGenerator, isGenerator);
+ setFlag(ImmutableFlags::IsAsync, isAsync);
+}
+
+FunctionBox::FunctionBox(JSContext* cx, SourceExtent extent,
+ CompilationStencil& stencil,
+ CompilationState& compilationState,
+ Directives directives, GeneratorKind generatorKind,
+ FunctionAsyncKind asyncKind, const ParserAtom* atom,
+ FunctionFlags flags, ScriptIndex index)
+ : SuspendableContext(cx, Kind::FunctionBox, stencil, directives, extent,
+ generatorKind == GeneratorKind::Generator,
+ asyncKind == FunctionAsyncKind::AsyncFunction),
+ compilationState_(compilationState),
+ atom_(atom),
+ funcDataIndex_(index),
+ flags_(FunctionFlags::clearMutableflags(flags)),
+ emitBytecode(false),
+ wasEmitted_(false),
+ isAnnexB(false),
+ useAsm(false),
+ hasParameterExprs(false),
+ hasDestructuringArgs(false),
+ hasDuplicateParameters(false),
+ hasExprBody_(false),
+ isFunctionFieldCopiedToStencil(false) {}
+
+void FunctionBox::initFromLazyFunction(JSFunction* fun) {
+ BaseScript* lazy = fun->baseScript();
+ immutableFlags_ = lazy->immutableFlags();
+ extent_ = lazy->extent();
+}
+
+void FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing,
+ FunctionFlags flags,
+ FunctionSyntaxKind kind) {
+ SharedContext* sc = enclosing->sc();
+
+ // HasModuleGoal and useAsm are inherited from enclosing context.
+ useAsm = sc->isFunctionBox() && sc->asFunctionBox()->useAsmOrInsideUseAsm();
+ setHasModuleGoal(sc->hasModuleGoal());
+
+ // Arrow functions don't have their own `this` binding.
+ if (flags.isArrow()) {
+ allowNewTarget_ = sc->allowNewTarget();
+ allowSuperProperty_ = sc->allowSuperProperty();
+ allowSuperCall_ = sc->allowSuperCall();
+ allowArguments_ = sc->allowArguments();
+ thisBinding_ = sc->thisBinding();
+ } else {
+ if (IsConstructorKind(kind)) {
+ auto stmt =
+ enclosing->findInnermostStatement<ParseContext::ClassStatement>();
+ MOZ_ASSERT(stmt);
+ stmt->constructorBox = this;
+ }
+
+ allowNewTarget_ = true;
+ allowSuperProperty_ = flags.allowSuperProperty();
+
+ if (kind == FunctionSyntaxKind::DerivedClassConstructor) {
+ setDerivedClassConstructor();
+ allowSuperCall_ = true;
+ thisBinding_ = ThisBinding::DerivedConstructor;
+ } else {
+ thisBinding_ = ThisBinding::Function;
+ }
+
+ if (kind == FunctionSyntaxKind::FieldInitializer) {
+ setFieldInitializer();
+ allowArguments_ = false;
+ }
+ }
+
+ if (sc->inWith()) {
+ inWith_ = true;
+ } else {
+ auto isWith = [](ParseContext::Statement* stmt) {
+ return stmt->kind() == StatementKind::With;
+ };
+
+ inWith_ = enclosing->findInnermostStatement(isWith);
+ }
+
+ if (sc->inClass()) {
+ inClass_ = true;
+ } else {
+ auto isClass = [](ParseContext::Statement* stmt) {
+ return stmt->kind() == StatementKind::Class;
+ };
+
+ inClass_ = enclosing->findInnermostStatement(isClass);
+ }
+}
+
+void FunctionBox::initStandalone(ScopeContext& scopeContext,
+ FunctionFlags flags, FunctionSyntaxKind kind) {
+ if (flags.isArrow()) {
+ allowNewTarget_ = scopeContext.allowNewTarget;
+ allowSuperProperty_ = scopeContext.allowSuperProperty;
+ allowSuperCall_ = scopeContext.allowSuperCall;
+ allowArguments_ = scopeContext.allowArguments;
+ thisBinding_ = scopeContext.thisBinding;
+ } else {
+ allowNewTarget_ = true;
+ allowSuperProperty_ = flags.allowSuperProperty();
+
+ if (kind == FunctionSyntaxKind::DerivedClassConstructor) {
+ setDerivedClassConstructor();
+ allowSuperCall_ = true;
+ thisBinding_ = ThisBinding::DerivedConstructor;
+ } else {
+ thisBinding_ = ThisBinding::Function;
+ }
+
+ if (kind == FunctionSyntaxKind::FieldInitializer) {
+ setFieldInitializer();
+ allowArguments_ = false;
+ }
+ }
+
+ inWith_ = scopeContext.inWith;
+ inClass_ = scopeContext.inClass;
+}
+
+void FunctionBox::setEnclosingScopeForInnerLazyFunction(ScopeIndex scopeIndex) {
+ // For lazy functions inside a function which is being compiled, we cache
+ // the incomplete scope object while compiling, and store it to the
+ // BaseScript once the enclosing script successfully finishes compilation
+ // in FunctionBox::finish.
+ MOZ_ASSERT(enclosingScopeIndex_.isNothing());
+ enclosingScopeIndex_ = mozilla::Some(scopeIndex);
+ if (isFunctionFieldCopiedToStencil) {
+ copyUpdatedEnclosingScopeIndex();
+ }
+}
+
+bool FunctionBox::setAsmJSModule(const JS::WasmModule* module) {
+ MOZ_ASSERT(!isFunctionFieldCopiedToStencil);
+
+ MOZ_ASSERT(flags_.kind() == FunctionFlags::NormalFunction);
+
+ // Update flags we will use to allocate the JSFunction.
+ flags_.clearBaseScript();
+ flags_.setIsExtended();
+ flags_.setKind(FunctionFlags::AsmJS);
+
+ if (!stencil_.asmJS.putNew(index(), module)) {
+ js::ReportOutOfMemory(cx_);
+ return false;
+ }
+ return true;
+}
+
+ModuleSharedContext::ModuleSharedContext(JSContext* cx,
+ CompilationStencil& stencil,
+ ModuleBuilder& builder,
+ SourceExtent extent)
+ : SuspendableContext(cx, Kind::Module, stencil, Directives(true), extent,
+ /* isGenerator = */ false,
+ /* isAsync = */ false),
+ bindings(nullptr),
+ builder(builder) {
+ thisBinding_ = ThisBinding::Module;
+ setFlag(ImmutableFlags::HasModuleGoal);
+}
+
+ScriptStencil& FunctionBox::functionStencil() const {
+ return compilationState_.scriptData[funcDataIndex_];
+}
+
+ScriptStencilExtra& FunctionBox::functionExtraStencil() const {
+ return compilationState_.scriptExtra[funcDataIndex_];
+}
+
+bool FunctionBox::hasFunctionExtraStencil() const {
+ return funcDataIndex_ < compilationState_.scriptExtra.length();
+}
+
+void SharedContext::copyScriptFields(ScriptStencil& script) {
+ MOZ_ASSERT(!isScriptFieldCopiedToStencil);
+ isScriptFieldCopiedToStencil = true;
+}
+
+void SharedContext::copyScriptExtraFields(ScriptStencilExtra& scriptExtra) {
+ scriptExtra.immutableFlags = immutableFlags_;
+ scriptExtra.extent = extent_;
+}
+
+void FunctionBox::finishScriptFlags() {
+ MOZ_ASSERT(!isScriptFieldCopiedToStencil);
+
+ using ImmutableFlags = ImmutableScriptFlagsEnum;
+ immutableFlags_.setFlag(ImmutableFlags::HasMappedArgsObj, hasMappedArgsObj());
+}
+
+void FunctionBox::copyScriptFields(ScriptStencil& script) {
+ MOZ_ASSERT(&script == &functionStencil());
+
+ SharedContext::copyScriptFields(script);
+
+ if (memberInitializers_) {
+ script.setMemberInitializers(*memberInitializers_);
+ }
+
+ isScriptFieldCopiedToStencil = true;
+}
+
+void FunctionBox::copyFunctionFields(ScriptStencil& script) {
+ MOZ_ASSERT(&script == &functionStencil());
+ MOZ_ASSERT(!isFunctionFieldCopiedToStencil);
+
+ if (atom_) {
+ atom_->markUsedByStencil();
+ script.functionAtom = atom_->toIndex();
+ }
+ script.functionFlags = flags_;
+ if (enclosingScopeIndex_) {
+ script.setLazyFunctionEnclosingScopeIndex(*enclosingScopeIndex_);
+ }
+ if (wasEmitted_) {
+ script.setWasFunctionEmitted();
+ }
+
+ isFunctionFieldCopiedToStencil = true;
+}
+
+void FunctionBox::copyFunctionExtraFields(ScriptStencilExtra& scriptExtra) {
+ scriptExtra.nargs = nargs_;
+}
+
+void FunctionBox::copyUpdatedImmutableFlags() {
+ if (hasFunctionExtraStencil()) {
+ ScriptStencilExtra& scriptExtra = functionExtraStencil();
+ scriptExtra.immutableFlags = immutableFlags_;
+ }
+}
+
+void FunctionBox::copyUpdatedExtent() {
+ ScriptStencilExtra& scriptExtra = functionExtraStencil();
+ scriptExtra.extent = extent_;
+}
+
+void FunctionBox::copyUpdatedMemberInitializers() {
+ ScriptStencil& script = functionStencil();
+ if (memberInitializers_) {
+ script.setMemberInitializers(*memberInitializers_);
+ }
+}
+
+void FunctionBox::copyUpdatedEnclosingScopeIndex() {
+ ScriptStencil& script = functionStencil();
+ if (enclosingScopeIndex_) {
+ script.setLazyFunctionEnclosingScopeIndex(*enclosingScopeIndex_);
+ }
+}
+
+void FunctionBox::copyUpdatedAtomAndFlags() {
+ ScriptStencil& script = functionStencil();
+ if (atom_) {
+ atom_->markUsedByStencil();
+ script.functionAtom = atom_->toIndex();
+ }
+ script.functionFlags = flags_;
+}
+
+void FunctionBox::copyUpdatedWasEmitted() {
+ ScriptStencil& script = functionStencil();
+ if (wasEmitted_) {
+ script.setWasFunctionEmitted();
+ }
+}
+
+} // namespace frontend
+} // namespace js