SharedContext.cpp (15141B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "frontend/SharedContext.h" 8 9 #include "frontend/CompilationStencil.h" 10 #include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind 11 #include "frontend/ModuleSharedContext.h" 12 #include "frontend/ParseContext.h" 13 #include "frontend/ParseNode.h" 14 #include "frontend/ParserAtom.h" 15 #include "frontend/ScopeIndex.h" 16 #include "frontend/ScriptIndex.h" 17 #include "frontend/Stencil.h" 18 #include "js/CompileOptions.h" 19 #include "js/Vector.h" 20 #include "vm/FunctionFlags.h" // js::FunctionFlags 21 #include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind 22 #include "vm/JSScript.h" // js::FillImmutableFlagsFromCompileOptionsForTopLevel, js::FillImmutableFlagsFromCompileOptionsForFunction 23 #include "vm/StencilEnums.h" // ImmutableScriptFlagsEnum 24 25 #include "frontend/ParseContext-inl.h" 26 27 namespace js { 28 29 class ModuleBuilder; 30 31 namespace frontend { 32 33 SharedContext::SharedContext(FrontendContext* fc, Kind kind, 34 const JS::ReadOnlyCompileOptions& options, 35 Directives directives, SourceExtent extent) 36 : fc_(fc), 37 extent_(extent), 38 allowNewTarget_(false), 39 allowSuperProperty_(false), 40 allowSuperCall_(false), 41 allowArguments_(true), 42 inWith_(false), 43 inClass_(false), 44 localStrict(false), 45 hasExplicitUseStrict_(false), 46 isScriptExtraFieldCopiedToStencil(false), 47 eligibleForArgumentsLength(true) { 48 // Compute the script kind "input" flags. 49 if (kind == Kind::FunctionBox) { 50 setFlag(ImmutableFlags::IsFunction); 51 } else if (kind == Kind::Module) { 52 MOZ_ASSERT(!options.nonSyntacticScope); 53 setFlag(ImmutableFlags::IsModule); 54 } else if (kind == Kind::Eval) { 55 setFlag(ImmutableFlags::IsForEval); 56 } else { 57 MOZ_ASSERT(kind == Kind::Global); 58 } 59 60 // Initialize the transitive "input" flags. These are applied to all 61 // SharedContext in this compilation and generally cannot be determined from 62 // the source text alone. 63 if (isTopLevelContext()) { 64 js::FillImmutableFlagsFromCompileOptionsForTopLevel(options, 65 immutableFlags_); 66 } else { 67 js::FillImmutableFlagsFromCompileOptionsForFunction(options, 68 immutableFlags_); 69 } 70 71 // Initialize the strict flag. This may be updated by the parser as we observe 72 // further directives in the body. 73 setFlag(ImmutableFlags::Strict, directives.strict()); 74 } 75 76 GlobalSharedContext::GlobalSharedContext( 77 FrontendContext* fc, ScopeKind scopeKind, 78 const JS::ReadOnlyCompileOptions& options, Directives directives, 79 SourceExtent extent) 80 : SharedContext(fc, Kind::Global, options, directives, extent), 81 scopeKind_(scopeKind), 82 bindings(nullptr) { 83 MOZ_ASSERT(scopeKind == ScopeKind::Global || 84 scopeKind == ScopeKind::NonSyntactic); 85 MOZ_ASSERT(thisBinding_ == ThisBinding::Global); 86 } 87 88 EvalSharedContext::EvalSharedContext(FrontendContext* fc, 89 CompilationState& compilationState, 90 SourceExtent extent) 91 : SharedContext(fc, Kind::Eval, compilationState.input.options, 92 compilationState.directives, extent), 93 bindings(nullptr) { 94 // Eval inherits syntax and binding rules from enclosing environment. 95 allowNewTarget_ = compilationState.scopeContext.allowNewTarget; 96 allowSuperProperty_ = compilationState.scopeContext.allowSuperProperty; 97 allowSuperCall_ = compilationState.scopeContext.allowSuperCall; 98 allowArguments_ = compilationState.scopeContext.allowArguments; 99 thisBinding_ = compilationState.scopeContext.thisBinding; 100 inWith_ = compilationState.scopeContext.inWith; 101 } 102 103 SuspendableContext::SuspendableContext( 104 FrontendContext* fc, Kind kind, const JS::ReadOnlyCompileOptions& options, 105 Directives directives, SourceExtent extent, bool isGenerator, bool isAsync) 106 : SharedContext(fc, kind, options, directives, extent) { 107 setFlag(ImmutableFlags::IsGenerator, isGenerator); 108 setFlag(ImmutableFlags::IsAsync, isAsync); 109 } 110 111 FunctionBox::FunctionBox(FrontendContext* fc, SourceExtent extent, 112 CompilationState& compilationState, 113 Directives directives, GeneratorKind generatorKind, 114 FunctionAsyncKind asyncKind, bool isInitialCompilation, 115 TaggedParserAtomIndex atom, FunctionFlags flags, 116 ScriptIndex index) 117 : SuspendableContext(fc, Kind::FunctionBox, compilationState.input.options, 118 directives, extent, 119 generatorKind == GeneratorKind::Generator, 120 asyncKind == FunctionAsyncKind::AsyncFunction), 121 compilationState_(compilationState), 122 atom_(atom), 123 funcDataIndex_(index), 124 flags_(FunctionFlags::clearMutableflags(flags)), 125 emitBytecode(false), 126 wasEmittedByEnclosingScript_(false), 127 isAnnexB(false), 128 useAsm(false), 129 hasParameterExprs(false), 130 hasDestructuringArgs(false), 131 hasDuplicateParameters(false), 132 hasExprBody_(false), 133 allowReturn_(true), 134 isFunctionFieldCopiedToStencil(false), 135 isInitialCompilation(isInitialCompilation), 136 isStandalone(false) {} 137 138 void FunctionBox::initFromLazyFunction(const ScriptStencilExtra& extra, 139 ScopeContext& scopeContext, 140 FunctionSyntaxKind kind) { 141 initFromScriptStencilExtra(extra); 142 initStandaloneOrLazy(scopeContext, kind); 143 } 144 145 void FunctionBox::initFromScriptStencilExtra(const ScriptStencilExtra& extra) { 146 immutableFlags_ = extra.immutableFlags; 147 extent_ = extra.extent; 148 } 149 150 void FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, 151 FunctionSyntaxKind kind) { 152 SharedContext* sc = enclosing->sc(); 153 154 // HasModuleGoal and useAsm are inherited from enclosing context. 155 useAsm = sc->isFunctionBox() && sc->asFunctionBox()->useAsmOrInsideUseAsm(); 156 setHasModuleGoal(sc->hasModuleGoal()); 157 158 // Arrow functions don't have their own `this` binding. 159 if (flags_.isArrow()) { 160 allowNewTarget_ = sc->allowNewTarget(); 161 allowSuperProperty_ = sc->allowSuperProperty(); 162 allowSuperCall_ = sc->allowSuperCall(); 163 allowArguments_ = sc->allowArguments(); 164 thisBinding_ = sc->thisBinding(); 165 } else { 166 if (IsConstructorKind(kind)) { 167 // Record this function into the enclosing class statement so that 168 // finishClassConstructor can final processing. Due to aborted syntax 169 // parses (eg, because of asm.js), this may have already been set with an 170 // early FunctionBox. In that case, the FunctionNode should still match. 171 auto classStmt = 172 enclosing->findInnermostStatement<ParseContext::ClassStatement>(); 173 MOZ_ASSERT(classStmt); 174 MOZ_ASSERT(classStmt->constructorBox == nullptr || 175 classStmt->constructorBox->functionNode == this->functionNode); 176 classStmt->constructorBox = this; 177 } 178 179 allowNewTarget_ = true; 180 allowSuperProperty_ = flags_.allowSuperProperty(); 181 182 if (kind == FunctionSyntaxKind::DerivedClassConstructor) { 183 setDerivedClassConstructor(); 184 allowSuperCall_ = true; 185 thisBinding_ = ThisBinding::DerivedConstructor; 186 } else { 187 thisBinding_ = ThisBinding::Function; 188 } 189 190 if (kind == FunctionSyntaxKind::FieldInitializer || 191 kind == FunctionSyntaxKind::StaticClassBlock) { 192 setSyntheticFunction(); 193 allowArguments_ = false; 194 if (kind == FunctionSyntaxKind::StaticClassBlock) { 195 allowSuperCall_ = false; 196 allowReturn_ = false; 197 } 198 } 199 } 200 201 if (sc->inWith()) { 202 inWith_ = true; 203 } else { 204 auto isWith = [](ParseContext::Statement* stmt) { 205 return stmt->kind() == StatementKind::With; 206 }; 207 208 inWith_ = enclosing->findInnermostStatement(isWith); 209 } 210 211 if (sc->inClass()) { 212 inClass_ = true; 213 } else { 214 auto isClass = [](ParseContext::Statement* stmt) { 215 return stmt->kind() == StatementKind::Class; 216 }; 217 218 inClass_ = enclosing->findInnermostStatement(isClass); 219 } 220 } 221 222 void FunctionBox::initStandalone(ScopeContext& scopeContext, 223 FunctionSyntaxKind kind) { 224 initStandaloneOrLazy(scopeContext, kind); 225 226 isStandalone = true; 227 } 228 229 void FunctionBox::initStandaloneOrLazy(ScopeContext& scopeContext, 230 FunctionSyntaxKind kind) { 231 if (flags_.isArrow()) { 232 allowNewTarget_ = scopeContext.allowNewTarget; 233 allowSuperProperty_ = scopeContext.allowSuperProperty; 234 allowSuperCall_ = scopeContext.allowSuperCall; 235 allowArguments_ = scopeContext.allowArguments; 236 thisBinding_ = scopeContext.thisBinding; 237 } else { 238 allowNewTarget_ = true; 239 allowSuperProperty_ = flags_.allowSuperProperty(); 240 241 if (kind == FunctionSyntaxKind::DerivedClassConstructor) { 242 setDerivedClassConstructor(); 243 allowSuperCall_ = true; 244 thisBinding_ = ThisBinding::DerivedConstructor; 245 } else { 246 thisBinding_ = ThisBinding::Function; 247 } 248 249 if (kind == FunctionSyntaxKind::FieldInitializer) { 250 setSyntheticFunction(); 251 allowArguments_ = false; 252 } 253 } 254 255 inWith_ = scopeContext.inWith; 256 inClass_ = scopeContext.inClass; 257 } 258 259 void FunctionBox::setEnclosingScopeForInnerLazyFunction(ScopeIndex scopeIndex) { 260 // For lazy functions inside a function which is being compiled, we cache 261 // the incomplete scope object while compiling, and store it to the 262 // BaseScript once the enclosing script successfully finishes compilation 263 // in FunctionBox::finish. 264 MOZ_ASSERT(enclosingScopeIndex_.isNothing()); 265 enclosingScopeIndex_ = mozilla::Some(scopeIndex); 266 if (isFunctionFieldCopiedToStencil) { 267 copyUpdatedEnclosingScopeIndex(); 268 } 269 } 270 271 bool FunctionBox::setUseAsm() { 272 MOZ_ASSERT(!useAsm); 273 274 // Mark this function as being in "use asm". 275 useAsm = true; 276 277 // Initialize the stencil asm.js container eagerly. We do this before 278 // we validate/compile so that we can use the presence of this container to 279 // fire a use counter that works even if asm.js is disabled and the function 280 // doesn't end up being compiled with asm.js optimizations. 281 // 282 // This field is used to disable network and in-memory caching of stencils, 283 // and so we will be effectively disabling those even if this asm.js 284 // compilation fails or asm.js was disabled. Disabling asm.js is a non- 285 // standard configuration and so this is expected to be quite rare. 286 if (compilationState_.asmJS) { 287 return true; 288 } 289 compilationState_.asmJS = fc_->getAllocator()->new_<StencilAsmJSContainer>(); 290 return !!compilationState_.asmJS; 291 } 292 293 bool FunctionBox::setAsmJSModule(const JS::WasmModule* module) { 294 MOZ_ASSERT(!isFunctionFieldCopiedToStencil); 295 296 MOZ_ASSERT(flags_.kind() == FunctionFlags::NormalFunction); 297 298 // Update flags we will use to allocate the JSFunction. 299 flags_.clearBaseScript(); 300 flags_.setIsExtended(); 301 flags_.setKind(FunctionFlags::AsmJS); 302 303 // The asm.js stencil container should have been initialized in `setUseAsm` 304 // above. 305 if (!compilationState_.asmJS->moduleMap.putNew(index(), module)) { 306 js::ReportOutOfMemory(fc_); 307 return false; 308 } 309 return true; 310 } 311 312 ModuleSharedContext::ModuleSharedContext( 313 FrontendContext* fc, const JS::ReadOnlyCompileOptions& options, 314 ModuleBuilder& builder, SourceExtent extent) 315 : SuspendableContext(fc, Kind::Module, options, Directives(true), extent, 316 /* isGenerator = */ false, 317 /* isAsync = */ false), 318 bindings(nullptr), 319 builder(builder) { 320 thisBinding_ = ThisBinding::Module; 321 setFlag(ImmutableFlags::HasModuleGoal); 322 } 323 324 ScriptStencil& FunctionBox::functionStencil() const { 325 return compilationState_.scriptData[funcDataIndex_]; 326 } 327 328 ScriptStencilExtra& FunctionBox::functionExtraStencil() const { 329 return compilationState_.scriptExtra[funcDataIndex_]; 330 } 331 332 void SharedContext::copyScriptExtraFields(ScriptStencilExtra& scriptExtra) { 333 MOZ_ASSERT(!isScriptExtraFieldCopiedToStencil); 334 335 scriptExtra.immutableFlags = immutableFlags_; 336 scriptExtra.extent = extent_; 337 338 isScriptExtraFieldCopiedToStencil = true; 339 } 340 341 void FunctionBox::finishScriptFlags() { 342 MOZ_ASSERT(!isScriptExtraFieldCopiedToStencil); 343 344 using ImmutableFlags = ImmutableScriptFlagsEnum; 345 immutableFlags_.setFlag(ImmutableFlags::HasMappedArgsObj, hasMappedArgsObj()); 346 } 347 348 void FunctionBox::copyFunctionFields(ScriptStencil& script) { 349 MOZ_ASSERT(&script == &functionStencil()); 350 MOZ_ASSERT(!isFunctionFieldCopiedToStencil); 351 352 if (atom_) { 353 compilationState_.parserAtoms.markUsedByStencil(atom_, 354 ParserAtom::Atomize::Yes); 355 script.functionAtom = atom_; 356 } 357 script.functionFlags = flags_; 358 if (enclosingScopeIndex_) { 359 script.setLazyFunctionEnclosingScopeIndex(*enclosingScopeIndex_); 360 } 361 if (wasEmittedByEnclosingScript_) { 362 script.setWasEmittedByEnclosingScript(); 363 } 364 365 isFunctionFieldCopiedToStencil = true; 366 } 367 368 void FunctionBox::copyFunctionExtraFields(ScriptStencilExtra& scriptExtra) { 369 if (useMemberInitializers()) { 370 scriptExtra.setMemberInitializers(memberInitializers()); 371 } 372 373 scriptExtra.nargs = nargs_; 374 } 375 376 void FunctionBox::copyUpdatedImmutableFlags() { 377 if (isInitialCompilation) { 378 ScriptStencilExtra& scriptExtra = functionExtraStencil(); 379 scriptExtra.immutableFlags = immutableFlags_; 380 } 381 } 382 383 void FunctionBox::copyUpdatedExtent() { 384 ScriptStencilExtra& scriptExtra = functionExtraStencil(); 385 scriptExtra.extent = extent_; 386 } 387 388 void FunctionBox::copyUpdatedMemberInitializers() { 389 MOZ_ASSERT(useMemberInitializers()); 390 if (isInitialCompilation) { 391 ScriptStencilExtra& scriptExtra = functionExtraStencil(); 392 scriptExtra.setMemberInitializers(memberInitializers()); 393 } else { 394 // We are delazifying and the original PrivateScriptData has the member 395 // initializer information already. See: JSScript::fullyInitFromStencil. 396 } 397 } 398 399 void FunctionBox::copyUpdatedEnclosingScopeIndex() { 400 ScriptStencil& script = functionStencil(); 401 if (enclosingScopeIndex_) { 402 script.setLazyFunctionEnclosingScopeIndex(*enclosingScopeIndex_); 403 } 404 } 405 406 void FunctionBox::copyUpdatedAtomAndFlags() { 407 ScriptStencil& script = functionStencil(); 408 if (atom_) { 409 compilationState_.parserAtoms.markUsedByStencil(atom_, 410 ParserAtom::Atomize::Yes); 411 script.functionAtom = atom_; 412 } 413 script.functionFlags = flags_; 414 } 415 416 void FunctionBox::copyUpdatedWasEmitted() { 417 ScriptStencil& script = functionStencil(); 418 if (wasEmittedByEnclosingScript_) { 419 script.setWasEmittedByEnclosingScript(); 420 } 421 } 422 423 } // namespace frontend 424 } // namespace js