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/BinASTParserPerTokenizer.h"
8 
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/Casting.h"
11 #include "mozilla/Maybe.h"
12 #include "mozilla/PodOperations.h"
13 #include "mozilla/ScopeExit.h"
14 #include "mozilla/Vector.h"
15 
16 #include <utility>
17 
18 #include "frontend/BinAST-macros.h"
19 #include "frontend/BinASTParser.h"
20 #include "frontend/BinASTTokenReaderContext.h"
21 #include "frontend/BinASTTokenReaderMultipart.h"
22 #include "frontend/FullParseHandler.h"
23 #include "frontend/FunctionSyntaxKind.h"  // FunctionSyntaxKind
24 #include "frontend/ParseNode.h"
25 #include "frontend/Parser.h"
26 #include "frontend/SharedContext.h"
27 #include "js/Result.h"
28 #include "vm/FunctionFlags.h"          // js::FunctionFlags
29 #include "vm/GeneratorAndAsyncKind.h"  // js::GeneratorKind, js::FunctionAsyncKind
30 #include "vm/RegExpObject.h"
31 
32 #include "frontend/ParseContext-inl.h"
33 #include "frontend/SharedContext-inl.h"
34 #include "vm/JSContext-inl.h"
35 
36 // # About compliance with EcmaScript
37 //
38 // For the moment, this parser implements ES5. Future versions will be extended
39 // to ES6 and further on.
40 //
41 // By design, it does NOT implement Annex B.3.3. If possible, we would like
42 // to avoid going down that rabbit hole.
43 //
44 //
45 // # About the AST
46 //
47 // At this stage of experimentation, the AST specifications change often. This
48 // version of the parser attempts to implement
49 // https://gist.github.com/Yoric/2390f0367515c079172be2526349b294
50 //
51 //
52 // # About validating the AST
53 //
54 // Normally, this implementation validates all properties of the AST *except*
55 // the order of fields, which is partially constrained by the AST spec (e.g. in
56 // a block, field `scope` must appear before field `body`, etc.).
57 //
58 //
59 // # About names and scopes
60 //
61 // One of the key objectives of the BinAST syntax is to be able to entirely skip
62 // parsing inner functions until they are needed. With a purely syntactic AST,
63 // this is generally impossible, as we would need to walk the AST to find
64 // lexically-bound/var-bound variables, instances of direct eval, etc.
65 //
66 // To achieve this, BinAST files contain scope data, as instances of
67 // `BinJS:Scope` nodes. Rather than walking the AST to assign bindings
68 // to scopes, we extract data from the `BinJS:Scope` and check it lazily,
69 // once we actually need to walk the AST.
70 //
71 // WARNING: The current implementation DOES NOT perform the check yet. It
72 // is therefore unsafe.
73 //
74 // # About directives
75 //
76 // Currently, directives are ignored and treated as regular strings.
77 //
78 // They should be treated lazily (whenever we open a subscope), like bindings.
79 
80 namespace js::frontend {
81 
82 using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
83 
84 // ------------- Toplevel constructions
85 
86 template <typename Tok>
BinASTParserPerTokenizer(JSContext * cx,CompilationInfo & compilationInfo,const JS::ReadOnlyCompileOptions & options,Handle<BaseScript * > lazyScript)87 BinASTParserPerTokenizer<Tok>::BinASTParserPerTokenizer(
88     JSContext* cx, CompilationInfo& compilationInfo,
89     const JS::ReadOnlyCompileOptions& options,
90     Handle<BaseScript*> lazyScript /* = nullptr */)
91     : BinASTParserBase(cx, compilationInfo),
92       options_(options),
93       lazyScript_(cx, lazyScript),
94       handler_(cx, compilationInfo.allocScope.alloc(), nullptr,
95                SourceKind::Binary),
96       variableDeclarationKind_(VariableDeclarationKind::Var) {
97   MOZ_ASSERT_IF(lazyScript_, lazyScript_->isBinAST());
98 }
99 
100 template <typename Tok>
parse(GlobalSharedContext * globalsc,const Vector<uint8_t> & data,BinASTSourceMetadata ** metadataPtr)101 JS::Result<ParseNode*> BinASTParserPerTokenizer<Tok>::parse(
102     GlobalSharedContext* globalsc, const Vector<uint8_t>& data,
103     BinASTSourceMetadata** metadataPtr) {
104   return parse(globalsc, data.begin(), data.length(), metadataPtr);
105 }
106 
107 template <typename Tok>
parse(GlobalSharedContext * globalsc,const uint8_t * start,const size_t length,BinASTSourceMetadata ** metadataPtr)108 JS::Result<ParseNode*> BinASTParserPerTokenizer<Tok>::parse(
109     GlobalSharedContext* globalsc, const uint8_t* start, const size_t length,
110     BinASTSourceMetadata** metadataPtr) {
111   auto result = parseAux(globalsc, start, length, metadataPtr);
112   poison();  // Make sure that the parser is never used again accidentally.
113   return result;
114 }
115 
116 template <typename Tok>
parseAux(GlobalSharedContext * globalsc,const uint8_t * start,const size_t length,BinASTSourceMetadata ** metadataPtr)117 JS::Result<ParseNode*> BinASTParserPerTokenizer<Tok>::parseAux(
118     GlobalSharedContext* globalsc, const uint8_t* start, const size_t length,
119     BinASTSourceMetadata** metadataPtr) {
120   MOZ_ASSERT(globalsc);
121 
122   tokenizer_.emplace(cx_, this, start, length);
123 
124   BinASTParseContext globalpc(cx_, this, globalsc,
125                               /* newDirectives = */ nullptr);
126   if (MOZ_UNLIKELY(!globalpc.init())) {
127     return cx_->alreadyReportedError();
128   }
129 
130   ParseContext::VarScope varScope(cx_, &globalpc, usedNames_);
131   if (MOZ_UNLIKELY(!varScope.init(&globalpc))) {
132     return cx_->alreadyReportedError();
133   }
134 
135   MOZ_TRY(tokenizer_->readHeader());
136 
137   ParseNode* result(nullptr);
138   const auto topContext = RootContext();
139   MOZ_TRY_VAR(result, asFinalParser()->parseProgram(topContext));
140 
141   MOZ_TRY(tokenizer_->readTreeFooter());
142 
143   mozilla::Maybe<GlobalScope::Data*> bindings =
144       NewGlobalScopeData(cx_, varScope, alloc_, pc_);
145   if (MOZ_UNLIKELY(!bindings)) {
146     return cx_->alreadyReportedError();
147   }
148   globalsc->bindings = *bindings;
149 
150   if (metadataPtr) {
151     *metadataPtr = tokenizer_->takeMetadata();
152   }
153 
154   return result;  // Magic conversion to Ok.
155 }
156 
157 template <typename Tok>
parseLazyFunction(ScriptSource * scriptSource,const size_t firstOffset)158 JS::Result<FunctionNode*> BinASTParserPerTokenizer<Tok>::parseLazyFunction(
159     ScriptSource* scriptSource, const size_t firstOffset) {
160   MOZ_ASSERT(lazyScript_);
161   MOZ_ASSERT(scriptSource->length() > firstOffset);
162 
163   tokenizer_.emplace(cx_, this, scriptSource->binASTSource(),
164                      scriptSource->length());
165 
166   MOZ_TRY(tokenizer_->initFromScriptSource(scriptSource));
167 
168   tokenizer_->seek(firstOffset);
169 
170   // For now, only function declarations and function expression are supported.
171   RootedFunction func(cx_, lazyScript_->function());
172   bool isExpr = func->isLambda();
173   MOZ_ASSERT(func->kind() == FunctionFlags::FunctionKind::NormalFunction);
174 
175   // Poison the tokenizer when we leave to ensure that it's not used again by
176   // accident.
177   auto onExit = mozilla::MakeScopeExit([&]() { poison(); });
178 
179   // TODO: This should be actually shared with the auto-generated version.
180 
181   auto syntaxKind =
182       isExpr ? FunctionSyntaxKind::Expression : FunctionSyntaxKind::Statement;
183   BINJS_MOZ_TRY_DECL(
184       funbox, buildFunctionBox(lazyScript_->generatorKind(),
185                                lazyScript_->asyncKind(), syntaxKind, nullptr));
186 
187   // Push a new ParseContext. It will be used to parse `scope`, the arguments,
188   // the function.
189   BinASTParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
190   BINJS_TRY(funpc.init());
191   pc_->functionScope().useAsVarScope(pc_);
192   MOZ_ASSERT(pc_->isFunctionBox());
193 
194   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
195   BINJS_TRY(lexicalScope.init(pc_));
196   ListNode* params;
197   ListNode* tmpBody;
198   auto parseFunc = isExpr ? &FinalParser::parseFunctionExpressionContents
199                           : &FinalParser::parseFunctionOrMethodContents;
200 
201   // Inject a toplevel context (i.e. no parent) to parse the lazy content.
202   // In the future, we may move this to a more specific context.
203   const auto context = FieldOrRootContext(RootContext());
204   MOZ_TRY(
205       (asFinalParser()->*parseFunc)(func->nargs(), &params, &tmpBody, context));
206 
207   uint32_t nargs = params->count();
208 
209   BINJS_TRY_DECL(lexicalScopeData,
210                  NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
211   BINJS_TRY_DECL(body, handler_.newLexicalScope(*lexicalScopeData, tmpBody));
212 
213   BINJS_MOZ_TRY_DECL(result,
214                      makeEmptyFunctionNode(firstOffset, syntaxKind, funbox));
215   MOZ_TRY(setFunctionParametersAndBody(result, params, body));
216   MOZ_TRY(finishEagerFunction(funbox, nargs));
217   return result;
218 }
219 
220 template <typename Tok>
forceStrictIfNecessary(SharedContext * sc,ListNode * directives)221 void BinASTParserPerTokenizer<Tok>::forceStrictIfNecessary(
222     SharedContext* sc, ListNode* directives) {
223   JSAtom* useStrict = cx_->names().useStrict;
224 
225   for (const ParseNode* directive : directives->contents()) {
226     if (directive->as<NameNode>().atom() == useStrict) {
227       sc->setStrictScript();
228       break;
229     }
230   }
231 }
232 
233 template <typename Tok>
buildFunctionBox(GeneratorKind generatorKind,FunctionAsyncKind functionAsyncKind,FunctionSyntaxKind syntax,ParseNode * name)234 JS::Result<FunctionBox*> BinASTParserPerTokenizer<Tok>::buildFunctionBox(
235     GeneratorKind generatorKind, FunctionAsyncKind functionAsyncKind,
236     FunctionSyntaxKind syntax, ParseNode* name) {
237   MOZ_ASSERT_IF(!pc_, lazyScript_);
238 
239   RootedFunction fun(cx_);
240   RootedAtom atom(cx_);
241   FunctionFlags flags;
242 
243   // Name might be any of {Identifier,ComputedPropertyName,LiteralPropertyName}
244   if (name && name->is<NameNode>()) {
245     atom = name->as<NameNode>().atom();
246   }
247 
248   if (pc_) {
249     if (syntax == FunctionSyntaxKind::Statement) {
250       auto ptr = pc_->varScope().lookupDeclaredName(atom);
251       if (MOZ_UNLIKELY(!ptr)) {
252         return raiseError(
253             "FunctionDeclaration without corresponding AssertedDeclaredName.");
254       }
255 
256       DeclarationKind declaredKind = ptr->value()->kind();
257       if (DeclarationKindIsVar(declaredKind)) {
258         if (MOZ_UNLIKELY(!pc_->atBodyLevel())) {
259           return raiseError(
260               "body-level FunctionDeclaration inside non-body-level context.");
261         }
262         RedeclareVar(ptr, DeclarationKind::BodyLevelFunction);
263       }
264     }
265 
266     flags = InitialFunctionFlags(syntax, generatorKind, functionAsyncKind);
267   } else {
268     fun = lazyScript_->function();
269 
270     atom = fun->displayAtom();
271     flags = fun->flags();
272   }
273 
274   mozilla::Maybe<Directives> directives;
275   if (pc_) {
276     directives.emplace(pc_);
277   } else {
278     directives.emplace(lazyScript_->strict());
279   }
280 
281   size_t index = this->getCompilationInfo().funcData.length();
282   if (!this->getCompilationInfo().functions.emplaceBack(fun)) {
283     return nullptr;
284   }
285   if (!this->getCompilationInfo().funcData.emplaceBack(cx_)) {
286     return nullptr;
287   }
288 
289   // This extent will be further filled in during parse.
290   SourceExtent extent;
291   auto* funbox = alloc_.new_<FunctionBox>(
292       cx_, compilationInfo_.traceListHead, extent, getCompilationInfo(),
293       *directives, generatorKind, functionAsyncKind, atom, flags, index);
294   if (MOZ_UNLIKELY(!funbox)) {
295     return raiseOOM();
296   }
297 
298   compilationInfo_.traceListHead = funbox;
299   if (pc_) {
300     funbox->initWithEnclosingParseContext(pc_, flags, syntax);
301   } else {
302     frontend::ScopeContext scopeContext(fun->enclosingScope());
303     funbox->initFromLazyFunction(fun);
304     funbox->initWithEnclosingScope(scopeContext, fun->enclosingScope(), flags,
305                                    syntax);
306   }
307   return funbox;
308 }
309 
310 template <typename Tok>
makeEmptyFunctionNode(const size_t start,const FunctionSyntaxKind syntaxKind,FunctionBox * funbox)311 JS::Result<FunctionNode*> BinASTParserPerTokenizer<Tok>::makeEmptyFunctionNode(
312     const size_t start, const FunctionSyntaxKind syntaxKind,
313     FunctionBox* funbox) {
314   // Lazy script compilation requires basically none of the fields filled out.
315   TokenPos pos = tokenizer_->pos(start);
316 
317   BINJS_TRY_DECL(result, handler_.newFunction(syntaxKind, pos));
318 
319   funbox->setStart(start, 0, pos.begin);
320   funbox->setEnd(pos.end);
321   handler_.setFunctionBox(result, funbox);
322 
323   return result;
324 }
325 
326 template <typename Tok>
setFunctionParametersAndBody(FunctionNode * fun,ListNode * params,ParseNode * body)327 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::setFunctionParametersAndBody(
328     FunctionNode* fun, ListNode* params, ParseNode* body) {
329   params->appendWithoutOrderAssumption(body);
330   handler_.setFunctionFormalParametersAndBody(fun, params);
331   return Ok();
332 }
333 
334 template <typename Tok>
finishEagerFunction(FunctionBox * funbox,uint32_t nargs)335 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::finishEagerFunction(
336     FunctionBox* funbox, uint32_t nargs) {
337   // If this is delazification of a canonical function, the JSFunction object
338   // already has correct `nargs_`.
339   if (!lazyScript_ || lazyScript_->function() != funbox->function()) {
340     funbox->setArgCount(nargs);
341   } else {
342     MOZ_ASSERT(funbox->function()->nargs() == nargs);
343     funbox->setArgCount(nargs);
344   }
345 
346   // BCE will need to generate bytecode for this.
347   funbox->emitBytecode = true;
348 
349   const bool canSkipLazyClosedOverBindings = false;
350   BINJS_TRY(pc_->declareFunctionArgumentsObject(usedNames_,
351                                                 canSkipLazyClosedOverBindings));
352   BINJS_TRY(
353       pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings));
354 
355   // Check all our bindings after maybe adding function metavars.
356   MOZ_TRY(checkFunctionClosedVars());
357 
358   BINJS_TRY_DECL(bindings, NewFunctionScopeData(cx_, pc_->functionScope(),
359                                                 /* hasParameterExprs = */ false,
360                                                 alloc_, pc_));
361 
362   funbox->functionScopeBindings().set(*bindings);
363 
364   // See: JSFunction::needsCallObject()
365   if (FunctionScopeHasClosedOverBindings(pc_) ||
366       funbox->needsCallObjectRegardlessOfBindings()) {
367     funbox->setNeedsFunctionEnvironmentObjects();
368   }
369 
370   if (funbox->isNamedLambda()) {
371     BINJS_TRY_DECL(
372         recursiveBinding,
373         NewLexicalScopeData(cx_, pc_->namedLambdaScope(), alloc_, pc_));
374 
375     funbox->namedLambdaBindings().set(*recursiveBinding);
376 
377     // See: JSFunction::needsNamedLambdaEnvironment()
378     if (LexicalScopeHasClosedOverBindings(pc_, pc_->namedLambdaScope())) {
379       funbox->setNeedsFunctionEnvironmentObjects();
380     }
381   }
382 
383   return Ok();
384 }
385 
386 template <typename Tok>
finishLazyFunction(FunctionBox * funbox,uint32_t nargs,size_t start,size_t end)387 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::finishLazyFunction(
388     FunctionBox* funbox, uint32_t nargs, size_t start, size_t end) {
389   funbox->setArgCount(nargs);
390 
391   ScriptStencil& stencil = funbox->functionStencil().get();
392 
393   // Compute the flags for the BaseScript.
394   using ImmutableFlags = ImmutableScriptFlagsEnum;
395   stencil.immutableFlags = funbox->immutableFlags();
396   stencil.immutableFlags.setFlag(ImmutableFlags::HasMappedArgsObj,
397                                  funbox->hasMappedArgsObj());
398   stencil.immutableFlags.setFlag(ImmutableFlags::IsLikelyConstructorWrapper,
399                                  funbox->isLikelyConstructorWrapper());
400 
401   funbox->extent = SourceExtent(start, end, start, end,
402                                 /* lineno = */ 0, start);
403 
404   MOZ_TRY(tokenizer_->registerLazyScript(funbox));
405 
406   return Ok();
407 }
408 
409 template <typename Tok>
addScopeName(AssertedScopeKind scopeKind,HandleAtom name,ParseContext::Scope * scope,DeclarationKind declKind,bool isCaptured,bool allowDuplicateName)410 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::addScopeName(
411     AssertedScopeKind scopeKind, HandleAtom name, ParseContext::Scope* scope,
412     DeclarationKind declKind, bool isCaptured, bool allowDuplicateName) {
413   auto ptr = scope->lookupDeclaredNameForAdd(name);
414   if (ptr) {
415     if (allowDuplicateName) {
416       return Ok();
417     }
418     return raiseError("Variable redeclaration");
419   }
420 
421   BINJS_TRY(scope->addDeclaredName(pc_, ptr, name.get(), declKind,
422                                    tokenizer_->offset()));
423 
424   if (isCaptured) {
425     auto declaredPtr = scope->lookupDeclaredName(name);
426     MOZ_ASSERT(declaredPtr);
427     declaredPtr->value()->setClosedOver();
428   }
429 
430   return Ok();
431 }
432 
433 template <typename Tok>
captureFunctionName()434 void BinASTParserPerTokenizer<Tok>::captureFunctionName() {
435   MOZ_ASSERT(pc_->isFunctionBox());
436   MOZ_ASSERT(pc_->functionBox()->isNamedLambda());
437 
438   RootedAtom funName(cx_, pc_->functionBox()->explicitName());
439   MOZ_ASSERT(funName);
440 
441   auto ptr = pc_->namedLambdaScope().lookupDeclaredName(funName);
442   MOZ_ASSERT(ptr);
443   ptr->value()->setClosedOver();
444 }
445 
446 template <typename Tok>
getDeclaredScope(AssertedScopeKind scopeKind,AssertedDeclaredKind kind,ParseContext::Scope * & scope,DeclarationKind & declKind)447 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::getDeclaredScope(
448     AssertedScopeKind scopeKind, AssertedDeclaredKind kind,
449     ParseContext::Scope*& scope, DeclarationKind& declKind) {
450   MOZ_ASSERT(scopeKind == AssertedScopeKind::Block ||
451              scopeKind == AssertedScopeKind::Global ||
452              scopeKind == AssertedScopeKind::Var);
453   switch (kind) {
454     case AssertedDeclaredKind::Var:
455       if (MOZ_UNLIKELY(scopeKind == AssertedScopeKind::Block)) {
456         return raiseError("AssertedBlockScope cannot contain 'var' binding");
457       }
458       declKind = DeclarationKind::Var;
459       scope = &pc_->varScope();
460       break;
461     case AssertedDeclaredKind::NonConstLexical:
462       declKind = DeclarationKind::Let;
463       scope = pc_->innermostScope();
464       break;
465     case AssertedDeclaredKind::ConstLexical:
466       declKind = DeclarationKind::Const;
467       scope = pc_->innermostScope();
468       break;
469   }
470 
471   return Ok();
472 }
473 
474 template <typename Tok>
getBoundScope(AssertedScopeKind scopeKind,ParseContext::Scope * & scope,DeclarationKind & declKind)475 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::getBoundScope(
476     AssertedScopeKind scopeKind, ParseContext::Scope*& scope,
477     DeclarationKind& declKind) {
478   MOZ_ASSERT(scopeKind == AssertedScopeKind::Catch ||
479              scopeKind == AssertedScopeKind::Parameter);
480   switch (scopeKind) {
481     case AssertedScopeKind::Catch:
482       declKind = DeclarationKind::CatchParameter;
483       scope = pc_->innermostScope();
484       break;
485     case AssertedScopeKind::Parameter:
486       MOZ_ASSERT(pc_->isFunctionBox());
487       declKind = DeclarationKind::PositionalFormalParameter;
488       scope = &pc_->functionScope();
489       break;
490     default:
491       MOZ_ASSERT_UNREACHABLE("Unexpected AssertedScopeKind");
492       break;
493   }
494 
495   return Ok();
496 }
497 
498 template <typename Tok>
checkBinding(JSAtom * name)499 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkBinding(JSAtom* name) {
500   // Check that the variable appears in the corresponding scope.
501   ParseContext::Scope& scope =
502       variableDeclarationKind_ == VariableDeclarationKind::Var
503           ? pc_->varScope()
504           : *pc_->innermostScope();
505 
506   auto ptr = scope.lookupDeclaredName(name->asPropertyName());
507   if (MOZ_UNLIKELY(!ptr)) {
508     return raiseMissingVariableInAssertedScope(name);
509   }
510 
511   return Ok();
512 }
513 
514 // Binary AST (revision 8eab67e0c434929a66ff6abe99ff790bca087dda)
515 // 3.1.5 CheckPositionalParameterIndices.
516 template <typename Tok>
checkPositionalParameterIndices(Handle<GCVector<JSAtom * >> positionalParams,ListNode * params)517 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkPositionalParameterIndices(
518     Handle<GCVector<JSAtom*>> positionalParams, ListNode* params) {
519   // positionalParams should have the corresponding entry up to the last
520   // positional parameter.
521 
522   // `positionalParams` corresponds to `expectedParams` parameter in the spec.
523   // `params` corresponds to `parseTree` in 3.1.9 CheckAssertedScope, and
524   // `positionalParamNames` parameter
525 
526   // Steps 1-3.
527   // PositionalParameterNames (3.1.9 CheckAssertedScope step 5.d) and
528   // CreatePositionalParameterIndices (3.1.5 CheckPositionalParameterIndices
529   // step 1) are done implicitly.
530   uint32_t i = 0;
531   const bool hasRest = pc_->functionBox()->hasRest();
532   for (ParseNode* param : params->contents()) {
533     if (param->isKind(ParseNodeKind::AssignExpr)) {
534       param = param->as<AssignmentNode>().left();
535     }
536 
537     // At this point, function body is not part of params list.
538     const bool isRest = hasRest && !param->pn_next;
539     if (isRest) {
540       // Rest parameter
541 
542       // Step 3.
543       if (i >= positionalParams.get().length()) {
544         continue;
545       }
546 
547       if (MOZ_UNLIKELY(positionalParams.get()[i])) {
548         return raiseError(
549             "Expected positional parameter per "
550             "AssertedParameterScope.paramNames, got rest parameter");
551       }
552     } else if (param->isKind(ParseNodeKind::Name)) {
553       // Simple or default parameter.
554 
555       // Step 2.a.
556       if (MOZ_UNLIKELY(i >= positionalParams.get().length())) {
557         return raiseError(
558             "AssertedParameterScope.paramNames doesn't have corresponding "
559             "entry to positional parameter");
560       }
561 
562       JSAtom* name = positionalParams.get()[i];
563       if (MOZ_UNLIKELY(!name)) {
564         // Step 2.a.ii.1.
565         return raiseError(
566             "Expected destructuring/rest parameter per "
567             "AssertedParameterScope.paramNames, got positional parameter");
568       }
569 
570       // Step 2.a.i.
571       if (MOZ_UNLIKELY(param->as<NameNode>().name() != name)) {
572         // Step 2.a.ii.1.
573         return raiseError(
574             "Name mismatch between AssertedPositionalParameterName in "
575             "AssertedParameterScope.paramNames and actual parameter");
576       }
577 
578       // Step 2.a.i.1.
579       // Implicitly done.
580     } else {
581       // Destructuring parameter.
582 
583       MOZ_ASSERT(param->isKind(ParseNodeKind::ObjectExpr) ||
584                  param->isKind(ParseNodeKind::ArrayExpr));
585 
586       // Step 3.
587       if (i >= positionalParams.get().length()) {
588         continue;
589       }
590 
591       if (MOZ_UNLIKELY(positionalParams.get()[i])) {
592         return raiseError(
593             "Expected positional parameter per "
594             "AssertedParameterScope.paramNames, got destructuring parameter");
595       }
596     }
597 
598     i++;
599   }
600 
601   // Step 3.
602   if (MOZ_UNLIKELY(positionalParams.get().length() > params->count())) {
603     // `positionalParams` has unchecked entries.
604     return raiseError(
605         "AssertedParameterScope.paramNames has unmatching items than the "
606         "actual parameters");
607   }
608 
609   return Ok();
610 }
611 
612 // Binary AST (revision 8eab67e0c434929a66ff6abe99ff790bca087dda)
613 // 3.1.13 CheckFunctionLength.
614 template <typename Tok>
checkFunctionLength(uint32_t expectedLength)615 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkFunctionLength(
616     uint32_t expectedLength) {
617   if (MOZ_UNLIKELY(pc_->functionBox()->length != expectedLength)) {
618     return raiseError("Function length does't match");
619   }
620   return Ok();
621 }
622 
623 template <typename Tok>
checkClosedVars(ParseContext::Scope & scope)624 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkClosedVars(
625     ParseContext::Scope& scope) {
626   for (ParseContext::Scope::BindingIter bi = scope.bindings(pc_); bi; bi++) {
627     if (UsedNamePtr p = usedNames_.lookup(bi.name())) {
628       bool closedOver;
629       p->value().noteBoundInScope(pc_->scriptId(), scope.id(), &closedOver);
630       if (MOZ_UNLIKELY(closedOver && !bi.closedOver())) {
631         return raiseInvalidClosedVar(bi.name());
632       }
633     }
634   }
635 
636   return Ok();
637 }
638 
639 template <typename Tok>
checkFunctionClosedVars()640 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkFunctionClosedVars() {
641   MOZ_ASSERT(pc_->isFunctionBox());
642 
643   MOZ_TRY(checkClosedVars(*pc_->innermostScope()));
644   MOZ_TRY(checkClosedVars(pc_->functionScope()));
645   if (pc_->functionBox()->isNamedLambda()) {
646     MOZ_TRY(checkClosedVars(pc_->namedLambdaScope()));
647   }
648 
649   return Ok();
650 }
651 
652 template <typename Tok>
prependDirectivesToBody(ListNode * body,ListNode * directives)653 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::prependDirectivesToBody(
654     ListNode* body, ListNode* directives) {
655   if (!directives) {
656     return Ok();
657   }
658 
659   if (directives->empty()) {
660     return Ok();
661   }
662 
663   MOZ_TRY(prependDirectivesImpl(body, directives->head()));
664 
665   return Ok();
666 }
667 
668 template <typename Tok>
prependDirectivesImpl(ListNode * body,ParseNode * directive)669 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::prependDirectivesImpl(
670     ListNode* body, ParseNode* directive) {
671   BINJS_TRY(CheckRecursionLimit(cx_));
672 
673   // Prepend in the reverse order by using stack, so that the directives are
674   // prepended in the original order.
675   if (ParseNode* next = directive->pn_next) {
676     MOZ_TRY(prependDirectivesImpl(body, next));
677   }
678 
679   BINJS_TRY_DECL(statement,
680                  handler_.newExprStatement(directive, directive->pn_pos.end));
681   body->prependAndUpdatePos(statement);
682 
683   return Ok();
684 }
685 
686 template <typename Tok>
687 mozilla::GenericErrorResult<JS::Error&>
raiseInvalidClosedVar(JSAtom * name)688 BinASTParserPerTokenizer<Tok>::raiseInvalidClosedVar(JSAtom* name) {
689   return raiseError("Captured variable was not declared as captured");
690 }
691 
692 template <typename Tok>
693 mozilla::GenericErrorResult<JS::Error&>
raiseMissingVariableInAssertedScope(JSAtom * name)694 BinASTParserPerTokenizer<Tok>::raiseMissingVariableInAssertedScope(
695     JSAtom* name) {
696   // For the moment, we don't trust inputs sufficiently to put the name
697   // in an error message.
698   return raiseError("Missing variable in AssertedScope");
699 }
700 
701 template <typename Tok>
702 mozilla::GenericErrorResult<JS::Error&>
raiseMissingDirectEvalInAssertedScope()703 BinASTParserPerTokenizer<Tok>::raiseMissingDirectEvalInAssertedScope() {
704   return raiseError("Direct call to `eval` was not declared in AssertedScope");
705 }
706 
707 template <typename Tok>
708 mozilla::GenericErrorResult<JS::Error&>
raiseInvalidKind(const char * superKind,const BinASTKind kind)709 BinASTParserPerTokenizer<Tok>::raiseInvalidKind(const char* superKind,
710                                                 const BinASTKind kind) {
711   Sprinter out(cx_);
712   BINJS_TRY(out.init());
713   BINJS_TRY(out.printf("In %s, invalid kind %s", superKind,
714                        describeBinASTKind(kind)));
715   return raiseError(out.string());
716 }
717 
718 template <typename Tok>
719 mozilla::GenericErrorResult<JS::Error&>
raiseInvalidVariant(const char * kind,const BinASTVariant value)720 BinASTParserPerTokenizer<Tok>::raiseInvalidVariant(const char* kind,
721                                                    const BinASTVariant value) {
722   Sprinter out(cx_);
723   BINJS_TRY(out.init());
724   BINJS_TRY(out.printf("In %s, invalid variant '%s'", kind,
725                        describeBinASTVariant(value)));
726 
727   return raiseError(out.string());
728 }
729 
730 template <typename Tok>
731 mozilla::GenericErrorResult<JS::Error&>
raiseMissingField(const char * kind,const BinASTField field)732 BinASTParserPerTokenizer<Tok>::raiseMissingField(const char* kind,
733                                                  const BinASTField field) {
734   Sprinter out(cx_);
735   BINJS_TRY(out.init());
736   BINJS_TRY(out.printf("In %s, missing field '%s'", kind,
737                        describeBinASTField(field)));
738 
739   return raiseError(out.string());
740 }
741 
742 template <typename Tok>
743 mozilla::GenericErrorResult<JS::Error&>
raiseEmpty(const char * description)744 BinASTParserPerTokenizer<Tok>::raiseEmpty(const char* description) {
745   Sprinter out(cx_);
746   BINJS_TRY(out.init());
747   BINJS_TRY(out.printf("Empty %s", description));
748 
749   return raiseError(out.string());
750 }
751 
752 template <typename Tok>
753 mozilla::GenericErrorResult<JS::Error&>
raiseOOM()754 BinASTParserPerTokenizer<Tok>::raiseOOM() {
755   return tokenizer_->raiseOOM();
756 }
757 
758 template <typename Tok>
759 mozilla::GenericErrorResult<JS::Error&>
raiseError(BinASTKind kind,const char * description)760 BinASTParserPerTokenizer<Tok>::raiseError(BinASTKind kind,
761                                           const char* description) {
762   Sprinter out(cx_);
763   BINJS_TRY(out.init());
764   BINJS_TRY(out.printf("In %s, %s", describeBinASTKind(kind), description));
765   return tokenizer_->raiseError(out.string());
766 }
767 
768 template <typename Tok>
769 mozilla::GenericErrorResult<JS::Error&>
raiseError(const char * description)770 BinASTParserPerTokenizer<Tok>::raiseError(const char* description) {
771   return tokenizer_->raiseError(description);
772 }
773 
774 template <typename Tok>
poison()775 void BinASTParserPerTokenizer<Tok>::poison() {
776   tokenizer_.reset();
777 }
778 
779 template <typename Tok>
computeErrorMetadata(ErrorMetadata * err,const ErrorOffset & errorOffset)780 bool BinASTParserPerTokenizer<Tok>::computeErrorMetadata(
781     ErrorMetadata* err, const ErrorOffset& errorOffset) {
782   err->filename = getFilename();
783   err->lineNumber = 0;
784   if (errorOffset.is<uint32_t>()) {
785     err->columnNumber = errorOffset.as<uint32_t>();
786   } else if (errorOffset.is<Current>()) {
787     err->columnNumber = offset();
788   } else {
789     errorOffset.is<NoOffset>();
790     err->columnNumber = 0;
791   }
792 
793   err->isMuted = options().mutedErrors();
794   return true;
795 }
796 
797 template <typename Tok>
trace(JSTracer * trc)798 void BinASTParserPerTokenizer<Tok>::trace(JSTracer* trc) {
799   BinASTParserBase::trace(trc);
800   if (tokenizer_) {
801     tokenizer_->traceMetadata(trc);
802   }
803 }
804 
805 template <typename Tok>
806 inline typename BinASTParserPerTokenizer<Tok>::FinalParser*
asFinalParser()807 BinASTParserPerTokenizer<Tok>::asFinalParser() {
808   // Same as GeneralParser::asFinalParser, verify the inheritance to
809   // make sure the static downcast works.
810   static_assert(std::is_base_of_v<BinASTParserPerTokenizer<Tok>, FinalParser>,
811                 "inheritance relationship required by the static_cast<> below");
812 
813   return static_cast<FinalParser*>(this);
814 }
815 
816 template <typename Tok>
817 inline const typename BinASTParserPerTokenizer<Tok>::FinalParser*
asFinalParser() const818 BinASTParserPerTokenizer<Tok>::asFinalParser() const {
819   static_assert(std::is_base_of_v<BinASTParserPerTokenizer<Tok>, FinalParser>,
820                 "inheritance relationship required by the static_cast<> below");
821 
822   return static_cast<const FinalParser*>(this);
823 }
824 
825 // Force class instantiation.
826 // This ensures that the symbols are built, without having to export all our
827 // code (and its baggage of #include and macros) in the header.
828 template class BinASTParserPerTokenizer<BinASTTokenReaderContext>;
829 template class BinASTParserPerTokenizer<BinASTTokenReaderMultipart>;
830 
831 }  // namespace js::frontend
832