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