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/AbstractScopePtr.h"
10 #include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind
11 #include "frontend/ModuleSharedContext.h"
12 #include "vm/FunctionFlags.h" // js::FunctionFlags
13 #include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind
14 #include "wasm/AsmJS.h"
15
16 #include "frontend/ParseContext-inl.h"
17 #include "vm/EnvironmentObject-inl.h"
18
19 namespace js {
20 namespace frontend {
21
SharedContext(JSContext * cx,Kind kind,CompilationInfo & compilationInfo,Directives directives,SourceExtent extent)22 SharedContext::SharedContext(JSContext* cx, Kind kind,
23 CompilationInfo& compilationInfo,
24 Directives directives, SourceExtent extent)
25 : cx_(cx),
26 compilationInfo_(compilationInfo),
27 extent(extent),
28 allowNewTarget_(false),
29 allowSuperProperty_(false),
30 allowSuperCall_(false),
31 allowArguments_(true),
32 inWith_(false),
33 localStrict(false),
34 hasExplicitUseStrict_(false) {
35 // Compute the script kind "input" flags.
36 if (kind == Kind::FunctionBox) {
37 setFlag(ImmutableFlags::IsFunction);
38 } else if (kind == Kind::Module) {
39 MOZ_ASSERT(!compilationInfo.options.nonSyntacticScope);
40 setFlag(ImmutableFlags::IsModule);
41 } else if (kind == Kind::Eval) {
42 setFlag(ImmutableFlags::IsForEval);
43 } else {
44 MOZ_ASSERT(kind == Kind::Global);
45 }
46
47 // Note: This is a mix of transitive and non-transitive options.
48 const JS::ReadOnlyCompileOptions& options = compilationInfo.options;
49
50 // Initialize the transitive "input" flags. These are applied to all
51 // SharedContext in this compilation and generally cannot be determined from
52 // the source text alone.
53 setFlag(ImmutableFlags::SelfHosted, options.selfHostingMode);
54 setFlag(ImmutableFlags::ForceStrict, options.forceStrictMode());
55 setFlag(ImmutableFlags::HasNonSyntacticScope, options.nonSyntacticScope);
56
57 // Initialize the non-transistive "input" flags if this is a top-level.
58 if (isTopLevelContext()) {
59 setFlag(ImmutableFlags::TreatAsRunOnce, options.isRunOnce);
60 setFlag(ImmutableFlags::NoScriptRval, options.noScriptRval);
61 }
62
63 // Initialize the strict flag. This may be updated by the parser as we observe
64 // further directives in the body.
65 setFlag(ImmutableFlags::Strict, directives.strict());
66 }
67
computeAllowSyntax(Scope * scope)68 void ScopeContext::computeAllowSyntax(Scope* scope) {
69 for (ScopeIter si(scope); si; si++) {
70 if (si.kind() == ScopeKind::Function) {
71 FunctionScope* funScope = &si.scope()->as<FunctionScope>();
72 JSFunction* fun = funScope->canonicalFunction();
73
74 // Arrow function inherit syntax restrictions of enclosing scope.
75 if (fun->isArrow()) {
76 continue;
77 }
78
79 allowNewTarget = true;
80 allowSuperProperty = fun->allowSuperProperty();
81
82 if (fun->isDerivedClassConstructor()) {
83 allowSuperCall = true;
84 }
85
86 if (fun->isFieldInitializer()) {
87 allowArguments = false;
88 }
89
90 return;
91 }
92 }
93 }
94
computeThisBinding(Scope * scope,JSObject * environment)95 void ScopeContext::computeThisBinding(Scope* scope, JSObject* environment) {
96 // If the scope-chain is non-syntactic, we may still determine a more precise
97 // effective-scope to use instead.
98 Scope* effectiveScope = scope;
99
100 // If this eval is in response to Debugger.Frame.eval, we may have been
101 // passed an incomplete scope chain. In order to better determine the 'this'
102 // binding type, we traverse the environment chain, looking for a CallObject
103 // and recompute the binding type based on its body scope.
104 //
105 // NOTE: A non-debug eval in a non-syntactic environment will also trigger
106 // this code. In that case, we should still compute the same binding type.
107 if (environment && scope->hasOnChain(ScopeKind::NonSyntactic)) {
108 JSObject* env = environment;
109 while (env) {
110 // Look at target of any DebugEnvironmentProxy, but be sure to use
111 // enclosingEnvironment() of the proxy itself.
112 JSObject* unwrapped = env;
113 if (env->is<DebugEnvironmentProxy>()) {
114 unwrapped = &env->as<DebugEnvironmentProxy>().environment();
115 }
116
117 if (unwrapped->is<CallObject>()) {
118 JSFunction* callee = &unwrapped->as<CallObject>().callee();
119 effectiveScope = callee->nonLazyScript()->bodyScope();
120 break;
121 }
122
123 env = env->enclosingEnvironment();
124 }
125 }
126
127 // Inspect the scope-chain.
128 for (ScopeIter si(effectiveScope); si; si++) {
129 if (si.kind() == ScopeKind::Module) {
130 thisBinding = ThisBinding::Module;
131 return;
132 }
133
134 if (si.kind() == ScopeKind::Function) {
135 JSFunction* fun = si.scope()->as<FunctionScope>().canonicalFunction();
136
137 // Arrow functions don't have their own `this` binding.
138 if (fun->isArrow()) {
139 continue;
140 }
141
142 // Derived class constructors (and their nested arrow functions and evals)
143 // use ThisBinding::DerivedConstructor, which ensures TDZ checks happen
144 // when accessing |this|.
145 if (fun->isDerivedClassConstructor()) {
146 thisBinding = ThisBinding::DerivedConstructor;
147 } else {
148 thisBinding = ThisBinding::Function;
149 }
150
151 return;
152 }
153 }
154
155 thisBinding = ThisBinding::Global;
156 }
157
computeInWith(Scope * scope)158 void ScopeContext::computeInWith(Scope* scope) {
159 for (ScopeIter si(scope); si; si++) {
160 if (si.kind() == ScopeKind::With) {
161 inWith = true;
162 break;
163 }
164 }
165 }
166
computeExternalInitializers(Scope * scope)167 void ScopeContext::computeExternalInitializers(Scope* scope) {
168 for (ScopeIter si(scope); si; si++) {
169 if (si.scope()->is<FunctionScope>()) {
170 FunctionScope& funcScope = si.scope()->as<FunctionScope>();
171 JSFunction* fun = funcScope.canonicalFunction();
172
173 // Arrows can call `super()` on behalf on parent so keep searching.
174 if (fun->isArrow()) {
175 continue;
176 }
177
178 if (fun->isClassConstructor()) {
179 fieldInitializers =
180 mozilla::Some(fun->baseScript()->getFieldInitializers());
181 MOZ_ASSERT(fieldInitializers->valid);
182 }
183
184 break;
185 }
186 }
187 }
188
EvalSharedContext(JSContext * cx,CompilationInfo & compilationInfo,Scope * enclosingScope,Directives directives,SourceExtent extent)189 EvalSharedContext::EvalSharedContext(JSContext* cx,
190 CompilationInfo& compilationInfo,
191 Scope* enclosingScope,
192 Directives directives, SourceExtent extent)
193 : SharedContext(cx, Kind::Eval, compilationInfo, directives, extent),
194 enclosingScope_(cx, enclosingScope),
195 bindings(cx) {
196 // Eval inherits syntax and binding rules from enclosing environment.
197 allowNewTarget_ = compilationInfo.scopeContext.allowNewTarget;
198 allowSuperProperty_ = compilationInfo.scopeContext.allowSuperProperty;
199 allowSuperCall_ = compilationInfo.scopeContext.allowSuperCall;
200 allowArguments_ = compilationInfo.scopeContext.allowArguments;
201 thisBinding_ = compilationInfo.scopeContext.thisBinding;
202 inWith_ = compilationInfo.scopeContext.inWith;
203 }
204
205 #ifdef DEBUG
atomsAreKept()206 bool FunctionBox::atomsAreKept() { return cx_->zone()->hasKeptAtoms(); }
207 #endif
208
FunctionBox(JSContext * cx,FunctionBox * traceListHead,SourceExtent extent,CompilationInfo & compilationInfo,Directives directives,GeneratorKind generatorKind,FunctionAsyncKind asyncKind,JSAtom * atom,FunctionFlags flags,size_t index)209 FunctionBox::FunctionBox(JSContext* cx, FunctionBox* traceListHead,
210 SourceExtent extent, CompilationInfo& compilationInfo,
211 Directives directives, GeneratorKind generatorKind,
212 FunctionAsyncKind asyncKind, JSAtom* atom,
213 FunctionFlags flags, size_t index)
214 : SharedContext(cx, Kind::FunctionBox, compilationInfo, directives, extent),
215 traceLink_(traceListHead),
216 atom_(atom),
217 funcDataIndex_(index),
218 flags_(flags),
219 emitBytecode(false),
220 wasEmitted(false),
221 isSingleton(false),
222 isAnnexB(false),
223 useAsm(false),
224 isAsmJSModule_(false),
225 hasParameterExprs(false),
226 hasDestructuringArgs(false),
227 hasDuplicateParameters(false),
228 hasExprBody_(false),
229 usesApply(false),
230 usesThis(false),
231 usesReturn(false) {
232 setFlag(ImmutableFlags::IsGenerator,
233 generatorKind == GeneratorKind::Generator);
234 setFlag(ImmutableFlags::IsAsync,
235 asyncKind == FunctionAsyncKind::AsyncFunction);
236 }
237
createFunction(JSContext * cx)238 JSFunction* FunctionBox::createFunction(JSContext* cx) {
239 RootedObject proto(cx);
240 if (!GetFunctionPrototype(cx, generatorKind(), asyncKind(), &proto)) {
241 return nullptr;
242 }
243
244 RootedAtom atom(cx, displayAtom());
245 gc::AllocKind allocKind = flags_.isExtended()
246 ? gc::AllocKind::FUNCTION_EXTENDED
247 : gc::AllocKind::FUNCTION;
248
249 return NewFunctionWithProto(cx, nullptr, nargs_, flags_, nullptr, atom, proto,
250 allocKind, TenuredObject);
251 }
252
hasFunction() const253 bool FunctionBox::hasFunction() const {
254 return compilationInfo_.functions[funcDataIndex_] != nullptr;
255 }
256
initFromLazyFunction(JSFunction * fun)257 void FunctionBox::initFromLazyFunction(JSFunction* fun) {
258 BaseScript* lazy = fun->baseScript();
259 immutableFlags_ = lazy->immutableFlags();
260 extent = lazy->extent();
261
262 if (fun->isClassConstructor()) {
263 fieldInitializers = mozilla::Some(lazy->getFieldInitializers());
264 }
265 }
266
initWithEnclosingParseContext(ParseContext * enclosing,FunctionFlags flags,FunctionSyntaxKind kind)267 void FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing,
268 FunctionFlags flags,
269 FunctionSyntaxKind kind) {
270 SharedContext* sc = enclosing->sc();
271
272 // HasModuleGoal and useAsm are inherited from enclosing context.
273 useAsm = sc->isFunctionBox() && sc->asFunctionBox()->useAsmOrInsideUseAsm();
274 setHasModuleGoal(sc->hasModuleGoal());
275
276 // Arrow functions don't have their own `this` binding.
277 if (flags.isArrow()) {
278 allowNewTarget_ = sc->allowNewTarget();
279 allowSuperProperty_ = sc->allowSuperProperty();
280 allowSuperCall_ = sc->allowSuperCall();
281 allowArguments_ = sc->allowArguments();
282 thisBinding_ = sc->thisBinding();
283 } else {
284 if (IsConstructorKind(kind)) {
285 auto stmt =
286 enclosing->findInnermostStatement<ParseContext::ClassStatement>();
287 MOZ_ASSERT(stmt);
288 stmt->constructorBox = this;
289 }
290
291 allowNewTarget_ = true;
292 allowSuperProperty_ = flags.allowSuperProperty();
293
294 if (kind == FunctionSyntaxKind::DerivedClassConstructor) {
295 setDerivedClassConstructor();
296 allowSuperCall_ = true;
297 thisBinding_ = ThisBinding::DerivedConstructor;
298 } else {
299 thisBinding_ = ThisBinding::Function;
300 }
301
302 if (kind == FunctionSyntaxKind::FieldInitializer) {
303 setFieldInitializer();
304 allowArguments_ = false;
305 }
306 }
307
308 if (sc->inWith()) {
309 inWith_ = true;
310 } else {
311 auto isWith = [](ParseContext::Statement* stmt) {
312 return stmt->kind() == StatementKind::With;
313 };
314
315 inWith_ = enclosing->findInnermostStatement(isWith);
316 }
317 }
318
initWithEnclosingScope(ScopeContext & scopeContext,Scope * enclosingScope,FunctionFlags flags,FunctionSyntaxKind kind)319 void FunctionBox::initWithEnclosingScope(ScopeContext& scopeContext,
320 Scope* enclosingScope,
321 FunctionFlags flags,
322 FunctionSyntaxKind kind) {
323 MOZ_ASSERT(enclosingScope);
324 enclosingScope_ = AbstractScopePtr(enclosingScope);
325
326 if (flags.isArrow()) {
327 allowNewTarget_ = scopeContext.allowNewTarget;
328 allowSuperProperty_ = scopeContext.allowSuperProperty;
329 allowSuperCall_ = scopeContext.allowSuperCall;
330 allowArguments_ = scopeContext.allowArguments;
331 thisBinding_ = scopeContext.thisBinding;
332 } else {
333 allowNewTarget_ = true;
334 allowSuperProperty_ = flags.allowSuperProperty();
335
336 if (kind == FunctionSyntaxKind::DerivedClassConstructor) {
337 setDerivedClassConstructor();
338 allowSuperCall_ = true;
339 thisBinding_ = ThisBinding::DerivedConstructor;
340 } else {
341 thisBinding_ = ThisBinding::Function;
342 }
343
344 if (kind == FunctionSyntaxKind::FieldInitializer) {
345 setFieldInitializer();
346 allowArguments_ = false;
347 }
348 }
349
350 inWith_ = scopeContext.inWith;
351 }
352
setEnclosingScopeForInnerLazyFunction(const AbstractScopePtr & enclosingScope)353 void FunctionBox::setEnclosingScopeForInnerLazyFunction(
354 const AbstractScopePtr& enclosingScope) {
355 // For lazy functions inside a function which is being compiled, we cache
356 // the incomplete scope object while compiling, and store it to the
357 // BaseScript once the enclosing script successfully finishes compilation
358 // in FunctionBox::finish.
359 MOZ_ASSERT(!enclosingScope_);
360 enclosingScope_ = enclosingScope;
361 }
362
setAsmJSModule(JSFunction * function)363 void FunctionBox::setAsmJSModule(JSFunction* function) {
364 MOZ_ASSERT(IsAsmJSModule(function));
365 isAsmJSModule_ = true;
366 clobberFunction(function);
367 }
368
finish()369 void FunctionBox::finish() {
370 if (emitBytecode || isAsmJSModule()) {
371 // Non-lazy inner functions don't use the enclosingScope_ field.
372 MOZ_ASSERT(!enclosingScope_);
373 } else {
374 // Apply updates from FunctionEmitter::emitLazy().
375 BaseScript* script = function()->baseScript();
376
377 script->setEnclosingScope(enclosingScope_.getExistingScope());
378 script->initTreatAsRunOnce(treatAsRunOnce());
379
380 if (fieldInitializers) {
381 script->setFieldInitializers(*fieldInitializers);
382 }
383 }
384
385 // Inferred and Guessed names are computed by BytecodeEmitter and so may need
386 // to be applied to existing JSFunctions during delazification.
387 if (function()->displayAtom() == nullptr) {
388 if (hasInferredName()) {
389 function()->setInferredName(atom_);
390 }
391
392 if (hasGuessedAtom()) {
393 function()->setGuessedAtom(atom_);
394 }
395 }
396 }
397
398 /* static */
TraceList(JSTracer * trc,FunctionBox * listHead)399 void FunctionBox::TraceList(JSTracer* trc, FunctionBox* listHead) {
400 for (FunctionBox* node = listHead; node; node = node->traceLink_) {
401 node->trace(trc);
402 }
403 }
404
trace(JSTracer * trc)405 void FunctionBox::trace(JSTracer* trc) {
406 if (enclosingScope_) {
407 enclosingScope_.trace(trc);
408 }
409 if (atom_) {
410 TraceRoot(trc, &atom_, "funbox-atom");
411 }
412 }
413
function() const414 JSFunction* FunctionBox::function() const {
415 return compilationInfo_.functions[funcDataIndex_];
416 }
417
clobberFunction(JSFunction * function)418 void FunctionBox::clobberFunction(JSFunction* function) {
419 compilationInfo_.functions[funcDataIndex_].set(function);
420 // After clobbering, these flags need to be updated
421 setIsInterpreted(function->isInterpreted());
422 }
423
ModuleSharedContext(JSContext * cx,ModuleObject * module,CompilationInfo & compilationInfo,Scope * enclosingScope,ModuleBuilder & builder,SourceExtent extent)424 ModuleSharedContext::ModuleSharedContext(JSContext* cx, ModuleObject* module,
425 CompilationInfo& compilationInfo,
426 Scope* enclosingScope,
427 ModuleBuilder& builder,
428 SourceExtent extent)
429 : SharedContext(cx, Kind::Module, compilationInfo, Directives(true),
430 extent),
431 module_(cx, module),
432 enclosingScope_(cx, enclosingScope),
433 bindings(cx),
434 builder(builder) {
435 thisBinding_ = ThisBinding::Module;
436 setFlag(ImmutableFlags::HasModuleGoal);
437 }
438
functionStencil() const439 MutableHandle<ScriptStencil> FunctionBox::functionStencil() const {
440 return compilationInfo_.funcData[funcDataIndex_];
441 }
442
443 } // namespace frontend
444 } // namespace js
445