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(), ¶ms, &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