|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
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 /* |
|
8 * JS parser. |
|
9 * |
|
10 * This is a recursive-descent parser for the JavaScript language specified by |
|
11 * "The JavaScript 1.5 Language Specification". It uses lexical and semantic |
|
12 * feedback to disambiguate non-LL(1) structures. It generates trees of nodes |
|
13 * induced by the recursive parsing (not precise syntax trees, see Parser.h). |
|
14 * After tree construction, it rewrites trees to fold constants and evaluate |
|
15 * compile-time expressions. |
|
16 * |
|
17 * This parser attempts no error recovery. |
|
18 */ |
|
19 |
|
20 #include "frontend/Parser-inl.h" |
|
21 |
|
22 #include "jsapi.h" |
|
23 #include "jsatom.h" |
|
24 #include "jscntxt.h" |
|
25 #include "jsfun.h" |
|
26 #include "jsobj.h" |
|
27 #include "jsopcode.h" |
|
28 #include "jsscript.h" |
|
29 #include "jstypes.h" |
|
30 |
|
31 #include "frontend/BytecodeCompiler.h" |
|
32 #include "frontend/FoldConstants.h" |
|
33 #include "frontend/ParseMaps.h" |
|
34 #include "frontend/TokenStream.h" |
|
35 #include "jit/AsmJS.h" |
|
36 #include "vm/Shape.h" |
|
37 |
|
38 #include "jsatominlines.h" |
|
39 #include "jsscriptinlines.h" |
|
40 |
|
41 #include "frontend/ParseNode-inl.h" |
|
42 |
|
43 using namespace js; |
|
44 using namespace js::gc; |
|
45 using mozilla::Maybe; |
|
46 |
|
47 namespace js { |
|
48 namespace frontend { |
|
49 |
|
50 typedef Rooted<StaticBlockObject*> RootedStaticBlockObject; |
|
51 typedef Handle<StaticBlockObject*> HandleStaticBlockObject; |
|
52 typedef Rooted<NestedScopeObject*> RootedNestedScopeObject; |
|
53 typedef Handle<NestedScopeObject*> HandleNestedScopeObject; |
|
54 |
|
55 |
|
56 /* |
|
57 * Insist that the next token be of type tt, or report errno and return null. |
|
58 * NB: this macro uses cx and ts from its lexical environment. |
|
59 */ |
|
60 #define MUST_MATCH_TOKEN(tt, errno) \ |
|
61 JS_BEGIN_MACRO \ |
|
62 if (tokenStream.getToken() != tt) { \ |
|
63 report(ParseError, false, null(), errno); \ |
|
64 return null(); \ |
|
65 } \ |
|
66 JS_END_MACRO |
|
67 |
|
68 static const unsigned BlockIdLimit = 1 << ParseNode::NumBlockIdBits; |
|
69 |
|
70 template <typename ParseHandler> |
|
71 bool |
|
72 GenerateBlockId(TokenStream &ts, ParseContext<ParseHandler> *pc, uint32_t &blockid) |
|
73 { |
|
74 if (pc->blockidGen == BlockIdLimit) { |
|
75 ts.reportError(JSMSG_NEED_DIET, "program"); |
|
76 return false; |
|
77 } |
|
78 JS_ASSERT(pc->blockidGen < BlockIdLimit); |
|
79 blockid = pc->blockidGen++; |
|
80 return true; |
|
81 } |
|
82 |
|
83 template bool |
|
84 GenerateBlockId(TokenStream &ts, ParseContext<SyntaxParseHandler> *pc, uint32_t &blockid); |
|
85 |
|
86 template bool |
|
87 GenerateBlockId(TokenStream &ts, ParseContext<FullParseHandler> *pc, uint32_t &blockid); |
|
88 |
|
89 template <typename ParseHandler> |
|
90 static void |
|
91 PushStatementPC(ParseContext<ParseHandler> *pc, StmtInfoPC *stmt, StmtType type) |
|
92 { |
|
93 stmt->blockid = pc->blockid(); |
|
94 PushStatement(pc, stmt, type); |
|
95 } |
|
96 |
|
97 // See comment on member function declaration. |
|
98 template <> |
|
99 bool |
|
100 ParseContext<FullParseHandler>::define(TokenStream &ts, |
|
101 HandlePropertyName name, ParseNode *pn, Definition::Kind kind) |
|
102 { |
|
103 JS_ASSERT(!pn->isUsed()); |
|
104 JS_ASSERT_IF(pn->isDefn(), pn->isPlaceholder()); |
|
105 |
|
106 Definition *prevDef = nullptr; |
|
107 if (kind == Definition::LET) |
|
108 prevDef = decls_.lookupFirst(name); |
|
109 else |
|
110 JS_ASSERT(!decls_.lookupFirst(name)); |
|
111 |
|
112 if (!prevDef) |
|
113 prevDef = lexdeps.lookupDefn<FullParseHandler>(name); |
|
114 |
|
115 if (prevDef) { |
|
116 ParseNode **pnup = &prevDef->dn_uses; |
|
117 ParseNode *pnu; |
|
118 unsigned start = (kind == Definition::LET) ? pn->pn_blockid : bodyid; |
|
119 |
|
120 while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) { |
|
121 JS_ASSERT(pnu->pn_blockid >= bodyid); |
|
122 JS_ASSERT(pnu->isUsed()); |
|
123 pnu->pn_lexdef = (Definition *) pn; |
|
124 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS; |
|
125 pnup = &pnu->pn_link; |
|
126 } |
|
127 |
|
128 if (!pnu || pnu != prevDef->dn_uses) { |
|
129 *pnup = pn->dn_uses; |
|
130 pn->dn_uses = prevDef->dn_uses; |
|
131 prevDef->dn_uses = pnu; |
|
132 |
|
133 if (!pnu && prevDef->isPlaceholder()) |
|
134 lexdeps->remove(name); |
|
135 } |
|
136 |
|
137 pn->pn_dflags |= prevDef->pn_dflags & PND_CLOSED; |
|
138 } |
|
139 |
|
140 JS_ASSERT_IF(kind != Definition::LET, !lexdeps->lookup(name)); |
|
141 pn->setDefn(true); |
|
142 pn->pn_dflags &= ~PND_PLACEHOLDER; |
|
143 if (kind == Definition::CONST) |
|
144 pn->pn_dflags |= PND_CONST; |
|
145 |
|
146 Definition *dn = (Definition *)pn; |
|
147 switch (kind) { |
|
148 case Definition::ARG: |
|
149 JS_ASSERT(sc->isFunctionBox()); |
|
150 dn->setOp(JSOP_GETARG); |
|
151 dn->pn_dflags |= PND_BOUND; |
|
152 if (!dn->pn_cookie.set(ts, staticLevel, args_.length())) |
|
153 return false; |
|
154 if (!args_.append(dn)) |
|
155 return false; |
|
156 if (args_.length() >= ARGNO_LIMIT) { |
|
157 ts.reportError(JSMSG_TOO_MANY_FUN_ARGS); |
|
158 return false; |
|
159 } |
|
160 if (name == ts.names().empty) |
|
161 break; |
|
162 if (!decls_.addUnique(name, dn)) |
|
163 return false; |
|
164 break; |
|
165 |
|
166 case Definition::CONST: |
|
167 case Definition::VAR: |
|
168 if (sc->isFunctionBox()) { |
|
169 dn->setOp(JSOP_GETLOCAL); |
|
170 dn->pn_dflags |= PND_BOUND; |
|
171 if (!dn->pn_cookie.set(ts, staticLevel, vars_.length())) |
|
172 return false; |
|
173 if (!vars_.append(dn)) |
|
174 return false; |
|
175 if (vars_.length() >= LOCALNO_LIMIT) { |
|
176 ts.reportError(JSMSG_TOO_MANY_LOCALS); |
|
177 return false; |
|
178 } |
|
179 } |
|
180 if (!decls_.addUnique(name, dn)) |
|
181 return false; |
|
182 break; |
|
183 |
|
184 case Definition::LET: |
|
185 dn->setOp(JSOP_GETLOCAL); |
|
186 dn->pn_dflags |= (PND_LET | PND_BOUND); |
|
187 JS_ASSERT(dn->pn_cookie.level() == staticLevel); /* see bindLet */ |
|
188 if (!decls_.addShadow(name, dn)) |
|
189 return false; |
|
190 break; |
|
191 |
|
192 default: |
|
193 MOZ_ASSUME_UNREACHABLE("unexpected kind"); |
|
194 } |
|
195 |
|
196 return true; |
|
197 } |
|
198 |
|
199 template <> |
|
200 bool |
|
201 ParseContext<SyntaxParseHandler>::define(TokenStream &ts, HandlePropertyName name, Node pn, |
|
202 Definition::Kind kind) |
|
203 { |
|
204 JS_ASSERT(!decls_.lookupFirst(name)); |
|
205 |
|
206 if (lexdeps.lookupDefn<SyntaxParseHandler>(name)) |
|
207 lexdeps->remove(name); |
|
208 |
|
209 // Keep track of the number of arguments in args_, for fun->nargs. |
|
210 if (kind == Definition::ARG) { |
|
211 if (!args_.append((Definition *) nullptr)) |
|
212 return false; |
|
213 if (args_.length() >= ARGNO_LIMIT) { |
|
214 ts.reportError(JSMSG_TOO_MANY_FUN_ARGS); |
|
215 return false; |
|
216 } |
|
217 } |
|
218 |
|
219 return decls_.addUnique(name, kind); |
|
220 } |
|
221 |
|
222 template <typename ParseHandler> |
|
223 void |
|
224 ParseContext<ParseHandler>::prepareToAddDuplicateArg(HandlePropertyName name, DefinitionNode prevDecl) |
|
225 { |
|
226 JS_ASSERT(decls_.lookupFirst(name) == prevDecl); |
|
227 decls_.remove(name); |
|
228 } |
|
229 |
|
230 template <typename ParseHandler> |
|
231 void |
|
232 ParseContext<ParseHandler>::updateDecl(JSAtom *atom, Node pn) |
|
233 { |
|
234 Definition *oldDecl = decls_.lookupFirst(atom); |
|
235 |
|
236 pn->setDefn(true); |
|
237 Definition *newDecl = (Definition *)pn; |
|
238 decls_.updateFirst(atom, newDecl); |
|
239 |
|
240 if (!sc->isFunctionBox()) { |
|
241 JS_ASSERT(newDecl->isFreeVar()); |
|
242 return; |
|
243 } |
|
244 |
|
245 JS_ASSERT(oldDecl->isBound()); |
|
246 JS_ASSERT(!oldDecl->pn_cookie.isFree()); |
|
247 newDecl->pn_cookie = oldDecl->pn_cookie; |
|
248 newDecl->pn_dflags |= PND_BOUND; |
|
249 if (IsArgOp(oldDecl->getOp())) { |
|
250 newDecl->setOp(JSOP_GETARG); |
|
251 JS_ASSERT(args_[oldDecl->pn_cookie.slot()] == oldDecl); |
|
252 args_[oldDecl->pn_cookie.slot()] = newDecl; |
|
253 } else { |
|
254 JS_ASSERT(IsLocalOp(oldDecl->getOp())); |
|
255 newDecl->setOp(JSOP_GETLOCAL); |
|
256 JS_ASSERT(vars_[oldDecl->pn_cookie.slot()] == oldDecl); |
|
257 vars_[oldDecl->pn_cookie.slot()] = newDecl; |
|
258 } |
|
259 } |
|
260 |
|
261 template <typename ParseHandler> |
|
262 void |
|
263 ParseContext<ParseHandler>::popLetDecl(JSAtom *atom) |
|
264 { |
|
265 JS_ASSERT(ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::LET); |
|
266 decls_.remove(atom); |
|
267 } |
|
268 |
|
269 template <typename ParseHandler> |
|
270 static void |
|
271 AppendPackedBindings(const ParseContext<ParseHandler> *pc, const DeclVector &vec, Binding *dst) |
|
272 { |
|
273 for (size_t i = 0; i < vec.length(); ++i, ++dst) { |
|
274 Definition *dn = vec[i]; |
|
275 PropertyName *name = dn->name(); |
|
276 |
|
277 Binding::Kind kind; |
|
278 switch (dn->kind()) { |
|
279 case Definition::VAR: |
|
280 kind = Binding::VARIABLE; |
|
281 break; |
|
282 case Definition::CONST: |
|
283 kind = Binding::CONSTANT; |
|
284 break; |
|
285 case Definition::ARG: |
|
286 kind = Binding::ARGUMENT; |
|
287 break; |
|
288 default: |
|
289 MOZ_ASSUME_UNREACHABLE("unexpected dn->kind"); |
|
290 } |
|
291 |
|
292 /* |
|
293 * Bindings::init does not check for duplicates so we must ensure that |
|
294 * only one binding with a given name is marked aliased. pc->decls |
|
295 * maintains the canonical definition for each name, so use that. |
|
296 */ |
|
297 JS_ASSERT_IF(dn->isClosed(), pc->decls().lookupFirst(name) == dn); |
|
298 bool aliased = dn->isClosed() || |
|
299 (pc->sc->allLocalsAliased() && |
|
300 pc->decls().lookupFirst(name) == dn); |
|
301 |
|
302 *dst = Binding(name, kind, aliased); |
|
303 } |
|
304 } |
|
305 |
|
306 template <typename ParseHandler> |
|
307 bool |
|
308 ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext *cx, TokenStream &ts, |
|
309 LifoAlloc &alloc, |
|
310 InternalHandle<Bindings*> bindings) const |
|
311 { |
|
312 JS_ASSERT(sc->isFunctionBox()); |
|
313 JS_ASSERT(args_.length() < ARGNO_LIMIT); |
|
314 JS_ASSERT(vars_.length() < LOCALNO_LIMIT); |
|
315 |
|
316 /* |
|
317 * Avoid pathological edge cases by explicitly limiting the total number of |
|
318 * bindings to what will fit in a uint32_t. |
|
319 */ |
|
320 if (UINT32_MAX - args_.length() <= vars_.length()) |
|
321 return ts.reportError(JSMSG_TOO_MANY_LOCALS); |
|
322 |
|
323 uint32_t count = args_.length() + vars_.length(); |
|
324 Binding *packedBindings = alloc.newArrayUninitialized<Binding>(count); |
|
325 if (!packedBindings) { |
|
326 js_ReportOutOfMemory(cx); |
|
327 return false; |
|
328 } |
|
329 |
|
330 AppendPackedBindings(this, args_, packedBindings); |
|
331 AppendPackedBindings(this, vars_, packedBindings + args_.length()); |
|
332 |
|
333 return Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(), |
|
334 packedBindings, blockScopeDepth); |
|
335 } |
|
336 |
|
337 template <typename ParseHandler> |
|
338 bool |
|
339 Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset, |
|
340 unsigned errorNumber, va_list args) |
|
341 { |
|
342 bool result = false; |
|
343 switch (kind) { |
|
344 case ParseError: |
|
345 result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args); |
|
346 break; |
|
347 case ParseWarning: |
|
348 result = |
|
349 tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args); |
|
350 break; |
|
351 case ParseExtraWarning: |
|
352 result = tokenStream.reportStrictWarningErrorNumberVA(offset, errorNumber, args); |
|
353 break; |
|
354 case ParseStrictError: |
|
355 result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args); |
|
356 break; |
|
357 } |
|
358 return result; |
|
359 } |
|
360 |
|
361 template <typename ParseHandler> |
|
362 bool |
|
363 Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...) |
|
364 { |
|
365 uint32_t offset = (pn ? handler.getPosition(pn) : pos()).begin; |
|
366 |
|
367 va_list args; |
|
368 va_start(args, errorNumber); |
|
369 bool result = reportHelper(kind, strict, offset, errorNumber, args); |
|
370 va_end(args); |
|
371 return result; |
|
372 } |
|
373 |
|
374 template <typename ParseHandler> |
|
375 bool |
|
376 Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...) |
|
377 { |
|
378 va_list args; |
|
379 va_start(args, errorNumber); |
|
380 bool result = reportHelper(kind, strict, TokenStream::NoOffset, errorNumber, args); |
|
381 va_end(args); |
|
382 return result; |
|
383 } |
|
384 |
|
385 template <typename ParseHandler> |
|
386 bool |
|
387 Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, |
|
388 unsigned errorNumber, ...) |
|
389 { |
|
390 va_list args; |
|
391 va_start(args, errorNumber); |
|
392 bool result = reportHelper(kind, strict, offset, errorNumber, args); |
|
393 va_end(args); |
|
394 return result; |
|
395 } |
|
396 |
|
397 template <> |
|
398 bool |
|
399 Parser<FullParseHandler>::abortIfSyntaxParser() |
|
400 { |
|
401 handler.disableSyntaxParser(); |
|
402 return true; |
|
403 } |
|
404 |
|
405 template <> |
|
406 bool |
|
407 Parser<SyntaxParseHandler>::abortIfSyntaxParser() |
|
408 { |
|
409 abortedSyntaxParse = true; |
|
410 return false; |
|
411 } |
|
412 |
|
413 template <typename ParseHandler> |
|
414 Parser<ParseHandler>::Parser(ExclusiveContext *cx, LifoAlloc *alloc, |
|
415 const ReadOnlyCompileOptions &options, |
|
416 const jschar *chars, size_t length, bool foldConstants, |
|
417 Parser<SyntaxParseHandler> *syntaxParser, |
|
418 LazyScript *lazyOuterFunction) |
|
419 : AutoGCRooter(cx, PARSER), |
|
420 context(cx), |
|
421 alloc(*alloc), |
|
422 tokenStream(cx, options, chars, length, thisForCtor()), |
|
423 traceListHead(nullptr), |
|
424 pc(nullptr), |
|
425 sct(nullptr), |
|
426 ss(nullptr), |
|
427 keepAtoms(cx->perThreadData), |
|
428 foldConstants(foldConstants), |
|
429 abortedSyntaxParse(false), |
|
430 isUnexpectedEOF_(false), |
|
431 handler(cx, *alloc, tokenStream, foldConstants, syntaxParser, lazyOuterFunction) |
|
432 { |
|
433 { |
|
434 AutoLockForExclusiveAccess lock(cx); |
|
435 cx->perThreadData->addActiveCompilation(); |
|
436 } |
|
437 |
|
438 // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings |
|
439 // which are not generated if functions are parsed lazily. Note that the |
|
440 // standard "use strict" does not inhibit lazy parsing. |
|
441 if (options.extraWarningsOption) |
|
442 handler.disableSyntaxParser(); |
|
443 |
|
444 tempPoolMark = alloc->mark(); |
|
445 } |
|
446 |
|
447 template <typename ParseHandler> |
|
448 Parser<ParseHandler>::~Parser() |
|
449 { |
|
450 alloc.release(tempPoolMark); |
|
451 |
|
452 /* |
|
453 * The parser can allocate enormous amounts of memory for large functions. |
|
454 * Eagerly free the memory now (which otherwise won't be freed until the |
|
455 * next GC) to avoid unnecessary OOMs. |
|
456 */ |
|
457 alloc.freeAllIfHugeAndUnused(); |
|
458 |
|
459 { |
|
460 AutoLockForExclusiveAccess lock(context); |
|
461 context->perThreadData->removeActiveCompilation(); |
|
462 } |
|
463 } |
|
464 |
|
465 template <typename ParseHandler> |
|
466 ObjectBox * |
|
467 Parser<ParseHandler>::newObjectBox(JSObject *obj) |
|
468 { |
|
469 JS_ASSERT(obj && !IsPoisonedPtr(obj)); |
|
470 |
|
471 /* |
|
472 * We use JSContext.tempLifoAlloc to allocate parsed objects and place them |
|
473 * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc |
|
474 * arenas containing the entries must be alive until we are done with |
|
475 * scanning, parsing and code generation for the whole script or top-level |
|
476 * function. |
|
477 */ |
|
478 |
|
479 ObjectBox *objbox = alloc.new_<ObjectBox>(obj, traceListHead); |
|
480 if (!objbox) { |
|
481 js_ReportOutOfMemory(context); |
|
482 return nullptr; |
|
483 } |
|
484 |
|
485 traceListHead = objbox; |
|
486 |
|
487 return objbox; |
|
488 } |
|
489 |
|
490 template <typename ParseHandler> |
|
491 FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun, |
|
492 ParseContext<ParseHandler> *outerpc, Directives directives, |
|
493 bool extraWarnings, GeneratorKind generatorKind) |
|
494 : ObjectBox(fun, traceListHead), |
|
495 SharedContext(cx, directives, extraWarnings), |
|
496 bindings(), |
|
497 bufStart(0), |
|
498 bufEnd(0), |
|
499 length(0), |
|
500 generatorKindBits_(GeneratorKindAsBits(generatorKind)), |
|
501 inWith(false), // initialized below |
|
502 inGenexpLambda(false), |
|
503 hasDestructuringArgs(false), |
|
504 useAsm(directives.asmJS()), |
|
505 insideUseAsm(outerpc && outerpc->useAsmOrInsideUseAsm()), |
|
506 usesArguments(false), |
|
507 usesApply(false), |
|
508 funCxFlags() |
|
509 { |
|
510 // Functions created at parse time may be set singleton after parsing and |
|
511 // baked into JIT code, so they must be allocated tenured. They are held by |
|
512 // the JSScript so cannot be collected during a minor GC anyway. |
|
513 JS_ASSERT(fun->isTenured()); |
|
514 |
|
515 if (!outerpc) { |
|
516 inWith = false; |
|
517 |
|
518 } else if (outerpc->parsingWith) { |
|
519 // This covers cases that don't involve eval(). For example: |
|
520 // |
|
521 // with (o) { (function() { g(); })(); } |
|
522 // |
|
523 // In this case, |outerpc| corresponds to global code, and |
|
524 // outerpc->parsingWith is true. |
|
525 inWith = true; |
|
526 |
|
527 } else if (outerpc->sc->isGlobalSharedContext()) { |
|
528 // This covers the case where a function is nested within an eval() |
|
529 // within a |with| statement. |
|
530 // |
|
531 // with (o) { eval("(function() { g(); })();"); } |
|
532 // |
|
533 // In this case, |outerpc| corresponds to the eval(), |
|
534 // outerpc->parsingWith is false because the eval() breaks the |
|
535 // ParseContext chain, and |parent| is nullptr (again because of the |
|
536 // eval(), so we have to look at |outerpc|'s scopeChain. |
|
537 // |
|
538 JSObject *scope = outerpc->sc->asGlobalSharedContext()->scopeChain(); |
|
539 while (scope) { |
|
540 if (scope->is<DynamicWithObject>()) |
|
541 inWith = true; |
|
542 scope = scope->enclosingScope(); |
|
543 } |
|
544 } else if (outerpc->sc->isFunctionBox()) { |
|
545 // This is like the above case, but for more deeply nested functions. |
|
546 // For example: |
|
547 // |
|
548 // with (o) { eval("(function() { (function() { g(); })(); })();"); } } |
|
549 // |
|
550 // In this case, the inner anonymous function needs to inherit the |
|
551 // setting of |inWith| from the outer one. |
|
552 FunctionBox *parent = outerpc->sc->asFunctionBox(); |
|
553 if (parent && parent->inWith) |
|
554 inWith = true; |
|
555 } |
|
556 } |
|
557 |
|
558 template <typename ParseHandler> |
|
559 FunctionBox * |
|
560 Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *outerpc, |
|
561 Directives inheritedDirectives, GeneratorKind generatorKind) |
|
562 { |
|
563 JS_ASSERT(fun && !IsPoisonedPtr(fun)); |
|
564 |
|
565 /* |
|
566 * We use JSContext.tempLifoAlloc to allocate parsed objects and place them |
|
567 * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc |
|
568 * arenas containing the entries must be alive until we are done with |
|
569 * scanning, parsing and code generation for the whole script or top-level |
|
570 * function. |
|
571 */ |
|
572 FunctionBox *funbox = |
|
573 alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc, |
|
574 inheritedDirectives, options().extraWarningsOption, |
|
575 generatorKind); |
|
576 if (!funbox) { |
|
577 js_ReportOutOfMemory(context); |
|
578 return nullptr; |
|
579 } |
|
580 |
|
581 traceListHead = funbox; |
|
582 if (fn) |
|
583 handler.setFunctionBox(fn, funbox); |
|
584 |
|
585 return funbox; |
|
586 } |
|
587 |
|
588 template <typename ParseHandler> |
|
589 void |
|
590 Parser<ParseHandler>::trace(JSTracer *trc) |
|
591 { |
|
592 traceListHead->trace(trc); |
|
593 } |
|
594 |
|
595 void |
|
596 MarkParser(JSTracer *trc, AutoGCRooter *parser) |
|
597 { |
|
598 static_cast<Parser<FullParseHandler> *>(parser)->trace(trc); |
|
599 } |
|
600 |
|
601 /* |
|
602 * Parse a top-level JS script. |
|
603 */ |
|
604 template <typename ParseHandler> |
|
605 typename ParseHandler::Node |
|
606 Parser<ParseHandler>::parse(JSObject *chain) |
|
607 { |
|
608 /* |
|
609 * Protect atoms from being collected by a GC activation, which might |
|
610 * - nest on this thread due to out of memory (the so-called "last ditch" |
|
611 * GC attempted within js_NewGCThing), or |
|
612 * - run for any reason on another thread if this thread is suspended on |
|
613 * an object lock before it finishes generating bytecode into a script |
|
614 * protected from the GC by a root or a stack frame reference. |
|
615 */ |
|
616 Directives directives(options().strictOption); |
|
617 GlobalSharedContext globalsc(context, chain, directives, options().extraWarningsOption); |
|
618 ParseContext<ParseHandler> globalpc(this, /* parent = */ nullptr, ParseHandler::null(), |
|
619 &globalsc, /* newDirectives = */ nullptr, |
|
620 /* staticLevel = */ 0, /* bodyid = */ 0, |
|
621 /* blockScopeDepth = */ 0); |
|
622 if (!globalpc.init(tokenStream)) |
|
623 return null(); |
|
624 |
|
625 Node pn = statements(); |
|
626 if (pn) { |
|
627 if (!tokenStream.matchToken(TOK_EOF)) { |
|
628 report(ParseError, false, null(), JSMSG_SYNTAX_ERROR); |
|
629 return null(); |
|
630 } |
|
631 if (foldConstants) { |
|
632 if (!FoldConstants(context, &pn, this)) |
|
633 return null(); |
|
634 } |
|
635 } |
|
636 return pn; |
|
637 } |
|
638 |
|
639 /* |
|
640 * Insist on a final return before control flows out of pn. Try to be a bit |
|
641 * smart about loops: do {...; return e2;} while(0) at the end of a function |
|
642 * that contains an early return e1 will get a strict warning. Similarly for |
|
643 * iloops: while (true){...} is treated as though ... returns. |
|
644 */ |
|
645 enum { |
|
646 ENDS_IN_OTHER = 0, |
|
647 ENDS_IN_RETURN = 1, |
|
648 ENDS_IN_BREAK = 2 |
|
649 }; |
|
650 |
|
651 static int |
|
652 HasFinalReturn(ParseNode *pn) |
|
653 { |
|
654 ParseNode *pn2, *pn3; |
|
655 unsigned rv, rv2, hasDefault; |
|
656 |
|
657 switch (pn->getKind()) { |
|
658 case PNK_STATEMENTLIST: |
|
659 if (!pn->pn_head) |
|
660 return ENDS_IN_OTHER; |
|
661 return HasFinalReturn(pn->last()); |
|
662 |
|
663 case PNK_IF: |
|
664 if (!pn->pn_kid3) |
|
665 return ENDS_IN_OTHER; |
|
666 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3); |
|
667 |
|
668 case PNK_WHILE: |
|
669 pn2 = pn->pn_left; |
|
670 if (pn2->isKind(PNK_TRUE)) |
|
671 return ENDS_IN_RETURN; |
|
672 if (pn2->isKind(PNK_NUMBER) && pn2->pn_dval) |
|
673 return ENDS_IN_RETURN; |
|
674 return ENDS_IN_OTHER; |
|
675 |
|
676 case PNK_DOWHILE: |
|
677 pn2 = pn->pn_right; |
|
678 if (pn2->isKind(PNK_FALSE)) |
|
679 return HasFinalReturn(pn->pn_left); |
|
680 if (pn2->isKind(PNK_TRUE)) |
|
681 return ENDS_IN_RETURN; |
|
682 if (pn2->isKind(PNK_NUMBER)) { |
|
683 if (pn2->pn_dval == 0) |
|
684 return HasFinalReturn(pn->pn_left); |
|
685 return ENDS_IN_RETURN; |
|
686 } |
|
687 return ENDS_IN_OTHER; |
|
688 |
|
689 case PNK_FOR: |
|
690 pn2 = pn->pn_left; |
|
691 if (pn2->isArity(PN_TERNARY) && !pn2->pn_kid2) |
|
692 return ENDS_IN_RETURN; |
|
693 return ENDS_IN_OTHER; |
|
694 |
|
695 case PNK_SWITCH: |
|
696 rv = ENDS_IN_RETURN; |
|
697 hasDefault = ENDS_IN_OTHER; |
|
698 pn2 = pn->pn_right; |
|
699 if (pn2->isKind(PNK_LEXICALSCOPE)) |
|
700 pn2 = pn2->expr(); |
|
701 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) { |
|
702 if (pn2->isKind(PNK_DEFAULT)) |
|
703 hasDefault = ENDS_IN_RETURN; |
|
704 pn3 = pn2->pn_right; |
|
705 JS_ASSERT(pn3->isKind(PNK_STATEMENTLIST)); |
|
706 if (pn3->pn_head) { |
|
707 rv2 = HasFinalReturn(pn3->last()); |
|
708 if (rv2 == ENDS_IN_OTHER && pn2->pn_next) |
|
709 /* Falling through to next case or default. */; |
|
710 else |
|
711 rv &= rv2; |
|
712 } |
|
713 } |
|
714 /* If a final switch has no default case, we judge it harshly. */ |
|
715 rv &= hasDefault; |
|
716 return rv; |
|
717 |
|
718 case PNK_BREAK: |
|
719 return ENDS_IN_BREAK; |
|
720 |
|
721 case PNK_WITH: |
|
722 return HasFinalReturn(pn->pn_right); |
|
723 |
|
724 case PNK_RETURN: |
|
725 return ENDS_IN_RETURN; |
|
726 |
|
727 case PNK_COLON: |
|
728 case PNK_LEXICALSCOPE: |
|
729 return HasFinalReturn(pn->expr()); |
|
730 |
|
731 case PNK_THROW: |
|
732 return ENDS_IN_RETURN; |
|
733 |
|
734 case PNK_TRY: |
|
735 /* If we have a finally block that returns, we are done. */ |
|
736 if (pn->pn_kid3) { |
|
737 rv = HasFinalReturn(pn->pn_kid3); |
|
738 if (rv == ENDS_IN_RETURN) |
|
739 return rv; |
|
740 } |
|
741 |
|
742 /* Else check the try block and any and all catch statements. */ |
|
743 rv = HasFinalReturn(pn->pn_kid1); |
|
744 if (pn->pn_kid2) { |
|
745 JS_ASSERT(pn->pn_kid2->isArity(PN_LIST)); |
|
746 for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next) |
|
747 rv &= HasFinalReturn(pn2); |
|
748 } |
|
749 return rv; |
|
750 |
|
751 case PNK_CATCH: |
|
752 /* Check this catch block's body. */ |
|
753 return HasFinalReturn(pn->pn_kid3); |
|
754 |
|
755 case PNK_LET: |
|
756 /* Non-binary let statements are let declarations. */ |
|
757 if (!pn->isArity(PN_BINARY)) |
|
758 return ENDS_IN_OTHER; |
|
759 return HasFinalReturn(pn->pn_right); |
|
760 |
|
761 default: |
|
762 return ENDS_IN_OTHER; |
|
763 } |
|
764 } |
|
765 |
|
766 static int |
|
767 HasFinalReturn(SyntaxParseHandler::Node pn) |
|
768 { |
|
769 return ENDS_IN_RETURN; |
|
770 } |
|
771 |
|
772 template <typename ParseHandler> |
|
773 bool |
|
774 Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind, |
|
775 unsigned errnum, unsigned anonerrnum) |
|
776 { |
|
777 JSAutoByteString name; |
|
778 JSAtom *atom = pc->sc->asFunctionBox()->function()->atom(); |
|
779 if (atom) { |
|
780 if (!AtomToPrintableString(context, atom, &name)) |
|
781 return false; |
|
782 } else { |
|
783 errnum = anonerrnum; |
|
784 } |
|
785 return report(kind, pc->sc->strict, pn, errnum, name.ptr()); |
|
786 } |
|
787 |
|
788 template <typename ParseHandler> |
|
789 bool |
|
790 Parser<ParseHandler>::checkFinalReturn(Node pn) |
|
791 { |
|
792 JS_ASSERT(pc->sc->isFunctionBox()); |
|
793 return HasFinalReturn(pn) == ENDS_IN_RETURN || |
|
794 reportBadReturn(pn, ParseExtraWarning, |
|
795 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE); |
|
796 } |
|
797 |
|
798 /* |
|
799 * Check that assigning to lhs is permitted. Assigning to 'eval' or |
|
800 * 'arguments' is banned in strict mode and in destructuring assignment. |
|
801 */ |
|
802 template <typename ParseHandler> |
|
803 bool |
|
804 Parser<ParseHandler>::checkStrictAssignment(Node lhs, AssignmentFlavor flavor) |
|
805 { |
|
806 if (!pc->sc->needStrictChecks() && flavor != KeyedDestructuringAssignment) |
|
807 return true; |
|
808 |
|
809 JSAtom *atom = handler.isName(lhs); |
|
810 if (!atom) |
|
811 return true; |
|
812 |
|
813 if (atom == context->names().eval || atom == context->names().arguments) { |
|
814 JSAutoByteString name; |
|
815 if (!AtomToPrintableString(context, atom, &name)) |
|
816 return false; |
|
817 |
|
818 ParseReportKind kind; |
|
819 unsigned errnum; |
|
820 if (pc->sc->strict || flavor != KeyedDestructuringAssignment) { |
|
821 kind = ParseStrictError; |
|
822 errnum = JSMSG_BAD_STRICT_ASSIGN; |
|
823 } else { |
|
824 kind = ParseError; |
|
825 errnum = JSMSG_BAD_DESTRUCT_ASSIGN; |
|
826 } |
|
827 if (!report(kind, pc->sc->strict, lhs, errnum, name.ptr())) |
|
828 return false; |
|
829 } |
|
830 return true; |
|
831 } |
|
832 |
|
833 /* |
|
834 * Check that it is permitted to introduce a binding for atom. Strict mode |
|
835 * forbids introducing new definitions for 'eval', 'arguments', or for any |
|
836 * strict mode reserved keyword. Use pn for reporting error locations, or use |
|
837 * pc's token stream if pn is nullptr. |
|
838 */ |
|
839 template <typename ParseHandler> |
|
840 bool |
|
841 Parser<ParseHandler>::checkStrictBinding(PropertyName *name, Node pn) |
|
842 { |
|
843 if (!pc->sc->needStrictChecks()) |
|
844 return true; |
|
845 |
|
846 if (name == context->names().eval || name == context->names().arguments || IsKeyword(name)) { |
|
847 JSAutoByteString bytes; |
|
848 if (!AtomToPrintableString(context, name, &bytes)) |
|
849 return false; |
|
850 return report(ParseStrictError, pc->sc->strict, pn, |
|
851 JSMSG_BAD_BINDING, bytes.ptr()); |
|
852 } |
|
853 |
|
854 return true; |
|
855 } |
|
856 |
|
857 template <> |
|
858 ParseNode * |
|
859 Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, |
|
860 GeneratorKind generatorKind, |
|
861 Directives inheritedDirectives, |
|
862 Directives *newDirectives) |
|
863 { |
|
864 Node fn = handler.newFunctionDefinition(); |
|
865 if (!fn) |
|
866 return null(); |
|
867 |
|
868 ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &handler); |
|
869 if (!argsbody) |
|
870 return null(); |
|
871 argsbody->setOp(JSOP_NOP); |
|
872 argsbody->makeEmpty(); |
|
873 fn->pn_body = argsbody; |
|
874 |
|
875 FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ nullptr, inheritedDirectives, |
|
876 generatorKind); |
|
877 if (!funbox) |
|
878 return null(); |
|
879 funbox->length = fun->nargs() - fun->hasRest(); |
|
880 handler.setFunctionBox(fn, funbox); |
|
881 |
|
882 ParseContext<FullParseHandler> funpc(this, pc, fn, funbox, newDirectives, |
|
883 /* staticLevel = */ 0, /* bodyid = */ 0, |
|
884 /* blockScopeDepth = */ 0); |
|
885 if (!funpc.init(tokenStream)) |
|
886 return null(); |
|
887 |
|
888 for (unsigned i = 0; i < formals.length(); i++) { |
|
889 if (!defineArg(fn, formals[i])) |
|
890 return null(); |
|
891 } |
|
892 |
|
893 ParseNode *pn = functionBody(Statement, StatementListBody); |
|
894 if (!pn) |
|
895 return null(); |
|
896 |
|
897 if (!tokenStream.matchToken(TOK_EOF)) { |
|
898 report(ParseError, false, null(), JSMSG_SYNTAX_ERROR); |
|
899 return null(); |
|
900 } |
|
901 |
|
902 if (!FoldConstants(context, &pn, this)) |
|
903 return null(); |
|
904 |
|
905 InternalHandle<Bindings*> funboxBindings = |
|
906 InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings); |
|
907 if (!funpc.generateFunctionBindings(context, tokenStream, alloc, funboxBindings)) |
|
908 return null(); |
|
909 |
|
910 JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); |
|
911 fn->pn_body->append(pn); |
|
912 fn->pn_body->pn_pos = pn->pn_pos; |
|
913 return fn; |
|
914 } |
|
915 |
|
916 template <> |
|
917 bool |
|
918 Parser<FullParseHandler>::checkFunctionArguments() |
|
919 { |
|
920 /* |
|
921 * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope |
|
922 * operation which means it aliases any bindings with the same name. |
|
923 */ |
|
924 if (FuncStmtSet *set = pc->funcStmts) { |
|
925 for (FuncStmtSet::Range r = set->all(); !r.empty(); r.popFront()) { |
|
926 PropertyName *name = r.front()->asPropertyName(); |
|
927 if (Definition *dn = pc->decls().lookupFirst(name)) |
|
928 dn->pn_dflags |= PND_CLOSED; |
|
929 } |
|
930 } |
|
931 |
|
932 /* Time to implement the odd semantics of 'arguments'. */ |
|
933 HandlePropertyName arguments = context->names().arguments; |
|
934 |
|
935 /* |
|
936 * As explained by the ContextFlags::funArgumentsHasLocalBinding comment, |
|
937 * create a declaration for 'arguments' if there are any unbound uses in |
|
938 * the function body. |
|
939 */ |
|
940 for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) { |
|
941 if (r.front().key() == arguments) { |
|
942 Definition *dn = r.front().value().get<FullParseHandler>(); |
|
943 pc->lexdeps->remove(arguments); |
|
944 dn->pn_dflags |= PND_IMPLICITARGUMENTS; |
|
945 if (!pc->define(tokenStream, arguments, dn, Definition::VAR)) |
|
946 return false; |
|
947 pc->sc->asFunctionBox()->usesArguments = true; |
|
948 break; |
|
949 } |
|
950 } |
|
951 |
|
952 /* |
|
953 * Report error if both rest parameters and 'arguments' are used. Do this |
|
954 * check before adding artificial 'arguments' below. |
|
955 */ |
|
956 Definition *maybeArgDef = pc->decls().lookupFirst(arguments); |
|
957 bool argumentsHasBinding = !!maybeArgDef; |
|
958 bool argumentsHasLocalBinding = maybeArgDef && maybeArgDef->kind() != Definition::ARG; |
|
959 bool hasRest = pc->sc->asFunctionBox()->function()->hasRest(); |
|
960 if (hasRest && argumentsHasLocalBinding) { |
|
961 report(ParseError, false, nullptr, JSMSG_ARGUMENTS_AND_REST); |
|
962 return false; |
|
963 } |
|
964 |
|
965 /* |
|
966 * Even if 'arguments' isn't explicitly mentioned, dynamic name lookup |
|
967 * forces an 'arguments' binding. The exception is that functions with rest |
|
968 * parameters are free from 'arguments'. |
|
969 */ |
|
970 if (!argumentsHasBinding && pc->sc->bindingsAccessedDynamically() && !hasRest) { |
|
971 ParseNode *pn = newName(arguments); |
|
972 if (!pn) |
|
973 return false; |
|
974 if (!pc->define(tokenStream, arguments, pn, Definition::VAR)) |
|
975 return false; |
|
976 argumentsHasBinding = true; |
|
977 argumentsHasLocalBinding = true; |
|
978 } |
|
979 |
|
980 /* |
|
981 * Now that all possible 'arguments' bindings have been added, note whether |
|
982 * 'arguments' has a local binding and whether it unconditionally needs an |
|
983 * arguments object. (Also see the flags' comments in ContextFlags.) |
|
984 */ |
|
985 if (argumentsHasLocalBinding) { |
|
986 FunctionBox *funbox = pc->sc->asFunctionBox(); |
|
987 funbox->setArgumentsHasLocalBinding(); |
|
988 |
|
989 /* |
|
990 * If a script has both explicit mentions of 'arguments' and dynamic |
|
991 * name lookups which could access the arguments, an arguments object |
|
992 * must be created eagerly. The SSA analysis used for lazy arguments |
|
993 * cannot cope with dynamic name accesses, so any 'arguments' accessed |
|
994 * via a NAME opcode must force construction of the arguments object. |
|
995 */ |
|
996 if (pc->sc->bindingsAccessedDynamically() && maybeArgDef) |
|
997 funbox->setDefinitelyNeedsArgsObj(); |
|
998 |
|
999 /* |
|
1000 * If a script contains the debugger statement either directly or |
|
1001 * within an inner function, the arguments object must be created |
|
1002 * eagerly. The debugger can walk the scope chain and observe any |
|
1003 * values along it. |
|
1004 */ |
|
1005 if (pc->sc->hasDebuggerStatement()) |
|
1006 funbox->setDefinitelyNeedsArgsObj(); |
|
1007 |
|
1008 /* |
|
1009 * Check whether any parameters have been assigned within this |
|
1010 * function. In strict mode parameters do not alias arguments[i], and |
|
1011 * to make the arguments object reflect initial parameter values prior |
|
1012 * to any mutation we create it eagerly whenever parameters are (or |
|
1013 * might, in the case of calls to eval) be assigned. |
|
1014 */ |
|
1015 if (pc->sc->needStrictChecks()) { |
|
1016 for (AtomDefnListMap::Range r = pc->decls().all(); !r.empty(); r.popFront()) { |
|
1017 DefinitionList &dlist = r.front().value(); |
|
1018 for (DefinitionList::Range dr = dlist.all(); !dr.empty(); dr.popFront()) { |
|
1019 Definition *dn = dr.front<FullParseHandler>(); |
|
1020 if (dn->kind() == Definition::ARG && dn->isAssigned()) |
|
1021 funbox->setDefinitelyNeedsArgsObj(); |
|
1022 } |
|
1023 } |
|
1024 /* Watch for mutation of arguments through e.g. eval(). */ |
|
1025 if (pc->sc->bindingsAccessedDynamically()) |
|
1026 funbox->setDefinitelyNeedsArgsObj(); |
|
1027 } |
|
1028 } |
|
1029 |
|
1030 return true; |
|
1031 } |
|
1032 |
|
1033 template <> |
|
1034 bool |
|
1035 Parser<SyntaxParseHandler>::checkFunctionArguments() |
|
1036 { |
|
1037 bool hasRest = pc->sc->asFunctionBox()->function()->hasRest(); |
|
1038 |
|
1039 if (pc->lexdeps->lookup(context->names().arguments)) { |
|
1040 pc->sc->asFunctionBox()->usesArguments = true; |
|
1041 if (hasRest) { |
|
1042 report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST); |
|
1043 return false; |
|
1044 } |
|
1045 } else if (hasRest) { |
|
1046 DefinitionNode maybeArgDef = pc->decls().lookupFirst(context->names().arguments); |
|
1047 if (maybeArgDef && handler.getDefinitionKind(maybeArgDef) != Definition::ARG) { |
|
1048 report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST); |
|
1049 return false; |
|
1050 } |
|
1051 } |
|
1052 |
|
1053 return true; |
|
1054 } |
|
1055 |
|
1056 template <typename ParseHandler> |
|
1057 typename ParseHandler::Node |
|
1058 Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType type) |
|
1059 { |
|
1060 JS_ASSERT(pc->sc->isFunctionBox()); |
|
1061 JS_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid); |
|
1062 |
|
1063 #ifdef DEBUG |
|
1064 uint32_t startYieldOffset = pc->lastYieldOffset; |
|
1065 #endif |
|
1066 |
|
1067 Node pn; |
|
1068 if (type == StatementListBody) { |
|
1069 pn = statements(); |
|
1070 if (!pn) |
|
1071 return null(); |
|
1072 } else { |
|
1073 JS_ASSERT(type == ExpressionBody); |
|
1074 JS_ASSERT(JS_HAS_EXPR_CLOSURES); |
|
1075 |
|
1076 Node kid = assignExpr(); |
|
1077 if (!kid) |
|
1078 return null(); |
|
1079 |
|
1080 pn = handler.newReturnStatement(kid, handler.getPosition(kid)); |
|
1081 if (!pn) |
|
1082 return null(); |
|
1083 } |
|
1084 |
|
1085 switch (pc->generatorKind()) { |
|
1086 case NotGenerator: |
|
1087 JS_ASSERT(pc->lastYieldOffset == startYieldOffset); |
|
1088 break; |
|
1089 |
|
1090 case LegacyGenerator: |
|
1091 // FIXME: Catch these errors eagerly, in yieldExpression(). |
|
1092 JS_ASSERT(pc->lastYieldOffset != startYieldOffset); |
|
1093 if (kind == Arrow) { |
|
1094 reportWithOffset(ParseError, false, pc->lastYieldOffset, |
|
1095 JSMSG_YIELD_IN_ARROW, js_yield_str); |
|
1096 return null(); |
|
1097 } |
|
1098 if (type == ExpressionBody) { |
|
1099 reportBadReturn(pn, ParseError, |
|
1100 JSMSG_BAD_GENERATOR_RETURN, |
|
1101 JSMSG_BAD_ANON_GENERATOR_RETURN); |
|
1102 return null(); |
|
1103 } |
|
1104 break; |
|
1105 |
|
1106 case StarGenerator: |
|
1107 JS_ASSERT(kind != Arrow); |
|
1108 JS_ASSERT(type == StatementListBody); |
|
1109 break; |
|
1110 } |
|
1111 |
|
1112 /* Check for falling off the end of a function that returns a value. */ |
|
1113 if (options().extraWarningsOption && pc->funHasReturnExpr && !checkFinalReturn(pn)) |
|
1114 return null(); |
|
1115 |
|
1116 /* Define the 'arguments' binding if necessary. */ |
|
1117 if (!checkFunctionArguments()) |
|
1118 return null(); |
|
1119 |
|
1120 return pn; |
|
1121 } |
|
1122 |
|
1123 /* See comment for use in Parser::functionDef. */ |
|
1124 template <> |
|
1125 bool |
|
1126 Parser<FullParseHandler>::makeDefIntoUse(Definition *dn, ParseNode *pn, JSAtom *atom) |
|
1127 { |
|
1128 /* Turn pn into a definition. */ |
|
1129 pc->updateDecl(atom, pn); |
|
1130 |
|
1131 /* Change all uses of dn to be uses of pn. */ |
|
1132 for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) { |
|
1133 JS_ASSERT(pnu->isUsed()); |
|
1134 JS_ASSERT(!pnu->isDefn()); |
|
1135 pnu->pn_lexdef = (Definition *) pn; |
|
1136 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS; |
|
1137 } |
|
1138 pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS; |
|
1139 pn->dn_uses = dn; |
|
1140 |
|
1141 /* |
|
1142 * A PNK_FUNCTION node must be a definition, so convert shadowed function |
|
1143 * statements into nops. This is valid since all body-level function |
|
1144 * statement initialization happens at the beginning of the function |
|
1145 * (thus, only the last statement's effect is visible). E.g., in |
|
1146 * |
|
1147 * function outer() { |
|
1148 * function g() { return 1 } |
|
1149 * assertEq(g(), 2); |
|
1150 * function g() { return 2 } |
|
1151 * assertEq(g(), 2); |
|
1152 * } |
|
1153 * |
|
1154 * both asserts are valid. |
|
1155 */ |
|
1156 if (dn->getKind() == PNK_FUNCTION) { |
|
1157 JS_ASSERT(dn->functionIsHoisted()); |
|
1158 pn->dn_uses = dn->pn_link; |
|
1159 handler.prepareNodeForMutation(dn); |
|
1160 dn->setKind(PNK_NOP); |
|
1161 dn->setArity(PN_NULLARY); |
|
1162 return true; |
|
1163 } |
|
1164 |
|
1165 /* |
|
1166 * If dn is arg, or in [var, const, let] and has an initializer, then we |
|
1167 * must rewrite it to be an assignment node, whose freshly allocated |
|
1168 * left-hand side becomes a use of pn. |
|
1169 */ |
|
1170 if (dn->canHaveInitializer()) { |
|
1171 if (ParseNode *rhs = dn->expr()) { |
|
1172 ParseNode *lhs = handler.makeAssignment(dn, rhs); |
|
1173 if (!lhs) |
|
1174 return false; |
|
1175 pn->dn_uses = lhs; |
|
1176 dn->pn_link = nullptr; |
|
1177 dn = (Definition *) lhs; |
|
1178 } |
|
1179 } |
|
1180 |
|
1181 /* Turn dn into a use of pn. */ |
|
1182 JS_ASSERT(dn->isKind(PNK_NAME)); |
|
1183 JS_ASSERT(dn->isArity(PN_NAME)); |
|
1184 JS_ASSERT(dn->pn_atom == atom); |
|
1185 dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME); |
|
1186 dn->setDefn(false); |
|
1187 dn->setUsed(true); |
|
1188 dn->pn_lexdef = (Definition *) pn; |
|
1189 dn->pn_cookie.makeFree(); |
|
1190 dn->pn_dflags &= ~PND_BOUND; |
|
1191 return true; |
|
1192 } |
|
1193 |
|
1194 /* |
|
1195 * Parameter block types for the several Binder functions. We use a common |
|
1196 * helper function signature in order to share code among destructuring and |
|
1197 * simple variable declaration parsers. In the destructuring case, the binder |
|
1198 * function is called indirectly from the variable declaration parser by way |
|
1199 * of CheckDestructuring and its friends. |
|
1200 */ |
|
1201 |
|
1202 template <typename ParseHandler> |
|
1203 struct BindData |
|
1204 { |
|
1205 BindData(ExclusiveContext *cx) : let(cx) {} |
|
1206 |
|
1207 typedef bool |
|
1208 (*Binder)(BindData *data, HandlePropertyName name, Parser<ParseHandler> *parser); |
|
1209 |
|
1210 /* name node for definition processing and error source coordinates */ |
|
1211 typename ParseHandler::Node pn; |
|
1212 |
|
1213 JSOp op; /* prolog bytecode or nop */ |
|
1214 Binder binder; /* binder, discriminates u */ |
|
1215 |
|
1216 struct LetData { |
|
1217 LetData(ExclusiveContext *cx) : blockObj(cx) {} |
|
1218 VarContext varContext; |
|
1219 RootedStaticBlockObject blockObj; |
|
1220 unsigned overflow; |
|
1221 } let; |
|
1222 |
|
1223 void initLet(VarContext varContext, StaticBlockObject &blockObj, unsigned overflow) { |
|
1224 this->pn = ParseHandler::null(); |
|
1225 this->op = JSOP_NOP; |
|
1226 this->binder = Parser<ParseHandler>::bindLet; |
|
1227 this->let.varContext = varContext; |
|
1228 this->let.blockObj = &blockObj; |
|
1229 this->let.overflow = overflow; |
|
1230 } |
|
1231 |
|
1232 void initVarOrConst(JSOp op) { |
|
1233 this->op = op; |
|
1234 this->binder = Parser<ParseHandler>::bindVarOrConst; |
|
1235 } |
|
1236 }; |
|
1237 |
|
1238 template <typename ParseHandler> |
|
1239 JSFunction * |
|
1240 Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom, |
|
1241 FunctionSyntaxKind kind, JSObject *proto) |
|
1242 { |
|
1243 JS_ASSERT_IF(kind == Statement, atom != nullptr); |
|
1244 |
|
1245 /* |
|
1246 * Find the global compilation context in order to pre-set the newborn |
|
1247 * function's parent slot to pc->sc->as<GlobalObject>()->scopeChain. If the |
|
1248 * global context is a compile-and-go one, we leave the pre-set parent |
|
1249 * intact; otherwise we clear parent and proto. |
|
1250 */ |
|
1251 while (pc->parent) |
|
1252 pc = pc->parent; |
|
1253 |
|
1254 RootedFunction fun(context); |
|
1255 JSFunction::Flags flags = (kind == Expression) |
|
1256 ? JSFunction::INTERPRETED_LAMBDA |
|
1257 : (kind == Arrow) |
|
1258 ? JSFunction::INTERPRETED_LAMBDA_ARROW |
|
1259 : JSFunction::INTERPRETED; |
|
1260 gc::AllocKind allocKind = JSFunction::FinalizeKind; |
|
1261 if (kind == Arrow) |
|
1262 allocKind = JSFunction::ExtendedFinalizeKind; |
|
1263 fun = NewFunctionWithProto(context, NullPtr(), nullptr, 0, flags, NullPtr(), atom, proto, |
|
1264 allocKind, MaybeSingletonObject); |
|
1265 if (!fun) |
|
1266 return nullptr; |
|
1267 if (options().selfHostingMode) |
|
1268 fun->setIsSelfHostedBuiltin(); |
|
1269 return fun; |
|
1270 } |
|
1271 |
|
1272 static bool |
|
1273 MatchOrInsertSemicolon(TokenStream &ts) |
|
1274 { |
|
1275 TokenKind tt = ts.peekTokenSameLine(TokenStream::Operand); |
|
1276 if (tt == TOK_ERROR) |
|
1277 return false; |
|
1278 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { |
|
1279 /* Advance the scanner for proper error location reporting. */ |
|
1280 ts.getToken(TokenStream::Operand); |
|
1281 ts.reportError(JSMSG_SEMI_BEFORE_STMNT); |
|
1282 return false; |
|
1283 } |
|
1284 (void) ts.matchToken(TOK_SEMI); |
|
1285 return true; |
|
1286 } |
|
1287 |
|
1288 template <typename ParseHandler> |
|
1289 typename ParseHandler::DefinitionNode |
|
1290 Parser<ParseHandler>::getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom) |
|
1291 { |
|
1292 AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(atom); |
|
1293 if (p) |
|
1294 return p.value().get<ParseHandler>(); |
|
1295 |
|
1296 DefinitionNode dn = handler.newPlaceholder(atom, pc->blockid(), pos()); |
|
1297 if (!dn) |
|
1298 return ParseHandler::nullDefinition(); |
|
1299 DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn); |
|
1300 if (!pc->lexdeps->add(p, atom, def)) |
|
1301 return ParseHandler::nullDefinition(); |
|
1302 return dn; |
|
1303 } |
|
1304 |
|
1305 static bool |
|
1306 ConvertDefinitionToNamedLambdaUse(TokenStream &ts, ParseContext<FullParseHandler> *pc, |
|
1307 FunctionBox *funbox, Definition *dn) |
|
1308 { |
|
1309 dn->setOp(JSOP_CALLEE); |
|
1310 if (!dn->pn_cookie.set(ts, pc->staticLevel, 0)) |
|
1311 return false; |
|
1312 dn->pn_dflags |= PND_BOUND; |
|
1313 JS_ASSERT(dn->kind() == Definition::NAMED_LAMBDA); |
|
1314 |
|
1315 /* |
|
1316 * Since 'dn' is a placeholder, it has not been defined in the |
|
1317 * ParseContext and hence we must manually flag a closed-over |
|
1318 * callee name as needing a dynamic scope (this is done for all |
|
1319 * definitions in the ParseContext by generateFunctionBindings). |
|
1320 * |
|
1321 * If 'dn' has been assigned to, then we also flag the function |
|
1322 * scope has needing a dynamic scope so that dynamic scope |
|
1323 * setter can either ignore the set (in non-strict mode) or |
|
1324 * produce an error (in strict mode). |
|
1325 */ |
|
1326 if (dn->isClosed() || dn->isAssigned()) |
|
1327 funbox->setNeedsDeclEnvObject(); |
|
1328 return true; |
|
1329 } |
|
1330 |
|
1331 /* |
|
1332 * Beware: this function is called for functions nested in other functions or |
|
1333 * global scripts but not for functions compiled through the Function |
|
1334 * constructor or JSAPI. To always execute code when a function has finished |
|
1335 * parsing, use Parser::functionBody. |
|
1336 */ |
|
1337 template <> |
|
1338 bool |
|
1339 Parser<FullParseHandler>::leaveFunction(ParseNode *fn, ParseContext<FullParseHandler> *outerpc, |
|
1340 FunctionSyntaxKind kind) |
|
1341 { |
|
1342 outerpc->blockidGen = pc->blockidGen; |
|
1343 |
|
1344 FunctionBox *funbox = fn->pn_funbox; |
|
1345 JS_ASSERT(funbox == pc->sc->asFunctionBox()); |
|
1346 |
|
1347 /* Propagate unresolved lexical names up to outerpc->lexdeps. */ |
|
1348 if (pc->lexdeps->count()) { |
|
1349 for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) { |
|
1350 JSAtom *atom = r.front().key(); |
|
1351 Definition *dn = r.front().value().get<FullParseHandler>(); |
|
1352 JS_ASSERT(dn->isPlaceholder()); |
|
1353 |
|
1354 if (atom == funbox->function()->name() && kind == Expression) { |
|
1355 if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn)) |
|
1356 return false; |
|
1357 continue; |
|
1358 } |
|
1359 |
|
1360 Definition *outer_dn = outerpc->decls().lookupFirst(atom); |
|
1361 |
|
1362 /* |
|
1363 * Make sure to deoptimize lexical dependencies that are polluted |
|
1364 * by eval and function statements (which both flag the function as |
|
1365 * having an extensible scope) or any enclosing 'with'. |
|
1366 */ |
|
1367 if (funbox->hasExtensibleScope() || outerpc->parsingWith) |
|
1368 handler.deoptimizeUsesWithin(dn, fn->pn_pos); |
|
1369 |
|
1370 if (!outer_dn) { |
|
1371 /* |
|
1372 * Create a new placeholder for our outer lexdep. We could |
|
1373 * simply re-use the inner placeholder, but that introduces |
|
1374 * subtleties in the case where we find a later definition |
|
1375 * that captures an existing lexdep. For example: |
|
1376 * |
|
1377 * function f() { function g() { x; } let x; } |
|
1378 * |
|
1379 * Here, g's TOK_UPVARS node lists the placeholder for x, |
|
1380 * which must be captured by the 'let' declaration later, |
|
1381 * since 'let's are hoisted. Taking g's placeholder as our |
|
1382 * own would work fine. But consider: |
|
1383 * |
|
1384 * function f() { x; { function g() { x; } let x; } } |
|
1385 * |
|
1386 * Here, the 'let' must not capture all the uses of f's |
|
1387 * lexdep entry for x, but it must capture the x node |
|
1388 * referred to from g's TOK_UPVARS node. Always turning |
|
1389 * inherited lexdeps into uses of a new outer definition |
|
1390 * allows us to handle both these cases in a natural way. |
|
1391 */ |
|
1392 outer_dn = getOrCreateLexicalDependency(outerpc, atom); |
|
1393 if (!outer_dn) |
|
1394 return false; |
|
1395 } |
|
1396 |
|
1397 /* |
|
1398 * Insert dn's uses list at the front of outer_dn's list. |
|
1399 * |
|
1400 * Without loss of generality or correctness, we allow a dn to |
|
1401 * be in inner and outer lexdeps, since the purpose of lexdeps |
|
1402 * is one-pass coordination of name use and definition across |
|
1403 * functions, and if different dn's are used we'll merge lists |
|
1404 * when leaving the inner function. |
|
1405 * |
|
1406 * The dn == outer_dn case arises with generator expressions |
|
1407 * (see LegacyCompExprTransplanter::transplant, the PN_CODE/PN_NAME |
|
1408 * case), and nowhere else, currently. |
|
1409 */ |
|
1410 if (dn != outer_dn) { |
|
1411 if (ParseNode *pnu = dn->dn_uses) { |
|
1412 while (true) { |
|
1413 pnu->pn_lexdef = outer_dn; |
|
1414 if (!pnu->pn_link) |
|
1415 break; |
|
1416 pnu = pnu->pn_link; |
|
1417 } |
|
1418 pnu->pn_link = outer_dn->dn_uses; |
|
1419 outer_dn->dn_uses = dn->dn_uses; |
|
1420 dn->dn_uses = nullptr; |
|
1421 } |
|
1422 |
|
1423 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER; |
|
1424 } |
|
1425 |
|
1426 /* Mark the outer dn as escaping. */ |
|
1427 outer_dn->pn_dflags |= PND_CLOSED; |
|
1428 } |
|
1429 } |
|
1430 |
|
1431 InternalHandle<Bindings*> bindings = |
|
1432 InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings); |
|
1433 return pc->generateFunctionBindings(context, tokenStream, alloc, bindings); |
|
1434 } |
|
1435 |
|
1436 template <> |
|
1437 bool |
|
1438 Parser<SyntaxParseHandler>::leaveFunction(Node fn, ParseContext<SyntaxParseHandler> *outerpc, |
|
1439 FunctionSyntaxKind kind) |
|
1440 { |
|
1441 outerpc->blockidGen = pc->blockidGen; |
|
1442 |
|
1443 FunctionBox *funbox = pc->sc->asFunctionBox(); |
|
1444 return addFreeVariablesFromLazyFunction(funbox->function(), outerpc); |
|
1445 } |
|
1446 |
|
1447 /* |
|
1448 * defineArg is called for both the arguments of a regular function definition |
|
1449 * and the arguments specified by the Function constructor. |
|
1450 * |
|
1451 * The 'disallowDuplicateArgs' bool indicates whether the use of another |
|
1452 * feature (destructuring or default arguments) disables duplicate arguments. |
|
1453 * (ECMA-262 requires us to support duplicate parameter names, but, for newer |
|
1454 * features, we consider the code to have "opted in" to higher standards and |
|
1455 * forbid duplicates.) |
|
1456 * |
|
1457 * If 'duplicatedArg' is non-null, then DefineArg assigns to it any previous |
|
1458 * argument with the same name. The caller may use this to report an error when |
|
1459 * one of the abovementioned features occurs after a duplicate. |
|
1460 */ |
|
1461 template <typename ParseHandler> |
|
1462 bool |
|
1463 Parser<ParseHandler>::defineArg(Node funcpn, HandlePropertyName name, |
|
1464 bool disallowDuplicateArgs, Node *duplicatedArg) |
|
1465 { |
|
1466 SharedContext *sc = pc->sc; |
|
1467 |
|
1468 /* Handle duplicate argument names. */ |
|
1469 if (DefinitionNode prevDecl = pc->decls().lookupFirst(name)) { |
|
1470 Node pn = handler.getDefinitionNode(prevDecl); |
|
1471 |
|
1472 /* |
|
1473 * Strict-mode disallows duplicate args. We may not know whether we are |
|
1474 * in strict mode or not (since the function body hasn't been parsed). |
|
1475 * In such cases, report will queue up the potential error and return |
|
1476 * 'true'. |
|
1477 */ |
|
1478 if (sc->needStrictChecks()) { |
|
1479 JSAutoByteString bytes; |
|
1480 if (!AtomToPrintableString(context, name, &bytes)) |
|
1481 return false; |
|
1482 if (!report(ParseStrictError, pc->sc->strict, pn, |
|
1483 JSMSG_DUPLICATE_FORMAL, bytes.ptr())) |
|
1484 { |
|
1485 return false; |
|
1486 } |
|
1487 } |
|
1488 |
|
1489 if (disallowDuplicateArgs) { |
|
1490 report(ParseError, false, pn, JSMSG_BAD_DUP_ARGS); |
|
1491 return false; |
|
1492 } |
|
1493 |
|
1494 if (duplicatedArg) |
|
1495 *duplicatedArg = pn; |
|
1496 |
|
1497 /* ParseContext::define assumes and asserts prevDecl is not in decls. */ |
|
1498 JS_ASSERT(handler.getDefinitionKind(prevDecl) == Definition::ARG); |
|
1499 pc->prepareToAddDuplicateArg(name, prevDecl); |
|
1500 } |
|
1501 |
|
1502 Node argpn = newName(name); |
|
1503 if (!argpn) |
|
1504 return false; |
|
1505 |
|
1506 if (!checkStrictBinding(name, argpn)) |
|
1507 return false; |
|
1508 |
|
1509 handler.addFunctionArgument(funcpn, argpn); |
|
1510 return pc->define(tokenStream, name, argpn, Definition::ARG); |
|
1511 } |
|
1512 |
|
1513 template <typename ParseHandler> |
|
1514 /* static */ bool |
|
1515 Parser<ParseHandler>::bindDestructuringArg(BindData<ParseHandler> *data, |
|
1516 HandlePropertyName name, Parser<ParseHandler> *parser) |
|
1517 { |
|
1518 ParseContext<ParseHandler> *pc = parser->pc; |
|
1519 JS_ASSERT(pc->sc->isFunctionBox()); |
|
1520 |
|
1521 if (pc->decls().lookupFirst(name)) { |
|
1522 parser->report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); |
|
1523 return false; |
|
1524 } |
|
1525 |
|
1526 if (!parser->checkStrictBinding(name, data->pn)) |
|
1527 return false; |
|
1528 |
|
1529 return pc->define(parser->tokenStream, name, data->pn, Definition::VAR); |
|
1530 } |
|
1531 |
|
1532 template <typename ParseHandler> |
|
1533 bool |
|
1534 Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, Node funcpn, |
|
1535 bool *hasRest) |
|
1536 { |
|
1537 FunctionBox *funbox = pc->sc->asFunctionBox(); |
|
1538 |
|
1539 *hasRest = false; |
|
1540 |
|
1541 bool parenFreeArrow = false; |
|
1542 if (kind == Arrow && tokenStream.peekToken() == TOK_NAME) { |
|
1543 parenFreeArrow = true; |
|
1544 } else { |
|
1545 if (tokenStream.getToken() != TOK_LP) { |
|
1546 report(ParseError, false, null(), |
|
1547 kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL); |
|
1548 return false; |
|
1549 } |
|
1550 |
|
1551 // Record the start of function source (for FunctionToString). If we |
|
1552 // are parenFreeArrow, we will set this below, after consuming the NAME. |
|
1553 funbox->setStart(tokenStream); |
|
1554 } |
|
1555 |
|
1556 Node argsbody = handler.newList(PNK_ARGSBODY); |
|
1557 if (!argsbody) |
|
1558 return false; |
|
1559 handler.setFunctionBody(funcpn, argsbody); |
|
1560 |
|
1561 if (parenFreeArrow || !tokenStream.matchToken(TOK_RP)) { |
|
1562 bool hasDefaults = false; |
|
1563 Node duplicatedArg = null(); |
|
1564 Node list = null(); |
|
1565 |
|
1566 do { |
|
1567 if (*hasRest) { |
|
1568 report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST); |
|
1569 return false; |
|
1570 } |
|
1571 |
|
1572 TokenKind tt = tokenStream.getToken(); |
|
1573 JS_ASSERT_IF(parenFreeArrow, tt == TOK_NAME); |
|
1574 switch (tt) { |
|
1575 case TOK_LB: |
|
1576 case TOK_LC: |
|
1577 { |
|
1578 /* See comment below in the TOK_NAME case. */ |
|
1579 if (duplicatedArg) { |
|
1580 report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS); |
|
1581 return false; |
|
1582 } |
|
1583 |
|
1584 if (hasDefaults) { |
|
1585 report(ParseError, false, null(), JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT); |
|
1586 return false; |
|
1587 } |
|
1588 |
|
1589 funbox->hasDestructuringArgs = true; |
|
1590 |
|
1591 /* |
|
1592 * A destructuring formal parameter turns into one or more |
|
1593 * local variables initialized from properties of a single |
|
1594 * anonymous positional parameter, so here we must tweak our |
|
1595 * binder and its data. |
|
1596 */ |
|
1597 BindData<ParseHandler> data(context); |
|
1598 data.pn = ParseHandler::null(); |
|
1599 data.op = JSOP_DEFVAR; |
|
1600 data.binder = bindDestructuringArg; |
|
1601 Node lhs = destructuringExpr(&data, tt); |
|
1602 if (!lhs) |
|
1603 return false; |
|
1604 |
|
1605 /* |
|
1606 * Synthesize a destructuring assignment from the single |
|
1607 * anonymous positional parameter into the destructuring |
|
1608 * left-hand-side expression and accumulate it in list. |
|
1609 */ |
|
1610 HandlePropertyName name = context->names().empty; |
|
1611 Node rhs = newName(name); |
|
1612 if (!rhs) |
|
1613 return false; |
|
1614 |
|
1615 if (!pc->define(tokenStream, name, rhs, Definition::ARG)) |
|
1616 return false; |
|
1617 |
|
1618 Node item = handler.newBinary(PNK_ASSIGN, lhs, rhs); |
|
1619 if (!item) |
|
1620 return false; |
|
1621 if (list) { |
|
1622 handler.addList(list, item); |
|
1623 } else { |
|
1624 list = handler.newList(PNK_VAR, item); |
|
1625 if (!list) |
|
1626 return false; |
|
1627 *listp = list; |
|
1628 } |
|
1629 break; |
|
1630 } |
|
1631 |
|
1632 case TOK_YIELD: |
|
1633 if (!checkYieldNameValidity()) |
|
1634 return false; |
|
1635 goto TOK_NAME; |
|
1636 |
|
1637 case TOK_TRIPLEDOT: |
|
1638 { |
|
1639 *hasRest = true; |
|
1640 tt = tokenStream.getToken(); |
|
1641 if (tt != TOK_NAME) { |
|
1642 if (tt != TOK_ERROR) |
|
1643 report(ParseError, false, null(), JSMSG_NO_REST_NAME); |
|
1644 return false; |
|
1645 } |
|
1646 goto TOK_NAME; |
|
1647 } |
|
1648 |
|
1649 TOK_NAME: |
|
1650 case TOK_NAME: |
|
1651 { |
|
1652 if (parenFreeArrow) |
|
1653 funbox->setStart(tokenStream); |
|
1654 |
|
1655 RootedPropertyName name(context, tokenStream.currentName()); |
|
1656 bool disallowDuplicateArgs = funbox->hasDestructuringArgs || hasDefaults; |
|
1657 if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg)) |
|
1658 return false; |
|
1659 |
|
1660 if (tokenStream.matchToken(TOK_ASSIGN)) { |
|
1661 // A default argument without parentheses would look like: |
|
1662 // a = expr => body, but both operators are right-associative, so |
|
1663 // that would have been parsed as a = (expr => body) instead. |
|
1664 // Therefore it's impossible to get here with parenFreeArrow. |
|
1665 JS_ASSERT(!parenFreeArrow); |
|
1666 |
|
1667 if (*hasRest) { |
|
1668 report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT); |
|
1669 return false; |
|
1670 } |
|
1671 if (duplicatedArg) { |
|
1672 report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS); |
|
1673 return false; |
|
1674 } |
|
1675 if (!hasDefaults) { |
|
1676 hasDefaults = true; |
|
1677 |
|
1678 // The Function.length property is the number of formals |
|
1679 // before the first default argument. |
|
1680 funbox->length = pc->numArgs() - 1; |
|
1681 } |
|
1682 Node def_expr = assignExprWithoutYield(JSMSG_YIELD_IN_DEFAULT); |
|
1683 if (!def_expr) |
|
1684 return false; |
|
1685 handler.setLastFunctionArgumentDefault(funcpn, def_expr); |
|
1686 } |
|
1687 |
|
1688 break; |
|
1689 } |
|
1690 |
|
1691 default: |
|
1692 report(ParseError, false, null(), JSMSG_MISSING_FORMAL); |
|
1693 /* FALL THROUGH */ |
|
1694 case TOK_ERROR: |
|
1695 return false; |
|
1696 } |
|
1697 } while (!parenFreeArrow && tokenStream.matchToken(TOK_COMMA)); |
|
1698 |
|
1699 if (!parenFreeArrow && tokenStream.getToken() != TOK_RP) { |
|
1700 report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL); |
|
1701 return false; |
|
1702 } |
|
1703 |
|
1704 if (!hasDefaults) |
|
1705 funbox->length = pc->numArgs() - *hasRest; |
|
1706 } |
|
1707 |
|
1708 return true; |
|
1709 } |
|
1710 |
|
1711 template <> |
|
1712 bool |
|
1713 Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName, |
|
1714 ParseNode **pn_, FunctionSyntaxKind kind, |
|
1715 bool *pbodyProcessed) |
|
1716 { |
|
1717 ParseNode *&pn = *pn_; |
|
1718 *pbodyProcessed = false; |
|
1719 |
|
1720 /* Function statements add a binding to the enclosing scope. */ |
|
1721 bool bodyLevel = pc->atBodyLevel(); |
|
1722 |
|
1723 if (kind == Statement) { |
|
1724 /* |
|
1725 * Handle redeclaration and optimize cases where we can statically bind the |
|
1726 * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). |
|
1727 */ |
|
1728 if (Definition *dn = pc->decls().lookupFirst(funName)) { |
|
1729 JS_ASSERT(!dn->isUsed()); |
|
1730 JS_ASSERT(dn->isDefn()); |
|
1731 |
|
1732 if (options().extraWarningsOption || dn->kind() == Definition::CONST) { |
|
1733 JSAutoByteString name; |
|
1734 ParseReportKind reporter = (dn->kind() != Definition::CONST) |
|
1735 ? ParseExtraWarning |
|
1736 : ParseError; |
|
1737 if (!AtomToPrintableString(context, funName, &name) || |
|
1738 !report(reporter, false, nullptr, JSMSG_REDECLARED_VAR, |
|
1739 Definition::kindString(dn->kind()), name.ptr())) |
|
1740 { |
|
1741 return false; |
|
1742 } |
|
1743 } |
|
1744 |
|
1745 /* |
|
1746 * Body-level function statements are effectively variable |
|
1747 * declarations where the initialization is hoisted to the |
|
1748 * beginning of the block. This means that any other variable |
|
1749 * declaration with the same name is really just an assignment to |
|
1750 * the function's binding (which is mutable), so turn any existing |
|
1751 * declaration into a use. |
|
1752 */ |
|
1753 if (bodyLevel && !makeDefIntoUse(dn, pn, funName)) |
|
1754 return false; |
|
1755 } else if (bodyLevel) { |
|
1756 /* |
|
1757 * If this function was used before it was defined, claim the |
|
1758 * pre-created definition node for this function that primaryExpr |
|
1759 * put in pc->lexdeps on first forward reference, and recycle pn. |
|
1760 */ |
|
1761 if (Definition *fn = pc->lexdeps.lookupDefn<FullParseHandler>(funName)) { |
|
1762 JS_ASSERT(fn->isDefn()); |
|
1763 fn->setKind(PNK_FUNCTION); |
|
1764 fn->setArity(PN_CODE); |
|
1765 fn->pn_pos.begin = pn->pn_pos.begin; |
|
1766 fn->pn_pos.end = pn->pn_pos.end; |
|
1767 |
|
1768 fn->pn_body = nullptr; |
|
1769 fn->pn_cookie.makeFree(); |
|
1770 |
|
1771 pc->lexdeps->remove(funName); |
|
1772 handler.freeTree(pn); |
|
1773 pn = fn; |
|
1774 } |
|
1775 |
|
1776 if (!pc->define(tokenStream, funName, pn, Definition::VAR)) |
|
1777 return false; |
|
1778 } |
|
1779 |
|
1780 if (bodyLevel) { |
|
1781 JS_ASSERT(pn->functionIsHoisted()); |
|
1782 JS_ASSERT_IF(pc->sc->isFunctionBox(), !pn->pn_cookie.isFree()); |
|
1783 JS_ASSERT_IF(!pc->sc->isFunctionBox(), pn->pn_cookie.isFree()); |
|
1784 } else { |
|
1785 /* |
|
1786 * As a SpiderMonkey-specific extension, non-body-level function |
|
1787 * statements (e.g., functions in an "if" or "while" block) are |
|
1788 * dynamically bound when control flow reaches the statement. |
|
1789 */ |
|
1790 JS_ASSERT(!pc->sc->strict); |
|
1791 JS_ASSERT(pn->pn_cookie.isFree()); |
|
1792 if (pc->sc->isFunctionBox()) { |
|
1793 FunctionBox *funbox = pc->sc->asFunctionBox(); |
|
1794 funbox->setMightAliasLocals(); |
|
1795 funbox->setHasExtensibleScope(); |
|
1796 } |
|
1797 pn->setOp(JSOP_DEFFUN); |
|
1798 |
|
1799 /* |
|
1800 * Instead of setting bindingsAccessedDynamically, which would be |
|
1801 * overly conservative, remember the names of all function |
|
1802 * statements and mark any bindings with the same as aliased at the |
|
1803 * end of functionBody. |
|
1804 */ |
|
1805 if (!pc->funcStmts) { |
|
1806 pc->funcStmts = context->new_<FuncStmtSet>(context); |
|
1807 if (!pc->funcStmts || !pc->funcStmts->init()) |
|
1808 return false; |
|
1809 } |
|
1810 if (!pc->funcStmts->put(funName)) |
|
1811 return false; |
|
1812 |
|
1813 /* |
|
1814 * Due to the implicit declaration mechanism, 'arguments' will not |
|
1815 * have decls and, even if it did, they will not be noted as closed |
|
1816 * in the emitter. Thus, in the corner case of function statements |
|
1817 * overridding arguments, flag the whole scope as dynamic. |
|
1818 */ |
|
1819 if (funName == context->names().arguments) |
|
1820 pc->sc->setBindingsAccessedDynamically(); |
|
1821 } |
|
1822 |
|
1823 /* No further binding (in BindNameToSlot) is needed for functions. */ |
|
1824 pn->pn_dflags |= PND_BOUND; |
|
1825 } else { |
|
1826 /* A function expression does not introduce any binding. */ |
|
1827 pn->setOp(kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA); |
|
1828 } |
|
1829 |
|
1830 // When a lazily-parsed function is called, we only fully parse (and emit) |
|
1831 // that function, not any of its nested children. The initial syntax-only |
|
1832 // parse recorded the free variables of nested functions and their extents, |
|
1833 // so we can skip over them after accounting for their free variables. |
|
1834 if (LazyScript *lazyOuter = handler.lazyOuterFunction()) { |
|
1835 JSFunction *fun = handler.nextLazyInnerFunction(); |
|
1836 JS_ASSERT(!fun->isLegacyGenerator()); |
|
1837 FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false), |
|
1838 fun->generatorKind()); |
|
1839 if (!funbox) |
|
1840 return false; |
|
1841 |
|
1842 if (!addFreeVariablesFromLazyFunction(fun, pc)) |
|
1843 return false; |
|
1844 |
|
1845 // The position passed to tokenStream.advance() is relative to |
|
1846 // userbuf.base() while LazyScript::{begin,end} offsets are relative to |
|
1847 // the outermost script source. N.B: userbuf.base() is initialized |
|
1848 // (in TokenStream()) to begin() - column() so that column numbers in |
|
1849 // the lazily parsed script are correct. |
|
1850 uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column(); |
|
1851 tokenStream.advance(fun->lazyScript()->end() - userbufBase); |
|
1852 |
|
1853 *pbodyProcessed = true; |
|
1854 return true; |
|
1855 } |
|
1856 |
|
1857 return true; |
|
1858 } |
|
1859 |
|
1860 template <class T, class U> |
|
1861 static inline void |
|
1862 PropagateTransitiveParseFlags(const T *inner, U *outer) |
|
1863 { |
|
1864 if (inner->bindingsAccessedDynamically()) |
|
1865 outer->setBindingsAccessedDynamically(); |
|
1866 if (inner->hasDebuggerStatement()) |
|
1867 outer->setHasDebuggerStatement(); |
|
1868 } |
|
1869 |
|
1870 template <typename ParseHandler> |
|
1871 bool |
|
1872 Parser<ParseHandler>::addFreeVariablesFromLazyFunction(JSFunction *fun, |
|
1873 ParseContext<ParseHandler> *pc) |
|
1874 { |
|
1875 // Update any definition nodes in this context according to free variables |
|
1876 // in a lazily parsed inner function. |
|
1877 |
|
1878 LazyScript *lazy = fun->lazyScript(); |
|
1879 HeapPtrAtom *freeVariables = lazy->freeVariables(); |
|
1880 for (size_t i = 0; i < lazy->numFreeVariables(); i++) { |
|
1881 JSAtom *atom = freeVariables[i]; |
|
1882 |
|
1883 // 'arguments' will be implicitly bound within the inner function. |
|
1884 if (atom == context->names().arguments) |
|
1885 continue; |
|
1886 |
|
1887 DefinitionNode dn = pc->decls().lookupFirst(atom); |
|
1888 |
|
1889 if (!dn) { |
|
1890 dn = getOrCreateLexicalDependency(pc, atom); |
|
1891 if (!dn) |
|
1892 return false; |
|
1893 } |
|
1894 |
|
1895 /* Mark the outer dn as escaping. */ |
|
1896 handler.setFlag(handler.getDefinitionNode(dn), PND_CLOSED); |
|
1897 } |
|
1898 |
|
1899 PropagateTransitiveParseFlags(lazy, pc->sc); |
|
1900 return true; |
|
1901 } |
|
1902 |
|
1903 template <> |
|
1904 bool |
|
1905 Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName, |
|
1906 Node *pn, FunctionSyntaxKind kind, |
|
1907 bool *pbodyProcessed) |
|
1908 { |
|
1909 *pbodyProcessed = false; |
|
1910 |
|
1911 /* Function statements add a binding to the enclosing scope. */ |
|
1912 bool bodyLevel = pc->atBodyLevel(); |
|
1913 |
|
1914 if (kind == Statement) { |
|
1915 /* |
|
1916 * Handle redeclaration and optimize cases where we can statically bind the |
|
1917 * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). |
|
1918 */ |
|
1919 if (DefinitionNode dn = pc->decls().lookupFirst(funName)) { |
|
1920 if (dn == Definition::CONST) { |
|
1921 JSAutoByteString name; |
|
1922 if (!AtomToPrintableString(context, funName, &name) || |
|
1923 !report(ParseError, false, null(), JSMSG_REDECLARED_VAR, |
|
1924 Definition::kindString(dn), name.ptr())) |
|
1925 { |
|
1926 return false; |
|
1927 } |
|
1928 } |
|
1929 } else if (bodyLevel) { |
|
1930 if (pc->lexdeps.lookupDefn<SyntaxParseHandler>(funName)) |
|
1931 pc->lexdeps->remove(funName); |
|
1932 |
|
1933 if (!pc->define(tokenStream, funName, *pn, Definition::VAR)) |
|
1934 return false; |
|
1935 } |
|
1936 |
|
1937 if (!bodyLevel && funName == context->names().arguments) |
|
1938 pc->sc->setBindingsAccessedDynamically(); |
|
1939 } |
|
1940 |
|
1941 if (kind == Arrow) { |
|
1942 /* Arrow functions cannot yet be parsed lazily. */ |
|
1943 return abortIfSyntaxParser(); |
|
1944 } |
|
1945 |
|
1946 return true; |
|
1947 } |
|
1948 |
|
1949 template <typename ParseHandler> |
|
1950 typename ParseHandler::Node |
|
1951 Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start, |
|
1952 FunctionType type, FunctionSyntaxKind kind, |
|
1953 GeneratorKind generatorKind) |
|
1954 { |
|
1955 JS_ASSERT_IF(kind == Statement, funName); |
|
1956 |
|
1957 /* Make a TOK_FUNCTION node. */ |
|
1958 Node pn = handler.newFunctionDefinition(); |
|
1959 if (!pn) |
|
1960 return null(); |
|
1961 |
|
1962 bool bodyProcessed; |
|
1963 if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed)) |
|
1964 return null(); |
|
1965 |
|
1966 if (bodyProcessed) |
|
1967 return pn; |
|
1968 |
|
1969 RootedObject proto(context); |
|
1970 if (generatorKind == StarGenerator) { |
|
1971 // If we are off the main thread, the generator meta-objects have |
|
1972 // already been created by js::StartOffThreadParseScript, so cx will not |
|
1973 // be necessary. |
|
1974 JSContext *cx = context->maybeJSContext(); |
|
1975 proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global()); |
|
1976 if (!proto) |
|
1977 return null(); |
|
1978 } |
|
1979 RootedFunction fun(context, newFunction(pc, funName, kind, proto)); |
|
1980 if (!fun) |
|
1981 return null(); |
|
1982 |
|
1983 // Speculatively parse using the directives of the parent parsing context. |
|
1984 // If a directive is encountered (e.g., "use strict") that changes how the |
|
1985 // function should have been parsed, we backup and reparse with the new set |
|
1986 // of directives. |
|
1987 Directives directives(pc); |
|
1988 Directives newDirectives = directives; |
|
1989 |
|
1990 while (true) { |
|
1991 if (functionArgsAndBody(pn, fun, type, kind, generatorKind, directives, &newDirectives)) |
|
1992 break; |
|
1993 if (tokenStream.hadError() || directives == newDirectives) |
|
1994 return null(); |
|
1995 |
|
1996 // Assignment must be monotonic to prevent reparsing iloops |
|
1997 JS_ASSERT_IF(directives.strict(), newDirectives.strict()); |
|
1998 JS_ASSERT_IF(directives.asmJS(), newDirectives.asmJS()); |
|
1999 directives = newDirectives; |
|
2000 |
|
2001 tokenStream.seek(start); |
|
2002 if (funName && tokenStream.getToken() == TOK_ERROR) |
|
2003 return null(); |
|
2004 |
|
2005 // functionArgsAndBody may have already set pn->pn_body before failing. |
|
2006 handler.setFunctionBody(pn, null()); |
|
2007 } |
|
2008 |
|
2009 return pn; |
|
2010 } |
|
2011 |
|
2012 template <> |
|
2013 bool |
|
2014 Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *funbox, |
|
2015 ParseNode *prelude, ParseNode *body) |
|
2016 { |
|
2017 pn->pn_pos.end = pos().end; |
|
2018 |
|
2019 /* |
|
2020 * If there were destructuring formal parameters, prepend the initializing |
|
2021 * comma expression that we synthesized to body. If the body is a return |
|
2022 * node, we must make a special PNK_SEQ node, to prepend the destructuring |
|
2023 * code without bracing the decompilation of the function body. |
|
2024 */ |
|
2025 if (prelude) { |
|
2026 if (!body->isArity(PN_LIST)) { |
|
2027 ParseNode *block; |
|
2028 |
|
2029 block = ListNode::create(PNK_SEQ, &handler); |
|
2030 if (!block) |
|
2031 return false; |
|
2032 block->pn_pos = body->pn_pos; |
|
2033 block->initList(body); |
|
2034 |
|
2035 body = block; |
|
2036 } |
|
2037 |
|
2038 ParseNode *item = UnaryNode::create(PNK_SEMI, &handler); |
|
2039 if (!item) |
|
2040 return false; |
|
2041 |
|
2042 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin; |
|
2043 item->pn_kid = prelude; |
|
2044 item->pn_next = body->pn_head; |
|
2045 body->pn_head = item; |
|
2046 if (body->pn_tail == &body->pn_head) |
|
2047 body->pn_tail = &item->pn_next; |
|
2048 ++body->pn_count; |
|
2049 body->pn_xflags |= PNX_DESTRUCT; |
|
2050 } |
|
2051 |
|
2052 JS_ASSERT(pn->pn_funbox == funbox); |
|
2053 JS_ASSERT(pn->pn_body->isKind(PNK_ARGSBODY)); |
|
2054 pn->pn_body->append(body); |
|
2055 pn->pn_body->pn_pos = body->pn_pos; |
|
2056 |
|
2057 return true; |
|
2058 } |
|
2059 |
|
2060 template <> |
|
2061 bool |
|
2062 Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbox, |
|
2063 Node prelude, Node body) |
|
2064 { |
|
2065 // The LazyScript for a lazily parsed function needs to be constructed |
|
2066 // while its ParseContext and associated lexdeps and inner functions are |
|
2067 // still available. |
|
2068 |
|
2069 if (funbox->inWith) |
|
2070 return abortIfSyntaxParser(); |
|
2071 |
|
2072 size_t numFreeVariables = pc->lexdeps->count(); |
|
2073 size_t numInnerFunctions = pc->innerFunctions.length(); |
|
2074 |
|
2075 RootedFunction fun(context, funbox->function()); |
|
2076 LazyScript *lazy = LazyScript::CreateRaw(context, fun, numFreeVariables, numInnerFunctions, |
|
2077 versionNumber(), funbox->bufStart, funbox->bufEnd, |
|
2078 funbox->startLine, funbox->startColumn); |
|
2079 if (!lazy) |
|
2080 return false; |
|
2081 |
|
2082 HeapPtrAtom *freeVariables = lazy->freeVariables(); |
|
2083 size_t i = 0; |
|
2084 for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) |
|
2085 freeVariables[i++].init(r.front().key()); |
|
2086 JS_ASSERT(i == numFreeVariables); |
|
2087 |
|
2088 HeapPtrFunction *innerFunctions = lazy->innerFunctions(); |
|
2089 for (size_t i = 0; i < numInnerFunctions; i++) |
|
2090 innerFunctions[i].init(pc->innerFunctions[i]); |
|
2091 |
|
2092 if (pc->sc->strict) |
|
2093 lazy->setStrict(); |
|
2094 lazy->setGeneratorKind(funbox->generatorKind()); |
|
2095 if (funbox->usesArguments && funbox->usesApply) |
|
2096 lazy->setUsesArgumentsAndApply(); |
|
2097 PropagateTransitiveParseFlags(funbox, lazy); |
|
2098 |
|
2099 fun->initLazyScript(lazy); |
|
2100 return true; |
|
2101 } |
|
2102 |
|
2103 template <> |
|
2104 bool |
|
2105 Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun, |
|
2106 FunctionType type, FunctionSyntaxKind kind, |
|
2107 GeneratorKind generatorKind, |
|
2108 Directives inheritedDirectives, |
|
2109 Directives *newDirectives) |
|
2110 { |
|
2111 ParseContext<FullParseHandler> *outerpc = pc; |
|
2112 |
|
2113 // Create box for fun->object early to protect against last-ditch GC. |
|
2114 FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind); |
|
2115 if (!funbox) |
|
2116 return false; |
|
2117 |
|
2118 // Try a syntax parse for this inner function. |
|
2119 do { |
|
2120 Parser<SyntaxParseHandler> *parser = handler.syntaxParser; |
|
2121 if (!parser) |
|
2122 break; |
|
2123 |
|
2124 { |
|
2125 // Move the syntax parser to the current position in the stream. |
|
2126 TokenStream::Position position(keepAtoms); |
|
2127 tokenStream.tell(&position); |
|
2128 if (!parser->tokenStream.seek(position, tokenStream)) |
|
2129 return false; |
|
2130 |
|
2131 ParseContext<SyntaxParseHandler> funpc(parser, outerpc, SyntaxParseHandler::null(), funbox, |
|
2132 newDirectives, outerpc->staticLevel + 1, |
|
2133 outerpc->blockidGen, |
|
2134 /* blockScopeDepth = */ 0); |
|
2135 if (!funpc.init(tokenStream)) |
|
2136 return false; |
|
2137 |
|
2138 if (!parser->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric, |
|
2139 fun, type, kind, newDirectives)) |
|
2140 { |
|
2141 if (parser->hadAbortedSyntaxParse()) { |
|
2142 // Try again with a full parse. |
|
2143 parser->clearAbortedSyntaxParse(); |
|
2144 break; |
|
2145 } |
|
2146 return false; |
|
2147 } |
|
2148 |
|
2149 outerpc->blockidGen = funpc.blockidGen; |
|
2150 |
|
2151 // Advance this parser over tokens processed by the syntax parser. |
|
2152 parser->tokenStream.tell(&position); |
|
2153 if (!tokenStream.seek(position, parser->tokenStream)) |
|
2154 return false; |
|
2155 |
|
2156 // Update the end position of the parse node. |
|
2157 pn->pn_pos.end = tokenStream.currentToken().pos.end; |
|
2158 } |
|
2159 |
|
2160 if (!addFreeVariablesFromLazyFunction(fun, pc)) |
|
2161 return false; |
|
2162 |
|
2163 pn->pn_blockid = outerpc->blockid(); |
|
2164 PropagateTransitiveParseFlags(funbox, outerpc->sc); |
|
2165 return true; |
|
2166 } while (false); |
|
2167 |
|
2168 // Continue doing a full parse for this inner function. |
|
2169 ParseContext<FullParseHandler> funpc(this, pc, pn, funbox, newDirectives, |
|
2170 outerpc->staticLevel + 1, outerpc->blockidGen, |
|
2171 /* blockScopeDepth = */ 0); |
|
2172 if (!funpc.init(tokenStream)) |
|
2173 return false; |
|
2174 |
|
2175 if (!functionArgsAndBodyGeneric(pn, fun, type, kind, newDirectives)) |
|
2176 return false; |
|
2177 |
|
2178 if (!leaveFunction(pn, outerpc, kind)) |
|
2179 return false; |
|
2180 |
|
2181 pn->pn_blockid = outerpc->blockid(); |
|
2182 |
|
2183 /* |
|
2184 * Fruit of the poisonous tree: if a closure contains a dynamic name access |
|
2185 * (eval, with, etc), we consider the parent to do the same. The reason is |
|
2186 * that the deoptimizing effects of dynamic name access apply equally to |
|
2187 * parents: any local can be read at runtime. |
|
2188 */ |
|
2189 PropagateTransitiveParseFlags(funbox, outerpc->sc); |
|
2190 return true; |
|
2191 } |
|
2192 |
|
2193 template <> |
|
2194 bool |
|
2195 Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, |
|
2196 FunctionType type, FunctionSyntaxKind kind, |
|
2197 GeneratorKind generatorKind, |
|
2198 Directives inheritedDirectives, |
|
2199 Directives *newDirectives) |
|
2200 { |
|
2201 ParseContext<SyntaxParseHandler> *outerpc = pc; |
|
2202 |
|
2203 // Create box for fun->object early to protect against last-ditch GC. |
|
2204 FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind); |
|
2205 if (!funbox) |
|
2206 return false; |
|
2207 |
|
2208 // Initialize early for possible flags mutation via destructuringExpr. |
|
2209 ParseContext<SyntaxParseHandler> funpc(this, pc, handler.null(), funbox, newDirectives, |
|
2210 outerpc->staticLevel + 1, outerpc->blockidGen, |
|
2211 /* blockScopeDepth = */ 0); |
|
2212 if (!funpc.init(tokenStream)) |
|
2213 return false; |
|
2214 |
|
2215 if (!functionArgsAndBodyGeneric(pn, fun, type, kind, newDirectives)) |
|
2216 return false; |
|
2217 |
|
2218 if (!leaveFunction(pn, outerpc, kind)) |
|
2219 return false; |
|
2220 |
|
2221 // This is a lazy function inner to another lazy function. Remember the |
|
2222 // inner function so that if the outer function is eventually parsed we do |
|
2223 // not need any further parsing or processing of the inner function. |
|
2224 JS_ASSERT(fun->lazyScript()); |
|
2225 return outerpc->innerFunctions.append(fun); |
|
2226 } |
|
2227 |
|
2228 template <> |
|
2229 ParseNode * |
|
2230 Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, |
|
2231 bool strict, GeneratorKind generatorKind) |
|
2232 { |
|
2233 Node pn = handler.newFunctionDefinition(); |
|
2234 if (!pn) |
|
2235 return null(); |
|
2236 |
|
2237 Directives directives(/* strict = */ strict); |
|
2238 FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ nullptr, directives, |
|
2239 generatorKind); |
|
2240 if (!funbox) |
|
2241 return null(); |
|
2242 funbox->length = fun->nargs() - fun->hasRest(); |
|
2243 |
|
2244 Directives newDirectives = directives; |
|
2245 ParseContext<FullParseHandler> funpc(this, /* parent = */ nullptr, pn, funbox, |
|
2246 &newDirectives, staticLevel, /* bodyid = */ 0, |
|
2247 /* blockScopeDepth = */ 0); |
|
2248 if (!funpc.init(tokenStream)) |
|
2249 return null(); |
|
2250 |
|
2251 if (!functionArgsAndBodyGeneric(pn, fun, Normal, Statement, &newDirectives)) { |
|
2252 JS_ASSERT(directives == newDirectives); |
|
2253 return null(); |
|
2254 } |
|
2255 |
|
2256 if (fun->isNamedLambda()) { |
|
2257 if (AtomDefnPtr p = pc->lexdeps->lookup(fun->name())) { |
|
2258 Definition *dn = p.value().get<FullParseHandler>(); |
|
2259 if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn)) |
|
2260 return nullptr; |
|
2261 } |
|
2262 } |
|
2263 |
|
2264 InternalHandle<Bindings*> bindings = |
|
2265 InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings); |
|
2266 if (!pc->generateFunctionBindings(context, tokenStream, alloc, bindings)) |
|
2267 return null(); |
|
2268 |
|
2269 if (!FoldConstants(context, &pn, this)) |
|
2270 return null(); |
|
2271 |
|
2272 return pn; |
|
2273 } |
|
2274 |
|
2275 template <typename ParseHandler> |
|
2276 bool |
|
2277 Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type, |
|
2278 FunctionSyntaxKind kind, |
|
2279 Directives *newDirectives) |
|
2280 { |
|
2281 // Given a properly initialized parse context, try to parse an actual |
|
2282 // function without concern for conversion to strict mode, use of lazy |
|
2283 // parsing and such. |
|
2284 |
|
2285 Node prelude = null(); |
|
2286 bool hasRest; |
|
2287 if (!functionArguments(kind, &prelude, pn, &hasRest)) |
|
2288 return false; |
|
2289 |
|
2290 FunctionBox *funbox = pc->sc->asFunctionBox(); |
|
2291 |
|
2292 fun->setArgCount(pc->numArgs()); |
|
2293 if (hasRest) |
|
2294 fun->setHasRest(); |
|
2295 |
|
2296 if (type == Getter && fun->nargs() > 0) { |
|
2297 report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s"); |
|
2298 return false; |
|
2299 } |
|
2300 if (type == Setter && fun->nargs() != 1) { |
|
2301 report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); |
|
2302 return false; |
|
2303 } |
|
2304 |
|
2305 if (kind == Arrow && !tokenStream.matchToken(TOK_ARROW)) { |
|
2306 report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS); |
|
2307 return false; |
|
2308 } |
|
2309 |
|
2310 // Parse the function body. |
|
2311 FunctionBodyType bodyType = StatementListBody; |
|
2312 if (tokenStream.getToken(TokenStream::Operand) != TOK_LC) { |
|
2313 if (funbox->isStarGenerator()) { |
|
2314 report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY); |
|
2315 return false; |
|
2316 } |
|
2317 tokenStream.ungetToken(); |
|
2318 bodyType = ExpressionBody; |
|
2319 fun->setIsExprClosure(); |
|
2320 } |
|
2321 |
|
2322 Node body = functionBody(kind, bodyType); |
|
2323 if (!body) |
|
2324 return false; |
|
2325 |
|
2326 if (fun->name() && !checkStrictBinding(fun->name(), pn)) |
|
2327 return false; |
|
2328 |
|
2329 #if JS_HAS_EXPR_CLOSURES |
|
2330 if (bodyType == StatementListBody) { |
|
2331 #endif |
|
2332 if (!tokenStream.matchToken(TOK_RC)) { |
|
2333 report(ParseError, false, null(), JSMSG_CURLY_AFTER_BODY); |
|
2334 return false; |
|
2335 } |
|
2336 funbox->bufEnd = pos().begin + 1; |
|
2337 #if JS_HAS_EXPR_CLOSURES |
|
2338 } else { |
|
2339 if (tokenStream.hadError()) |
|
2340 return false; |
|
2341 funbox->bufEnd = pos().end; |
|
2342 if (kind == Statement && !MatchOrInsertSemicolon(tokenStream)) |
|
2343 return false; |
|
2344 } |
|
2345 #endif |
|
2346 |
|
2347 return finishFunctionDefinition(pn, funbox, prelude, body); |
|
2348 } |
|
2349 |
|
2350 template <typename ParseHandler> |
|
2351 bool |
|
2352 Parser<ParseHandler>::checkYieldNameValidity() |
|
2353 { |
|
2354 // In star generators and in JS >= 1.7, yield is a keyword. Otherwise in |
|
2355 // strict mode, yield is a future reserved word. |
|
2356 if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7 || pc->sc->strict) { |
|
2357 report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield"); |
|
2358 return false; |
|
2359 } |
|
2360 return true; |
|
2361 } |
|
2362 |
|
2363 template <typename ParseHandler> |
|
2364 typename ParseHandler::Node |
|
2365 Parser<ParseHandler>::functionStmt() |
|
2366 { |
|
2367 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); |
|
2368 |
|
2369 TokenStream::Position start(keepAtoms); |
|
2370 tokenStream.tell(&start); |
|
2371 |
|
2372 RootedPropertyName name(context); |
|
2373 GeneratorKind generatorKind = NotGenerator; |
|
2374 TokenKind tt = tokenStream.getToken(); |
|
2375 |
|
2376 if (tt == TOK_MUL) { |
|
2377 tokenStream.tell(&start); |
|
2378 tt = tokenStream.getToken(); |
|
2379 generatorKind = StarGenerator; |
|
2380 } |
|
2381 |
|
2382 if (tt == TOK_NAME) { |
|
2383 name = tokenStream.currentName(); |
|
2384 } else if (tt == TOK_YIELD) { |
|
2385 if (!checkYieldNameValidity()) |
|
2386 return null(); |
|
2387 name = tokenStream.currentName(); |
|
2388 } else { |
|
2389 /* Unnamed function expressions are forbidden in statement context. */ |
|
2390 report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT); |
|
2391 return null(); |
|
2392 } |
|
2393 |
|
2394 /* We forbid function statements in strict mode code. */ |
|
2395 if (!pc->atBodyLevel() && pc->sc->needStrictChecks() && |
|
2396 !report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT)) |
|
2397 return null(); |
|
2398 |
|
2399 return functionDef(name, start, Normal, Statement, generatorKind); |
|
2400 } |
|
2401 |
|
2402 template <typename ParseHandler> |
|
2403 typename ParseHandler::Node |
|
2404 Parser<ParseHandler>::functionExpr() |
|
2405 { |
|
2406 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); |
|
2407 |
|
2408 TokenStream::Position start(keepAtoms); |
|
2409 tokenStream.tell(&start); |
|
2410 |
|
2411 GeneratorKind generatorKind = NotGenerator; |
|
2412 TokenKind tt = tokenStream.getToken(); |
|
2413 |
|
2414 if (tt == TOK_MUL) { |
|
2415 tokenStream.tell(&start); |
|
2416 tt = tokenStream.getToken(); |
|
2417 generatorKind = StarGenerator; |
|
2418 } |
|
2419 |
|
2420 RootedPropertyName name(context); |
|
2421 if (tt == TOK_NAME) { |
|
2422 name = tokenStream.currentName(); |
|
2423 } else if (tt == TOK_YIELD) { |
|
2424 if (!checkYieldNameValidity()) |
|
2425 return null(); |
|
2426 name = tokenStream.currentName(); |
|
2427 } else { |
|
2428 tokenStream.ungetToken(); |
|
2429 } |
|
2430 |
|
2431 return functionDef(name, start, Normal, Expression, generatorKind); |
|
2432 } |
|
2433 |
|
2434 /* |
|
2435 * Return true if this node, known to be an unparenthesized string literal, |
|
2436 * could be the string of a directive in a Directive Prologue. Directive |
|
2437 * strings never contain escape sequences or line continuations. |
|
2438 * isEscapeFreeStringLiteral, below, checks whether the node itself could be |
|
2439 * a directive. |
|
2440 */ |
|
2441 static inline bool |
|
2442 IsEscapeFreeStringLiteral(const TokenPos &pos, JSAtom *str) |
|
2443 { |
|
2444 /* |
|
2445 * If the string's length in the source code is its length as a value, |
|
2446 * accounting for the quotes, then it must not contain any escape |
|
2447 * sequences or line continuations. |
|
2448 */ |
|
2449 return pos.begin + str->length() + 2 == pos.end; |
|
2450 } |
|
2451 |
|
2452 template <> |
|
2453 bool |
|
2454 Parser<SyntaxParseHandler>::asmJS(Node list) |
|
2455 { |
|
2456 // While asm.js could technically be validated and compiled during syntax |
|
2457 // parsing, we have no guarantee that some later JS wouldn't abort the |
|
2458 // syntax parse and cause us to re-parse (and re-compile) the asm.js module. |
|
2459 // For simplicity, unconditionally abort the syntax parse when "use asm" is |
|
2460 // encountered so that asm.js is always validated/compiled exactly once |
|
2461 // during a full parse. |
|
2462 JS_ALWAYS_FALSE(abortIfSyntaxParser()); |
|
2463 return false; |
|
2464 } |
|
2465 |
|
2466 template <> |
|
2467 bool |
|
2468 Parser<FullParseHandler>::asmJS(Node list) |
|
2469 { |
|
2470 // If we are already inside "use asm" that means we are either actively |
|
2471 // compiling or we are reparsing after asm.js validation failure. In either |
|
2472 // case, nothing to do here. |
|
2473 if (pc->useAsmOrInsideUseAsm()) |
|
2474 return true; |
|
2475 |
|
2476 // If there is no ScriptSource, then we are doing a non-compiling parse and |
|
2477 // so we shouldn't (and can't, without a ScriptSource) compile. |
|
2478 if (ss == nullptr) |
|
2479 return true; |
|
2480 |
|
2481 pc->sc->asFunctionBox()->useAsm = true; |
|
2482 |
|
2483 #ifdef JS_ION |
|
2484 // Attempt to validate and compile this asm.js module. On success, the |
|
2485 // tokenStream has been advanced to the closing }. On failure, the |
|
2486 // tokenStream is in an indeterminate state and we must reparse the |
|
2487 // function from the beginning. Reparsing is triggered by marking that a |
|
2488 // new directive has been encountered and returning 'false'. |
|
2489 bool validated; |
|
2490 if (!CompileAsmJS(context, *this, list, &validated)) |
|
2491 return false; |
|
2492 if (!validated) { |
|
2493 pc->newDirectives->setAsmJS(); |
|
2494 return false; |
|
2495 } |
|
2496 #endif |
|
2497 |
|
2498 return true; |
|
2499 } |
|
2500 |
|
2501 /* |
|
2502 * Recognize Directive Prologue members and directives. Assuming |pn| is a |
|
2503 * candidate for membership in a directive prologue, recognize directives and |
|
2504 * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its |
|
2505 * |pn_prologue| flag. |
|
2506 * |
|
2507 * Note that the following is a strict mode function: |
|
2508 * |
|
2509 * function foo() { |
|
2510 * "blah" // inserted semi colon |
|
2511 * "blurgh" |
|
2512 * "use\x20loose" |
|
2513 * "use strict" |
|
2514 * } |
|
2515 * |
|
2516 * That is, even though "use\x20loose" can never be a directive, now or in the |
|
2517 * future (because of the hex escape), the Directive Prologue extends through it |
|
2518 * to the "use strict" statement, which is indeed a directive. |
|
2519 */ |
|
2520 template <typename ParseHandler> |
|
2521 bool |
|
2522 Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool *cont) |
|
2523 { |
|
2524 TokenPos directivePos; |
|
2525 JSAtom *directive = handler.isStringExprStatement(pn, &directivePos); |
|
2526 |
|
2527 *cont = !!directive; |
|
2528 if (!*cont) |
|
2529 return true; |
|
2530 |
|
2531 if (IsEscapeFreeStringLiteral(directivePos, directive)) { |
|
2532 // Mark this statement as being a possibly legitimate part of a |
|
2533 // directive prologue, so the bytecode emitter won't warn about it being |
|
2534 // useless code. (We mustn't just omit the statement entirely yet, as it |
|
2535 // could be producing the value of an eval or JSScript execution.) |
|
2536 // |
|
2537 // Note that even if the string isn't one we recognize as a directive, |
|
2538 // the emitter still shouldn't flag it as useless, as it could become a |
|
2539 // directive in the future. We don't want to interfere with people |
|
2540 // taking advantage of directive-prologue-enabled features that appear |
|
2541 // in other browsers first. |
|
2542 handler.setPrologue(pn); |
|
2543 |
|
2544 if (directive == context->names().useStrict) { |
|
2545 // We're going to be in strict mode. Note that this scope explicitly |
|
2546 // had "use strict"; |
|
2547 pc->sc->setExplicitUseStrict(); |
|
2548 if (!pc->sc->strict) { |
|
2549 if (pc->sc->isFunctionBox()) { |
|
2550 // Request that this function be reparsed as strict. |
|
2551 pc->newDirectives->setStrict(); |
|
2552 return false; |
|
2553 } else { |
|
2554 // We don't reparse global scopes, so we keep track of the |
|
2555 // one possible strict violation that could occur in the |
|
2556 // directive prologue -- octal escapes -- and complain now. |
|
2557 if (tokenStream.sawOctalEscape()) { |
|
2558 report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL); |
|
2559 return false; |
|
2560 } |
|
2561 pc->sc->strict = true; |
|
2562 } |
|
2563 } |
|
2564 } else if (directive == context->names().useAsm) { |
|
2565 if (pc->sc->isFunctionBox()) |
|
2566 return asmJS(list); |
|
2567 return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL); |
|
2568 } |
|
2569 } |
|
2570 return true; |
|
2571 } |
|
2572 |
|
2573 /* |
|
2574 * Parse the statements in a block, creating a StatementList node that lists |
|
2575 * the statements. If called from block-parsing code, the caller must match |
|
2576 * '{' before and '}' after. |
|
2577 */ |
|
2578 template <typename ParseHandler> |
|
2579 typename ParseHandler::Node |
|
2580 Parser<ParseHandler>::statements() |
|
2581 { |
|
2582 JS_CHECK_RECURSION(context, return null()); |
|
2583 |
|
2584 Node pn = handler.newStatementList(pc->blockid(), pos()); |
|
2585 if (!pn) |
|
2586 return null(); |
|
2587 |
|
2588 Node saveBlock = pc->blockNode; |
|
2589 pc->blockNode = pn; |
|
2590 |
|
2591 bool canHaveDirectives = pc->atBodyLevel(); |
|
2592 for (;;) { |
|
2593 TokenKind tt = tokenStream.peekToken(TokenStream::Operand); |
|
2594 if (tt <= TOK_EOF || tt == TOK_RC) { |
|
2595 if (tt == TOK_ERROR) { |
|
2596 if (tokenStream.isEOF()) |
|
2597 isUnexpectedEOF_ = true; |
|
2598 return null(); |
|
2599 } |
|
2600 break; |
|
2601 } |
|
2602 Node next = statement(canHaveDirectives); |
|
2603 if (!next) { |
|
2604 if (tokenStream.isEOF()) |
|
2605 isUnexpectedEOF_ = true; |
|
2606 return null(); |
|
2607 } |
|
2608 |
|
2609 if (canHaveDirectives) { |
|
2610 if (!maybeParseDirective(pn, next, &canHaveDirectives)) |
|
2611 return null(); |
|
2612 } |
|
2613 |
|
2614 handler.addStatementToList(pn, next, pc); |
|
2615 } |
|
2616 |
|
2617 /* |
|
2618 * Handle the case where there was a let declaration under this block. If |
|
2619 * it replaced pc->blockNode with a new block node then we must refresh pn |
|
2620 * and then restore pc->blockNode. |
|
2621 */ |
|
2622 if (pc->blockNode != pn) |
|
2623 pn = pc->blockNode; |
|
2624 pc->blockNode = saveBlock; |
|
2625 return pn; |
|
2626 } |
|
2627 |
|
2628 template <typename ParseHandler> |
|
2629 typename ParseHandler::Node |
|
2630 Parser<ParseHandler>::condition() |
|
2631 { |
|
2632 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND); |
|
2633 Node pn = exprInParens(); |
|
2634 if (!pn) |
|
2635 return null(); |
|
2636 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); |
|
2637 |
|
2638 /* Check for (a = b) and warn about possible (a == b) mistype. */ |
|
2639 if (handler.isOperationWithoutParens(pn, PNK_ASSIGN) && |
|
2640 !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) |
|
2641 { |
|
2642 return null(); |
|
2643 } |
|
2644 return pn; |
|
2645 } |
|
2646 |
|
2647 template <typename ParseHandler> |
|
2648 bool |
|
2649 Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label) |
|
2650 { |
|
2651 TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand); |
|
2652 if (tt == TOK_ERROR) |
|
2653 return false; |
|
2654 if (tt == TOK_NAME) { |
|
2655 tokenStream.consumeKnownToken(TOK_NAME); |
|
2656 label.set(tokenStream.currentName()); |
|
2657 } else if (tt == TOK_YIELD) { |
|
2658 tokenStream.consumeKnownToken(TOK_YIELD); |
|
2659 if (!checkYieldNameValidity()) |
|
2660 return false; |
|
2661 label.set(tokenStream.currentName()); |
|
2662 } else { |
|
2663 label.set(nullptr); |
|
2664 } |
|
2665 return true; |
|
2666 } |
|
2667 |
|
2668 template <typename ParseHandler> |
|
2669 bool |
|
2670 Parser<ParseHandler>::reportRedeclaration(Node pn, bool isConst, JSAtom *atom) |
|
2671 { |
|
2672 JSAutoByteString name; |
|
2673 if (AtomToPrintableString(context, atom, &name)) |
|
2674 report(ParseError, false, pn, JSMSG_REDECLARED_VAR, isConst ? "const" : "variable", name.ptr()); |
|
2675 return false; |
|
2676 } |
|
2677 |
|
2678 /* |
|
2679 * Define a let-variable in a block, let-expression, or comprehension scope. pc |
|
2680 * must already be in such a scope. |
|
2681 * |
|
2682 * Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a |
|
2683 * property for the new variable on the block object, pc->staticScope; |
|
2684 * populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to |
|
2685 * data->pn in a slot of the block object. |
|
2686 */ |
|
2687 template <> |
|
2688 /* static */ bool |
|
2689 Parser<FullParseHandler>::bindLet(BindData<FullParseHandler> *data, |
|
2690 HandlePropertyName name, Parser<FullParseHandler> *parser) |
|
2691 { |
|
2692 ParseContext<FullParseHandler> *pc = parser->pc; |
|
2693 ParseNode *pn = data->pn; |
|
2694 if (!parser->checkStrictBinding(name, pn)) |
|
2695 return false; |
|
2696 |
|
2697 ExclusiveContext *cx = parser->context; |
|
2698 |
|
2699 Rooted<StaticBlockObject *> blockObj(cx, data->let.blockObj); |
|
2700 unsigned index = blockObj->numVariables(); |
|
2701 if (index >= StaticBlockObject::LOCAL_INDEX_LIMIT) { |
|
2702 parser->report(ParseError, false, pn, data->let.overflow); |
|
2703 return false; |
|
2704 } |
|
2705 |
|
2706 /* |
|
2707 * Assign block-local index to pn->pn_cookie right away, encoding it as an |
|
2708 * upvar cookie whose skip tells the current static level. The emitter will |
|
2709 * adjust the node's slot based on its stack depth model -- and, for global |
|
2710 * and eval code, js::frontend::CompileScript will adjust the slot |
|
2711 * again to include script->nfixed. |
|
2712 */ |
|
2713 if (!pn->pn_cookie.set(parser->tokenStream, pc->staticLevel, index)) |
|
2714 return false; |
|
2715 |
|
2716 /* |
|
2717 * For bindings that are hoisted to the beginning of the block/function, |
|
2718 * define() right now. Otherwise, delay define until PushLetScope. |
|
2719 */ |
|
2720 if (data->let.varContext == HoistVars) { |
|
2721 JS_ASSERT(!pc->atBodyLevel()); |
|
2722 Definition *dn = pc->decls().lookupFirst(name); |
|
2723 if (dn && dn->pn_blockid == pc->blockid()) |
|
2724 return parser->reportRedeclaration(pn, dn->isConst(), name); |
|
2725 if (!pc->define(parser->tokenStream, name, pn, Definition::LET)) |
|
2726 return false; |
|
2727 } |
|
2728 |
|
2729 bool redeclared; |
|
2730 RootedId id(cx, NameToId(name)); |
|
2731 RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id, index, &redeclared)); |
|
2732 if (!shape) { |
|
2733 if (redeclared) |
|
2734 parser->reportRedeclaration(pn, false, name); |
|
2735 return false; |
|
2736 } |
|
2737 |
|
2738 /* Store pn in the static block object. */ |
|
2739 blockObj->setDefinitionParseNode(index, reinterpret_cast<Definition *>(pn)); |
|
2740 return true; |
|
2741 } |
|
2742 |
|
2743 template <> |
|
2744 /* static */ bool |
|
2745 Parser<SyntaxParseHandler>::bindLet(BindData<SyntaxParseHandler> *data, |
|
2746 HandlePropertyName name, Parser<SyntaxParseHandler> *parser) |
|
2747 { |
|
2748 if (!parser->checkStrictBinding(name, data->pn)) |
|
2749 return false; |
|
2750 |
|
2751 return true; |
|
2752 } |
|
2753 |
|
2754 template <typename ParseHandler, class Op> |
|
2755 static inline bool |
|
2756 ForEachLetDef(TokenStream &ts, ParseContext<ParseHandler> *pc, |
|
2757 HandleStaticBlockObject blockObj, Op op) |
|
2758 { |
|
2759 for (Shape::Range<CanGC> r(ts.context(), blockObj->lastProperty()); !r.empty(); r.popFront()) { |
|
2760 Shape &shape = r.front(); |
|
2761 |
|
2762 /* Beware the destructuring dummy slots. */ |
|
2763 if (JSID_IS_INT(shape.propid())) |
|
2764 continue; |
|
2765 |
|
2766 if (!op(ts, pc, blockObj, shape, JSID_TO_ATOM(shape.propid()))) |
|
2767 return false; |
|
2768 } |
|
2769 return true; |
|
2770 } |
|
2771 |
|
2772 template <typename ParseHandler> |
|
2773 struct PopLetDecl { |
|
2774 bool operator()(TokenStream &, ParseContext<ParseHandler> *pc, HandleStaticBlockObject, |
|
2775 const Shape &, JSAtom *atom) |
|
2776 { |
|
2777 pc->popLetDecl(atom); |
|
2778 return true; |
|
2779 } |
|
2780 }; |
|
2781 |
|
2782 // We compute the maximum block scope depth, in slots, of a compilation unit at |
|
2783 // parse-time. Each nested statement has a field indicating the maximum block |
|
2784 // scope depth that is nested inside it. When we leave a nested statement, we |
|
2785 // add the number of slots in the statement to the nested depth, and use that to |
|
2786 // update the maximum block scope depth of the outer statement or parse |
|
2787 // context. In the end, pc->blockScopeDepth will indicate the number of slots |
|
2788 // to reserve in the fixed part of a stack frame. |
|
2789 // |
|
2790 template <typename ParseHandler> |
|
2791 static void |
|
2792 AccumulateBlockScopeDepth(ParseContext<ParseHandler> *pc) |
|
2793 { |
|
2794 uint32_t innerDepth = pc->topStmt->innerBlockScopeDepth; |
|
2795 StmtInfoPC *outer = pc->topStmt->down; |
|
2796 |
|
2797 if (pc->topStmt->isBlockScope) |
|
2798 innerDepth += pc->topStmt->staticScope->template as<StaticBlockObject>().numVariables(); |
|
2799 |
|
2800 if (outer) { |
|
2801 if (outer->innerBlockScopeDepth < innerDepth) |
|
2802 outer->innerBlockScopeDepth = innerDepth; |
|
2803 } else { |
|
2804 if (pc->blockScopeDepth < innerDepth) |
|
2805 pc->blockScopeDepth = innerDepth; |
|
2806 } |
|
2807 } |
|
2808 |
|
2809 template <typename ParseHandler> |
|
2810 static void |
|
2811 PopStatementPC(TokenStream &ts, ParseContext<ParseHandler> *pc) |
|
2812 { |
|
2813 RootedNestedScopeObject scopeObj(ts.context(), pc->topStmt->staticScope); |
|
2814 JS_ASSERT(!!scopeObj == pc->topStmt->isNestedScope); |
|
2815 |
|
2816 AccumulateBlockScopeDepth(pc); |
|
2817 FinishPopStatement(pc); |
|
2818 |
|
2819 if (scopeObj) { |
|
2820 if (scopeObj->is<StaticBlockObject>()) { |
|
2821 RootedStaticBlockObject blockObj(ts.context(), &scopeObj->as<StaticBlockObject>()); |
|
2822 JS_ASSERT(!blockObj->inDictionaryMode()); |
|
2823 ForEachLetDef(ts, pc, blockObj, PopLetDecl<ParseHandler>()); |
|
2824 } |
|
2825 scopeObj->resetEnclosingNestedScopeFromParser(); |
|
2826 } |
|
2827 } |
|
2828 |
|
2829 /* |
|
2830 * The function LexicalLookup searches a static binding for the given name in |
|
2831 * the stack of statements enclosing the statement currently being parsed. Each |
|
2832 * statement that introduces a new scope has a corresponding scope object, on |
|
2833 * which the bindings for that scope are stored. LexicalLookup either returns |
|
2834 * the innermost statement which has a scope object containing a binding with |
|
2835 * the given name, or nullptr. |
|
2836 */ |
|
2837 template <class ContextT> |
|
2838 typename ContextT::StmtInfo * |
|
2839 LexicalLookup(ContextT *ct, HandleAtom atom, int *slotp, typename ContextT::StmtInfo *stmt) |
|
2840 { |
|
2841 RootedId id(ct->sc->context, AtomToId(atom)); |
|
2842 |
|
2843 if (!stmt) |
|
2844 stmt = ct->topScopeStmt; |
|
2845 for (; stmt; stmt = stmt->downScope) { |
|
2846 /* |
|
2847 * With-statements introduce dynamic bindings. Since dynamic bindings |
|
2848 * can potentially override any static bindings introduced by statements |
|
2849 * further up the stack, we have to abort the search. |
|
2850 */ |
|
2851 if (stmt->type == STMT_WITH) |
|
2852 break; |
|
2853 |
|
2854 // Skip statements that do not introduce a new scope |
|
2855 if (!stmt->isBlockScope) |
|
2856 continue; |
|
2857 |
|
2858 StaticBlockObject &blockObj = stmt->staticBlock(); |
|
2859 Shape *shape = blockObj.nativeLookup(ct->sc->context, id); |
|
2860 if (shape) { |
|
2861 if (slotp) |
|
2862 *slotp = blockObj.shapeToIndex(*shape); |
|
2863 return stmt; |
|
2864 } |
|
2865 } |
|
2866 |
|
2867 if (slotp) |
|
2868 *slotp = -1; |
|
2869 return stmt; |
|
2870 } |
|
2871 |
|
2872 template <typename ParseHandler> |
|
2873 static inline bool |
|
2874 OuterLet(ParseContext<ParseHandler> *pc, StmtInfoPC *stmt, HandleAtom atom) |
|
2875 { |
|
2876 while (stmt->downScope) { |
|
2877 stmt = LexicalLookup(pc, atom, nullptr, stmt->downScope); |
|
2878 if (!stmt) |
|
2879 return false; |
|
2880 if (stmt->type == STMT_BLOCK) |
|
2881 return true; |
|
2882 } |
|
2883 return false; |
|
2884 } |
|
2885 |
|
2886 template <typename ParseHandler> |
|
2887 /* static */ bool |
|
2888 Parser<ParseHandler>::bindVarOrConst(BindData<ParseHandler> *data, |
|
2889 HandlePropertyName name, Parser<ParseHandler> *parser) |
|
2890 { |
|
2891 ExclusiveContext *cx = parser->context; |
|
2892 ParseContext<ParseHandler> *pc = parser->pc; |
|
2893 Node pn = data->pn; |
|
2894 bool isConstDecl = data->op == JSOP_DEFCONST; |
|
2895 |
|
2896 /* Default best op for pn is JSOP_NAME; we'll try to improve below. */ |
|
2897 parser->handler.setOp(pn, JSOP_NAME); |
|
2898 |
|
2899 if (!parser->checkStrictBinding(name, pn)) |
|
2900 return false; |
|
2901 |
|
2902 StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC *)nullptr); |
|
2903 |
|
2904 if (stmt && stmt->type == STMT_WITH) { |
|
2905 parser->handler.setFlag(pn, PND_DEOPTIMIZED); |
|
2906 if (pc->sc->isFunctionBox()) { |
|
2907 FunctionBox *funbox = pc->sc->asFunctionBox(); |
|
2908 funbox->setMightAliasLocals(); |
|
2909 } |
|
2910 |
|
2911 /* |
|
2912 * This definition isn't being added to the parse context's |
|
2913 * declarations, so make sure to indicate the need to deoptimize |
|
2914 * the script's arguments object. Mark the function as if it |
|
2915 * contained a debugger statement, which will deoptimize arguments |
|
2916 * as much as possible. |
|
2917 */ |
|
2918 if (name == cx->names().arguments) |
|
2919 pc->sc->setHasDebuggerStatement(); |
|
2920 |
|
2921 return true; |
|
2922 } |
|
2923 |
|
2924 DefinitionList::Range defs = pc->decls().lookupMulti(name); |
|
2925 JS_ASSERT_IF(stmt, !defs.empty()); |
|
2926 |
|
2927 if (defs.empty()) { |
|
2928 return pc->define(parser->tokenStream, name, pn, |
|
2929 isConstDecl ? Definition::CONST : Definition::VAR); |
|
2930 } |
|
2931 |
|
2932 /* |
|
2933 * There was a previous declaration with the same name. The standard |
|
2934 * disallows several forms of redeclaration. Critically, |
|
2935 * let (x) { var x; } // error |
|
2936 * is not allowed which allows us to turn any non-error redeclaration |
|
2937 * into a use of the initial declaration. |
|
2938 */ |
|
2939 DefinitionNode dn = defs.front<ParseHandler>(); |
|
2940 Definition::Kind dn_kind = parser->handler.getDefinitionKind(dn); |
|
2941 if (dn_kind == Definition::ARG) { |
|
2942 JSAutoByteString bytes; |
|
2943 if (!AtomToPrintableString(cx, name, &bytes)) |
|
2944 return false; |
|
2945 |
|
2946 if (isConstDecl) { |
|
2947 parser->report(ParseError, false, pn, JSMSG_REDECLARED_PARAM, bytes.ptr()); |
|
2948 return false; |
|
2949 } |
|
2950 if (!parser->report(ParseExtraWarning, false, pn, JSMSG_VAR_HIDES_ARG, bytes.ptr())) |
|
2951 return false; |
|
2952 } else { |
|
2953 bool error = (isConstDecl || |
|
2954 dn_kind == Definition::CONST || |
|
2955 (dn_kind == Definition::LET && |
|
2956 (stmt->type != STMT_CATCH || OuterLet(pc, stmt, name)))); |
|
2957 |
|
2958 if (parser->options().extraWarningsOption |
|
2959 ? data->op != JSOP_DEFVAR || dn_kind != Definition::VAR |
|
2960 : error) |
|
2961 { |
|
2962 JSAutoByteString bytes; |
|
2963 ParseReportKind reporter = error ? ParseError : ParseExtraWarning; |
|
2964 if (!AtomToPrintableString(cx, name, &bytes) || |
|
2965 !parser->report(reporter, false, pn, JSMSG_REDECLARED_VAR, |
|
2966 Definition::kindString(dn_kind), bytes.ptr())) |
|
2967 { |
|
2968 return false; |
|
2969 } |
|
2970 } |
|
2971 } |
|
2972 |
|
2973 parser->handler.linkUseToDef(pn, dn); |
|
2974 return true; |
|
2975 } |
|
2976 |
|
2977 template <> |
|
2978 bool |
|
2979 Parser<FullParseHandler>::makeSetCall(ParseNode *pn, unsigned msg) |
|
2980 { |
|
2981 JS_ASSERT(pn->isKind(PNK_CALL)); |
|
2982 JS_ASSERT(pn->isArity(PN_LIST)); |
|
2983 JS_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL) || |
|
2984 pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_SPREADEVAL) || |
|
2985 pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY)); |
|
2986 |
|
2987 if (!report(ParseStrictError, pc->sc->strict, pn, msg)) |
|
2988 return false; |
|
2989 handler.markAsSetCall(pn); |
|
2990 return true; |
|
2991 } |
|
2992 |
|
2993 template <typename ParseHandler> |
|
2994 bool |
|
2995 Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn) |
|
2996 { |
|
2997 StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC *)nullptr); |
|
2998 |
|
2999 DefinitionList::Range defs = pc->decls().lookupMulti(name); |
|
3000 |
|
3001 DefinitionNode dn; |
|
3002 if (!defs.empty()) { |
|
3003 dn = defs.front<ParseHandler>(); |
|
3004 } else { |
|
3005 /* |
|
3006 * No definition before this use in any lexical scope. |
|
3007 * Create a placeholder definition node to either: |
|
3008 * - Be adopted when we parse the real defining |
|
3009 * declaration, or |
|
3010 * - Be left as a free variable definition if we never |
|
3011 * see the real definition. |
|
3012 */ |
|
3013 dn = getOrCreateLexicalDependency(pc, name); |
|
3014 if (!dn) |
|
3015 return false; |
|
3016 } |
|
3017 |
|
3018 handler.linkUseToDef(pn, dn); |
|
3019 |
|
3020 if (stmt && stmt->type == STMT_WITH) |
|
3021 handler.setFlag(pn, PND_DEOPTIMIZED); |
|
3022 |
|
3023 return true; |
|
3024 } |
|
3025 |
|
3026 template <> |
|
3027 bool |
|
3028 Parser<FullParseHandler>::bindDestructuringVar(BindData<FullParseHandler> *data, ParseNode *pn) |
|
3029 { |
|
3030 JS_ASSERT(pn->isKind(PNK_NAME)); |
|
3031 |
|
3032 RootedPropertyName name(context, pn->pn_atom->asPropertyName()); |
|
3033 |
|
3034 data->pn = pn; |
|
3035 if (!data->binder(data, name, this)) |
|
3036 return false; |
|
3037 |
|
3038 /* |
|
3039 * Select the appropriate name-setting opcode, respecting eager selection |
|
3040 * done by the data->binder function. |
|
3041 */ |
|
3042 if (pn->pn_dflags & PND_BOUND) |
|
3043 pn->setOp(JSOP_SETLOCAL); |
|
3044 else if (data->op == JSOP_DEFCONST) |
|
3045 pn->setOp(JSOP_SETCONST); |
|
3046 else |
|
3047 pn->setOp(JSOP_SETNAME); |
|
3048 |
|
3049 if (data->op == JSOP_DEFCONST) |
|
3050 pn->pn_dflags |= PND_CONST; |
|
3051 |
|
3052 pn->markAsAssigned(); |
|
3053 return true; |
|
3054 } |
|
3055 |
|
3056 /* |
|
3057 * Destructuring patterns can appear in two kinds of contexts: |
|
3058 * |
|
3059 * - assignment-like: assignment expressions and |for| loop heads. In |
|
3060 * these cases, the patterns' property value positions can be |
|
3061 * arbitrary lvalue expressions; the destructuring is just a fancy |
|
3062 * assignment. |
|
3063 * |
|
3064 * - declaration-like: |var| and |let| declarations, functions' formal |
|
3065 * parameter lists, |catch| clauses, and comprehension tails. In |
|
3066 * these cases, the patterns' property value positions must be |
|
3067 * simple names; the destructuring defines them as new variables. |
|
3068 * |
|
3069 * In both cases, other code parses the pattern as an arbitrary |
|
3070 * primaryExpr, and then, here in CheckDestructuring, verify that the |
|
3071 * tree is a valid destructuring expression. |
|
3072 * |
|
3073 * In assignment-like contexts, we parse the pattern with |
|
3074 * pc->inDeclDestructuring clear, so the lvalue expressions in the |
|
3075 * pattern are parsed normally. primaryExpr links variable references |
|
3076 * into the appropriate use chains; creates placeholder definitions; |
|
3077 * and so on. CheckDestructuring is called with |data| nullptr (since |
|
3078 * we won't be binding any new names), and we specialize lvalues as |
|
3079 * appropriate. |
|
3080 * |
|
3081 * In declaration-like contexts, the normal variable reference |
|
3082 * processing would just be an obstruction, because we're going to |
|
3083 * define the names that appear in the property value positions as new |
|
3084 * variables anyway. In this case, we parse the pattern with |
|
3085 * pc->inDeclDestructuring set, which directs primaryExpr to leave |
|
3086 * whatever name nodes it creates unconnected. Then, here in |
|
3087 * CheckDestructuring, we require the pattern's property value |
|
3088 * positions to be simple names, and define them as appropriate to the |
|
3089 * context. For these calls, |data| points to the right sort of |
|
3090 * BindData. |
|
3091 * |
|
3092 * The 'toplevel' is a private detail of the recursive strategy used by |
|
3093 * CheckDestructuring and callers should use the default value. |
|
3094 */ |
|
3095 template <> |
|
3096 bool |
|
3097 Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler> *data, |
|
3098 ParseNode *left, bool toplevel) |
|
3099 { |
|
3100 bool ok; |
|
3101 |
|
3102 if (left->isKind(PNK_ARRAYCOMP)) { |
|
3103 report(ParseError, false, left, JSMSG_ARRAY_COMP_LEFTSIDE); |
|
3104 return false; |
|
3105 } |
|
3106 |
|
3107 Rooted<StaticBlockObject *> blockObj(context); |
|
3108 blockObj = data && data->binder == bindLet ? data->let.blockObj.get() : nullptr; |
|
3109 |
|
3110 if (left->isKind(PNK_ARRAY)) { |
|
3111 for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) { |
|
3112 if (!pn->isKind(PNK_ELISION)) { |
|
3113 if (pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT)) { |
|
3114 ok = checkDestructuring(data, pn, false); |
|
3115 } else { |
|
3116 if (data) { |
|
3117 if (!pn->isKind(PNK_NAME)) { |
|
3118 report(ParseError, false, pn, JSMSG_NO_VARIABLE_NAME); |
|
3119 return false; |
|
3120 } |
|
3121 ok = bindDestructuringVar(data, pn); |
|
3122 } else { |
|
3123 ok = checkAndMarkAsAssignmentLhs(pn, KeyedDestructuringAssignment); |
|
3124 } |
|
3125 } |
|
3126 if (!ok) |
|
3127 return false; |
|
3128 } |
|
3129 } |
|
3130 } else { |
|
3131 JS_ASSERT(left->isKind(PNK_OBJECT)); |
|
3132 for (ParseNode *member = left->pn_head; member; member = member->pn_next) { |
|
3133 MOZ_ASSERT(member->isKind(PNK_COLON)); |
|
3134 ParseNode *expr = member->pn_right; |
|
3135 |
|
3136 if (expr->isKind(PNK_ARRAY) || expr->isKind(PNK_OBJECT)) { |
|
3137 ok = checkDestructuring(data, expr, false); |
|
3138 } else if (data) { |
|
3139 if (!expr->isKind(PNK_NAME)) { |
|
3140 report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME); |
|
3141 return false; |
|
3142 } |
|
3143 ok = bindDestructuringVar(data, expr); |
|
3144 } else { |
|
3145 /* |
|
3146 * If this is a destructuring shorthand ({x} = ...), then |
|
3147 * identifierName wasn't used to parse |x|. As a result, |x| |
|
3148 * hasn't been officially linked to its def or registered in |
|
3149 * lexdeps. Do that now. |
|
3150 */ |
|
3151 if (member->pn_right == member->pn_left) { |
|
3152 RootedPropertyName name(context, expr->pn_atom->asPropertyName()); |
|
3153 if (!noteNameUse(name, expr)) |
|
3154 return false; |
|
3155 } |
|
3156 ok = checkAndMarkAsAssignmentLhs(expr, KeyedDestructuringAssignment); |
|
3157 } |
|
3158 if (!ok) |
|
3159 return false; |
|
3160 } |
|
3161 } |
|
3162 |
|
3163 return true; |
|
3164 } |
|
3165 |
|
3166 template <> |
|
3167 bool |
|
3168 Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data, |
|
3169 Node left, bool toplevel) |
|
3170 { |
|
3171 return abortIfSyntaxParser(); |
|
3172 } |
|
3173 |
|
3174 template <typename ParseHandler> |
|
3175 typename ParseHandler::Node |
|
3176 Parser<ParseHandler>::destructuringExpr(BindData<ParseHandler> *data, TokenKind tt) |
|
3177 { |
|
3178 JS_ASSERT(tokenStream.isCurrentTokenType(tt)); |
|
3179 |
|
3180 pc->inDeclDestructuring = true; |
|
3181 Node pn = primaryExpr(tt); |
|
3182 pc->inDeclDestructuring = false; |
|
3183 if (!pn) |
|
3184 return null(); |
|
3185 if (!checkDestructuring(data, pn)) |
|
3186 return null(); |
|
3187 return pn; |
|
3188 } |
|
3189 |
|
3190 template <typename ParseHandler> |
|
3191 typename ParseHandler::Node |
|
3192 Parser<ParseHandler>::pushLexicalScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt) |
|
3193 { |
|
3194 JS_ASSERT(blockObj); |
|
3195 |
|
3196 ObjectBox *blockbox = newObjectBox(blockObj); |
|
3197 if (!blockbox) |
|
3198 return null(); |
|
3199 |
|
3200 PushStatementPC(pc, stmt, STMT_BLOCK); |
|
3201 blockObj->initEnclosingNestedScopeFromParser(pc->staticScope); |
|
3202 FinishPushNestedScope(pc, stmt, *blockObj.get()); |
|
3203 stmt->isBlockScope = true; |
|
3204 |
|
3205 Node pn = handler.newLexicalScope(blockbox); |
|
3206 if (!pn) |
|
3207 return null(); |
|
3208 |
|
3209 if (!GenerateBlockId(tokenStream, pc, stmt->blockid)) |
|
3210 return null(); |
|
3211 handler.setBlockId(pn, stmt->blockid); |
|
3212 return pn; |
|
3213 } |
|
3214 |
|
3215 template <typename ParseHandler> |
|
3216 typename ParseHandler::Node |
|
3217 Parser<ParseHandler>::pushLexicalScope(StmtInfoPC *stmt) |
|
3218 { |
|
3219 RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context)); |
|
3220 if (!blockObj) |
|
3221 return null(); |
|
3222 |
|
3223 return pushLexicalScope(blockObj, stmt); |
|
3224 } |
|
3225 |
|
3226 struct AddLetDecl |
|
3227 { |
|
3228 uint32_t blockid; |
|
3229 |
|
3230 AddLetDecl(uint32_t blockid) : blockid(blockid) {} |
|
3231 |
|
3232 bool operator()(TokenStream &ts, ParseContext<FullParseHandler> *pc, |
|
3233 HandleStaticBlockObject blockObj, const Shape &shape, JSAtom *) |
|
3234 { |
|
3235 ParseNode *def = (ParseNode *) blockObj->getSlot(shape.slot()).toPrivate(); |
|
3236 def->pn_blockid = blockid; |
|
3237 RootedPropertyName name(ts.context(), def->name()); |
|
3238 return pc->define(ts, name, def, Definition::LET); |
|
3239 } |
|
3240 }; |
|
3241 |
|
3242 template <> |
|
3243 ParseNode * |
|
3244 Parser<FullParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt) |
|
3245 { |
|
3246 JS_ASSERT(blockObj); |
|
3247 ParseNode *pn = pushLexicalScope(blockObj, stmt); |
|
3248 if (!pn) |
|
3249 return null(); |
|
3250 |
|
3251 pn->pn_dflags |= PND_LET; |
|
3252 |
|
3253 /* Populate the new scope with decls found in the head with updated blockid. */ |
|
3254 if (!ForEachLetDef(tokenStream, pc, blockObj, AddLetDecl(stmt->blockid))) |
|
3255 return null(); |
|
3256 |
|
3257 return pn; |
|
3258 } |
|
3259 |
|
3260 template <> |
|
3261 SyntaxParseHandler::Node |
|
3262 Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt) |
|
3263 { |
|
3264 JS_ALWAYS_FALSE(abortIfSyntaxParser()); |
|
3265 return SyntaxParseHandler::NodeFailure; |
|
3266 } |
|
3267 |
|
3268 /* |
|
3269 * Parse a let block statement or let expression (determined by 'letContext'). |
|
3270 * In both cases, bindings are not hoisted to the top of the enclosing block |
|
3271 * and thus must be carefully injected between variables() and the let body. |
|
3272 */ |
|
3273 template <typename ParseHandler> |
|
3274 typename ParseHandler::Node |
|
3275 Parser<ParseHandler>::letBlock(LetContext letContext) |
|
3276 { |
|
3277 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LET)); |
|
3278 |
|
3279 RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context)); |
|
3280 if (!blockObj) |
|
3281 return null(); |
|
3282 |
|
3283 uint32_t begin = pos().begin; |
|
3284 |
|
3285 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET); |
|
3286 |
|
3287 Node vars = variables(PNK_LET, nullptr, blockObj, DontHoistVars); |
|
3288 if (!vars) |
|
3289 return null(); |
|
3290 |
|
3291 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET); |
|
3292 |
|
3293 StmtInfoPC stmtInfo(context); |
|
3294 Node block = pushLetScope(blockObj, &stmtInfo); |
|
3295 if (!block) |
|
3296 return null(); |
|
3297 |
|
3298 Node pnlet = handler.newBinary(PNK_LET, vars, block); |
|
3299 if (!pnlet) |
|
3300 return null(); |
|
3301 handler.setBeginPosition(pnlet, begin); |
|
3302 |
|
3303 bool needExprStmt = false; |
|
3304 if (letContext == LetStatement && !tokenStream.matchToken(TOK_LC, TokenStream::Operand)) { |
|
3305 /* |
|
3306 * Strict mode eliminates a grammar ambiguity with unparenthesized |
|
3307 * LetExpressions in an ExpressionStatement. If followed immediately |
|
3308 * by an arguments list, it's ambiguous whether the let expression |
|
3309 * is the callee or the call is inside the let expression body. |
|
3310 * |
|
3311 * See bug 569464. |
|
3312 */ |
|
3313 if (!report(ParseStrictError, pc->sc->strict, pnlet, |
|
3314 JSMSG_STRICT_CODE_LET_EXPR_STMT)) |
|
3315 { |
|
3316 return null(); |
|
3317 } |
|
3318 |
|
3319 /* |
|
3320 * If this is really an expression in let statement guise, then we |
|
3321 * need to wrap the PNK_LET node in a PNK_SEMI node so that we pop |
|
3322 * the return value of the expression. |
|
3323 */ |
|
3324 needExprStmt = true; |
|
3325 letContext = LetExpresion; |
|
3326 } |
|
3327 |
|
3328 Node expr; |
|
3329 if (letContext == LetStatement) { |
|
3330 expr = statements(); |
|
3331 if (!expr) |
|
3332 return null(); |
|
3333 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET); |
|
3334 } else { |
|
3335 JS_ASSERT(letContext == LetExpresion); |
|
3336 expr = assignExpr(); |
|
3337 if (!expr) |
|
3338 return null(); |
|
3339 } |
|
3340 handler.setLexicalScopeBody(block, expr); |
|
3341 PopStatementPC(tokenStream, pc); |
|
3342 |
|
3343 handler.setEndPosition(pnlet, pos().end); |
|
3344 |
|
3345 if (needExprStmt) { |
|
3346 if (!MatchOrInsertSemicolon(tokenStream)) |
|
3347 return null(); |
|
3348 return handler.newExprStatement(pnlet, pos().end); |
|
3349 } |
|
3350 return pnlet; |
|
3351 } |
|
3352 |
|
3353 template <typename ParseHandler> |
|
3354 static bool |
|
3355 PushBlocklikeStatement(TokenStream &ts, StmtInfoPC *stmt, StmtType type, |
|
3356 ParseContext<ParseHandler> *pc) |
|
3357 { |
|
3358 PushStatementPC(pc, stmt, type); |
|
3359 return GenerateBlockId(ts, pc, stmt->blockid); |
|
3360 } |
|
3361 |
|
3362 template <typename ParseHandler> |
|
3363 typename ParseHandler::Node |
|
3364 Parser<ParseHandler>::blockStatement() |
|
3365 { |
|
3366 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); |
|
3367 |
|
3368 StmtInfoPC stmtInfo(context); |
|
3369 if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_BLOCK, pc)) |
|
3370 return null(); |
|
3371 |
|
3372 Node list = statements(); |
|
3373 if (!list) |
|
3374 return null(); |
|
3375 |
|
3376 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND); |
|
3377 PopStatementPC(tokenStream, pc); |
|
3378 return list; |
|
3379 } |
|
3380 |
|
3381 template <typename ParseHandler> |
|
3382 typename ParseHandler::Node |
|
3383 Parser<ParseHandler>::newBindingNode(PropertyName *name, bool functionScope, VarContext varContext) |
|
3384 { |
|
3385 /* |
|
3386 * If this name is being injected into an existing block/function, see if |
|
3387 * it has already been declared or if it resolves an outstanding lexdep. |
|
3388 * Otherwise, this is a let block/expr that introduces a new scope and thus |
|
3389 * shadows existing decls and doesn't resolve existing lexdeps. Duplicate |
|
3390 * names are caught by bindLet. |
|
3391 */ |
|
3392 if (varContext == HoistVars) { |
|
3393 if (AtomDefnPtr p = pc->lexdeps->lookup(name)) { |
|
3394 DefinitionNode lexdep = p.value().get<ParseHandler>(); |
|
3395 JS_ASSERT(handler.getDefinitionKind(lexdep) == Definition::PLACEHOLDER); |
|
3396 |
|
3397 Node pn = handler.getDefinitionNode(lexdep); |
|
3398 if (handler.dependencyCovered(pn, pc->blockid(), functionScope)) { |
|
3399 handler.setBlockId(pn, pc->blockid()); |
|
3400 pc->lexdeps->remove(p); |
|
3401 handler.setPosition(pn, pos()); |
|
3402 return pn; |
|
3403 } |
|
3404 } |
|
3405 } |
|
3406 |
|
3407 /* Make a new node for this declarator name (or destructuring pattern). */ |
|
3408 return newName(name); |
|
3409 } |
|
3410 |
|
3411 /* |
|
3412 * The 'blockObj' parameter is non-null when parsing the 'vars' in a let |
|
3413 * expression, block statement, non-top-level let declaration in statement |
|
3414 * context, and the let-initializer of a for-statement. |
|
3415 */ |
|
3416 template <typename ParseHandler> |
|
3417 typename ParseHandler::Node |
|
3418 Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple, |
|
3419 StaticBlockObject *blockObj, VarContext varContext) |
|
3420 { |
|
3421 /* |
|
3422 * The four options here are: |
|
3423 * - PNK_VAR: We're parsing var declarations. |
|
3424 * - PNK_CONST: We're parsing const declarations. |
|
3425 * - PNK_LET: We are parsing a let declaration. |
|
3426 * - PNK_CALL: We are parsing the head of a let block. |
|
3427 */ |
|
3428 JS_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_CALL); |
|
3429 |
|
3430 /* |
|
3431 * The simple flag is set if the declaration has the form 'var x', with |
|
3432 * only one variable declared and no initializer expression. |
|
3433 */ |
|
3434 JS_ASSERT_IF(psimple, *psimple); |
|
3435 |
|
3436 JSOp op = blockObj ? JSOP_NOP : kind == PNK_VAR ? JSOP_DEFVAR : JSOP_DEFCONST; |
|
3437 |
|
3438 Node pn = handler.newList(kind, null(), op); |
|
3439 if (!pn) |
|
3440 return null(); |
|
3441 |
|
3442 /* |
|
3443 * SpiderMonkey const is really "write once per initialization evaluation" |
|
3444 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so |
|
3445 * this code will change soon. |
|
3446 */ |
|
3447 BindData<ParseHandler> data(context); |
|
3448 if (blockObj) |
|
3449 data.initLet(varContext, *blockObj, JSMSG_TOO_MANY_LOCALS); |
|
3450 else |
|
3451 data.initVarOrConst(op); |
|
3452 |
|
3453 bool first = true; |
|
3454 Node pn2; |
|
3455 do { |
|
3456 if (psimple && !first) |
|
3457 *psimple = false; |
|
3458 first = false; |
|
3459 |
|
3460 TokenKind tt = tokenStream.getToken(); |
|
3461 if (tt == TOK_LB || tt == TOK_LC) { |
|
3462 if (psimple) |
|
3463 *psimple = false; |
|
3464 |
|
3465 pc->inDeclDestructuring = true; |
|
3466 pn2 = primaryExpr(tt); |
|
3467 pc->inDeclDestructuring = false; |
|
3468 if (!pn2) |
|
3469 return null(); |
|
3470 |
|
3471 if (!checkDestructuring(&data, pn2)) |
|
3472 return null(); |
|
3473 bool ignored; |
|
3474 if (pc->parsingForInit && matchInOrOf(&ignored)) { |
|
3475 tokenStream.ungetToken(); |
|
3476 handler.addList(pn, pn2); |
|
3477 continue; |
|
3478 } |
|
3479 |
|
3480 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL); |
|
3481 |
|
3482 Node init = assignExpr(); |
|
3483 if (!init) |
|
3484 return null(); |
|
3485 |
|
3486 pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init, pc); |
|
3487 if (!pn2) |
|
3488 return null(); |
|
3489 handler.addList(pn, pn2); |
|
3490 continue; |
|
3491 } |
|
3492 |
|
3493 if (tt != TOK_NAME) { |
|
3494 if (tt == TOK_YIELD) { |
|
3495 if (!checkYieldNameValidity()) |
|
3496 return null(); |
|
3497 } else { |
|
3498 if (tt != TOK_ERROR) |
|
3499 report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME); |
|
3500 return null(); |
|
3501 } |
|
3502 } |
|
3503 |
|
3504 RootedPropertyName name(context, tokenStream.currentName()); |
|
3505 pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext); |
|
3506 if (!pn2) |
|
3507 return null(); |
|
3508 if (data.op == JSOP_DEFCONST) |
|
3509 handler.setFlag(pn2, PND_CONST); |
|
3510 data.pn = pn2; |
|
3511 if (!data.binder(&data, name, this)) |
|
3512 return null(); |
|
3513 handler.addList(pn, pn2); |
|
3514 |
|
3515 if (tokenStream.matchToken(TOK_ASSIGN)) { |
|
3516 if (psimple) |
|
3517 *psimple = false; |
|
3518 |
|
3519 Node init = assignExpr(); |
|
3520 if (!init) |
|
3521 return null(); |
|
3522 |
|
3523 if (!handler.finishInitializerAssignment(pn2, init, data.op)) |
|
3524 return null(); |
|
3525 } |
|
3526 } while (tokenStream.matchToken(TOK_COMMA)); |
|
3527 |
|
3528 return pn; |
|
3529 } |
|
3530 |
|
3531 template <> |
|
3532 ParseNode * |
|
3533 Parser<FullParseHandler>::letDeclaration() |
|
3534 { |
|
3535 handler.disableSyntaxParser(); |
|
3536 |
|
3537 ParseNode *pn; |
|
3538 |
|
3539 do { |
|
3540 /* |
|
3541 * This is a let declaration. We must be directly under a block per the |
|
3542 * proposed ES4 specs, but not an implicit block created due to |
|
3543 * 'for (let ...)'. If we pass this error test, make the enclosing |
|
3544 * StmtInfoPC be our scope. Further let declarations in this block will |
|
3545 * find this scope statement and use the same block object. |
|
3546 * |
|
3547 * If we are the first let declaration in this block (i.e., when the |
|
3548 * enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then |
|
3549 * we also need to set pc->blockNode to be our PNK_LEXICALSCOPE. |
|
3550 */ |
|
3551 StmtInfoPC *stmt = pc->topStmt; |
|
3552 if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) { |
|
3553 report(ParseError, false, null(), JSMSG_LET_DECL_NOT_IN_BLOCK); |
|
3554 return null(); |
|
3555 } |
|
3556 |
|
3557 if (stmt && stmt->isBlockScope) { |
|
3558 JS_ASSERT(pc->staticScope == stmt->staticScope); |
|
3559 } else { |
|
3560 if (pc->atBodyLevel()) { |
|
3561 /* |
|
3562 * ES4 specifies that let at top level and at body-block scope |
|
3563 * does not shadow var, so convert back to var. |
|
3564 */ |
|
3565 pn = variables(PNK_VAR); |
|
3566 if (!pn) |
|
3567 return null(); |
|
3568 pn->pn_xflags |= PNX_POPVAR; |
|
3569 break; |
|
3570 } |
|
3571 |
|
3572 /* |
|
3573 * Some obvious assertions here, but they may help clarify the |
|
3574 * situation. This stmt is not yet a scope, so it must not be a |
|
3575 * catch block (catch is a lexical scope by definition). |
|
3576 */ |
|
3577 JS_ASSERT(!stmt->isBlockScope); |
|
3578 JS_ASSERT(stmt != pc->topScopeStmt); |
|
3579 JS_ASSERT(stmt->type == STMT_BLOCK || |
|
3580 stmt->type == STMT_SWITCH || |
|
3581 stmt->type == STMT_TRY || |
|
3582 stmt->type == STMT_FINALLY); |
|
3583 JS_ASSERT(!stmt->downScope); |
|
3584 |
|
3585 /* Convert the block statement into a scope statement. */ |
|
3586 StaticBlockObject *blockObj = StaticBlockObject::create(context); |
|
3587 if (!blockObj) |
|
3588 return null(); |
|
3589 |
|
3590 ObjectBox *blockbox = newObjectBox(blockObj); |
|
3591 if (!blockbox) |
|
3592 return null(); |
|
3593 |
|
3594 /* |
|
3595 * Insert stmt on the pc->topScopeStmt/stmtInfo.downScope linked |
|
3596 * list stack, if it isn't already there. If it is there, but it |
|
3597 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally |
|
3598 * block. |
|
3599 */ |
|
3600 stmt->isBlockScope = stmt->isNestedScope = true; |
|
3601 stmt->downScope = pc->topScopeStmt; |
|
3602 pc->topScopeStmt = stmt; |
|
3603 |
|
3604 blockObj->initEnclosingNestedScopeFromParser(pc->staticScope); |
|
3605 pc->staticScope = blockObj; |
|
3606 stmt->staticScope = blockObj; |
|
3607 |
|
3608 #ifdef DEBUG |
|
3609 ParseNode *tmp = pc->blockNode; |
|
3610 JS_ASSERT(!tmp || !tmp->isKind(PNK_LEXICALSCOPE)); |
|
3611 #endif |
|
3612 |
|
3613 /* Create a new lexical scope node for these statements. */ |
|
3614 ParseNode *pn1 = LexicalScopeNode::create(PNK_LEXICALSCOPE, &handler); |
|
3615 if (!pn1) |
|
3616 return null(); |
|
3617 |
|
3618 pn1->pn_pos = pc->blockNode->pn_pos; |
|
3619 pn1->pn_objbox = blockbox; |
|
3620 pn1->pn_expr = pc->blockNode; |
|
3621 pn1->pn_blockid = pc->blockNode->pn_blockid; |
|
3622 pc->blockNode = pn1; |
|
3623 } |
|
3624 |
|
3625 pn = variables(PNK_LET, nullptr, &pc->staticScope->as<StaticBlockObject>(), HoistVars); |
|
3626 if (!pn) |
|
3627 return null(); |
|
3628 pn->pn_xflags = PNX_POPVAR; |
|
3629 } while (0); |
|
3630 |
|
3631 return MatchOrInsertSemicolon(tokenStream) ? pn : nullptr; |
|
3632 } |
|
3633 |
|
3634 template <> |
|
3635 SyntaxParseHandler::Node |
|
3636 Parser<SyntaxParseHandler>::letDeclaration() |
|
3637 { |
|
3638 JS_ALWAYS_FALSE(abortIfSyntaxParser()); |
|
3639 return SyntaxParseHandler::NodeFailure; |
|
3640 } |
|
3641 |
|
3642 template <> |
|
3643 ParseNode * |
|
3644 Parser<FullParseHandler>::letStatement() |
|
3645 { |
|
3646 handler.disableSyntaxParser(); |
|
3647 |
|
3648 /* Check for a let statement or let expression. */ |
|
3649 ParseNode *pn; |
|
3650 if (tokenStream.peekToken() == TOK_LP) { |
|
3651 pn = letBlock(LetStatement); |
|
3652 JS_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI)); |
|
3653 } else { |
|
3654 pn = letDeclaration(); |
|
3655 } |
|
3656 return pn; |
|
3657 } |
|
3658 |
|
3659 template <> |
|
3660 SyntaxParseHandler::Node |
|
3661 Parser<SyntaxParseHandler>::letStatement() |
|
3662 { |
|
3663 JS_ALWAYS_FALSE(abortIfSyntaxParser()); |
|
3664 return SyntaxParseHandler::NodeFailure; |
|
3665 } |
|
3666 |
|
3667 template<typename ParseHandler> |
|
3668 typename ParseHandler::Node |
|
3669 Parser<ParseHandler>::importDeclaration() |
|
3670 { |
|
3671 JS_ASSERT(tokenStream.currentToken().type == TOK_IMPORT); |
|
3672 |
|
3673 if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) { |
|
3674 report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL); |
|
3675 return null(); |
|
3676 } |
|
3677 |
|
3678 uint32_t begin = pos().begin; |
|
3679 TokenKind tt = tokenStream.getToken(); |
|
3680 |
|
3681 Node importSpecSet = handler.newList(PNK_IMPORT_SPEC_LIST); |
|
3682 if (!importSpecSet) |
|
3683 return null(); |
|
3684 |
|
3685 if (tt == TOK_NAME || tt == TOK_LC) { |
|
3686 if (tt == TOK_NAME) { |
|
3687 // Handle the form |import a from 'b'|, by adding a single import |
|
3688 // specifier to the list, with 'default' as the import name and |
|
3689 // 'a' as the binding name. This is equivalent to |
|
3690 // |import { default as a } from 'b'|. |
|
3691 Node importName = newName(context->names().default_); |
|
3692 if (!importName) |
|
3693 return null(); |
|
3694 |
|
3695 Node bindingName = newName(tokenStream.currentName()); |
|
3696 if (!bindingName) |
|
3697 return null(); |
|
3698 |
|
3699 Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName); |
|
3700 if (!importSpec) |
|
3701 return null(); |
|
3702 |
|
3703 handler.addList(importSpecSet, importSpec); |
|
3704 } else { |
|
3705 do { |
|
3706 // Handle the forms |import {} from 'a'| and |
|
3707 // |import { ..., } from 'a'| (where ... is non empty), by |
|
3708 // escaping the loop early if the next token is }. |
|
3709 tt = tokenStream.peekToken(TokenStream::KeywordIsName); |
|
3710 if (tt == TOK_ERROR) |
|
3711 return null(); |
|
3712 if (tt == TOK_RC) |
|
3713 break; |
|
3714 |
|
3715 // If the next token is a keyword, the previous call to |
|
3716 // peekToken matched it as a TOK_NAME, and put it in the |
|
3717 // lookahead buffer, so this call will match keywords as well. |
|
3718 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME); |
|
3719 Node importName = newName(tokenStream.currentName()); |
|
3720 if (!importName) |
|
3721 return null(); |
|
3722 |
|
3723 if (tokenStream.getToken() == TOK_NAME && |
|
3724 tokenStream.currentName() == context->names().as) |
|
3725 { |
|
3726 if (tokenStream.getToken() != TOK_NAME) { |
|
3727 report(ParseError, false, null(), JSMSG_NO_BINDING_NAME); |
|
3728 return null(); |
|
3729 } |
|
3730 } else { |
|
3731 // Keywords cannot be bound to themselves, so an import name |
|
3732 // that is a keyword is a syntax error if it is not followed |
|
3733 // by the keyword 'as'. |
|
3734 if (IsKeyword(importName->name())) { |
|
3735 JSAutoByteString bytes; |
|
3736 if (!AtomToPrintableString(context, importName->name(), &bytes)) |
|
3737 return null(); |
|
3738 report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr()); |
|
3739 return null(); |
|
3740 } |
|
3741 tokenStream.ungetToken(); |
|
3742 } |
|
3743 Node bindingName = newName(tokenStream.currentName()); |
|
3744 if (!bindingName) |
|
3745 return null(); |
|
3746 |
|
3747 Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName); |
|
3748 if (!importSpec) |
|
3749 return null(); |
|
3750 |
|
3751 handler.addList(importSpecSet, importSpec); |
|
3752 } while (tokenStream.matchToken(TOK_COMMA)); |
|
3753 |
|
3754 MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_IMPORT_SPEC_LIST); |
|
3755 } |
|
3756 |
|
3757 if (tokenStream.getToken() != TOK_NAME || |
|
3758 tokenStream.currentName() != context->names().from) |
|
3759 { |
|
3760 report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_SPEC_SET); |
|
3761 return null(); |
|
3762 } |
|
3763 |
|
3764 MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); |
|
3765 } else { |
|
3766 if (tt != TOK_STRING) { |
|
3767 report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT); |
|
3768 return null(); |
|
3769 } |
|
3770 |
|
3771 // Handle the form |import 'a'| by leaving the list empty. This is |
|
3772 // equivalent to |import {} from 'a'|. |
|
3773 importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin; |
|
3774 } |
|
3775 |
|
3776 Node moduleSpec = stringLiteral(); |
|
3777 if (!moduleSpec) |
|
3778 return null(); |
|
3779 |
|
3780 if (!MatchOrInsertSemicolon(tokenStream)) |
|
3781 return null(); |
|
3782 |
|
3783 return handler.newImportDeclaration(importSpecSet, moduleSpec, |
|
3784 TokenPos(begin, pos().end)); |
|
3785 } |
|
3786 |
|
3787 template<> |
|
3788 SyntaxParseHandler::Node |
|
3789 Parser<SyntaxParseHandler>::importDeclaration() |
|
3790 { |
|
3791 JS_ALWAYS_FALSE(abortIfSyntaxParser()); |
|
3792 return SyntaxParseHandler::NodeFailure; |
|
3793 } |
|
3794 |
|
3795 template<typename ParseHandler> |
|
3796 typename ParseHandler::Node |
|
3797 Parser<ParseHandler>::exportDeclaration() |
|
3798 { |
|
3799 JS_ASSERT(tokenStream.currentToken().type == TOK_EXPORT); |
|
3800 |
|
3801 if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) { |
|
3802 report(ParseError, false, null(), JSMSG_EXPORT_DECL_AT_TOP_LEVEL); |
|
3803 return null(); |
|
3804 } |
|
3805 |
|
3806 uint32_t begin = pos().begin; |
|
3807 |
|
3808 Node kid; |
|
3809 switch (TokenKind tt = tokenStream.getToken()) { |
|
3810 case TOK_LC: |
|
3811 case TOK_MUL: |
|
3812 kid = handler.newList(PNK_EXPORT_SPEC_LIST); |
|
3813 if (!kid) |
|
3814 return null(); |
|
3815 |
|
3816 if (tt == TOK_LC) { |
|
3817 do { |
|
3818 // Handle the forms |export {}| and |export { ..., }| (where ... |
|
3819 // is non empty), by escaping the loop early if the next token |
|
3820 // is }. |
|
3821 tt = tokenStream.peekToken(); |
|
3822 if (tt == TOK_ERROR) |
|
3823 return null(); |
|
3824 if (tt == TOK_RC) |
|
3825 break; |
|
3826 |
|
3827 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME); |
|
3828 Node bindingName = newName(tokenStream.currentName()); |
|
3829 if (!bindingName) |
|
3830 return null(); |
|
3831 |
|
3832 if (tokenStream.getToken() == TOK_NAME && |
|
3833 tokenStream.currentName() == context->names().as) |
|
3834 { |
|
3835 if (tokenStream.getToken(TokenStream::KeywordIsName) != TOK_NAME) { |
|
3836 report(ParseError, false, null(), JSMSG_NO_EXPORT_NAME); |
|
3837 return null(); |
|
3838 } |
|
3839 } else { |
|
3840 tokenStream.ungetToken(); |
|
3841 } |
|
3842 Node exportName = newName(tokenStream.currentName()); |
|
3843 if (!exportName) |
|
3844 return null(); |
|
3845 |
|
3846 Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName); |
|
3847 if (!exportSpec) |
|
3848 return null(); |
|
3849 |
|
3850 handler.addList(kid, exportSpec); |
|
3851 } while (tokenStream.matchToken(TOK_COMMA)); |
|
3852 |
|
3853 MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_EXPORT_SPEC_LIST); |
|
3854 } else { |
|
3855 // Handle the form |export *| by adding a special export batch |
|
3856 // specifier to the list. |
|
3857 Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos()); |
|
3858 if (!kid) |
|
3859 return null(); |
|
3860 |
|
3861 handler.addList(kid, exportSpec); |
|
3862 } |
|
3863 if (tokenStream.getToken() == TOK_NAME && |
|
3864 tokenStream.currentName() == context->names().from) |
|
3865 { |
|
3866 MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); |
|
3867 |
|
3868 Node moduleSpec = stringLiteral(); |
|
3869 if (!moduleSpec) |
|
3870 return null(); |
|
3871 |
|
3872 if (!MatchOrInsertSemicolon(tokenStream)) |
|
3873 return null(); |
|
3874 |
|
3875 return handler.newExportFromDeclaration(begin, kid, moduleSpec); |
|
3876 } else { |
|
3877 tokenStream.ungetToken(); |
|
3878 } |
|
3879 |
|
3880 kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr; |
|
3881 if (!kid) |
|
3882 return null(); |
|
3883 break; |
|
3884 |
|
3885 case TOK_FUNCTION: |
|
3886 kid = functionStmt(); |
|
3887 if (!kid) |
|
3888 return null(); |
|
3889 break; |
|
3890 |
|
3891 case TOK_VAR: |
|
3892 case TOK_CONST: |
|
3893 kid = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST); |
|
3894 if (!kid) |
|
3895 return null(); |
|
3896 kid->pn_xflags = PNX_POPVAR; |
|
3897 |
|
3898 kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr; |
|
3899 if (!kid) |
|
3900 return null(); |
|
3901 break; |
|
3902 |
|
3903 case TOK_NAME: |
|
3904 // Handle the form |export a} in the same way as |export let a|, by |
|
3905 // acting as if we've just seen the let keyword. Simply unget the token |
|
3906 // and fall through. |
|
3907 tokenStream.ungetToken(); |
|
3908 case TOK_LET: |
|
3909 kid = letDeclaration(); |
|
3910 if (!kid) |
|
3911 return null(); |
|
3912 break; |
|
3913 |
|
3914 default: |
|
3915 report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT); |
|
3916 return null(); |
|
3917 } |
|
3918 |
|
3919 return handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); |
|
3920 } |
|
3921 |
|
3922 template<> |
|
3923 SyntaxParseHandler::Node |
|
3924 Parser<SyntaxParseHandler>::exportDeclaration() |
|
3925 { |
|
3926 JS_ALWAYS_FALSE(abortIfSyntaxParser()); |
|
3927 return SyntaxParseHandler::NodeFailure; |
|
3928 } |
|
3929 |
|
3930 |
|
3931 template <typename ParseHandler> |
|
3932 typename ParseHandler::Node |
|
3933 Parser<ParseHandler>::expressionStatement() |
|
3934 { |
|
3935 tokenStream.ungetToken(); |
|
3936 Node pnexpr = expr(); |
|
3937 if (!pnexpr) |
|
3938 return null(); |
|
3939 if (!MatchOrInsertSemicolon(tokenStream)) |
|
3940 return null(); |
|
3941 return handler.newExprStatement(pnexpr, pos().end); |
|
3942 } |
|
3943 |
|
3944 template <typename ParseHandler> |
|
3945 typename ParseHandler::Node |
|
3946 Parser<ParseHandler>::ifStatement() |
|
3947 { |
|
3948 uint32_t begin = pos().begin; |
|
3949 |
|
3950 /* An IF node has three kids: condition, then, and optional else. */ |
|
3951 Node cond = condition(); |
|
3952 if (!cond) |
|
3953 return null(); |
|
3954 |
|
3955 if (tokenStream.peekToken(TokenStream::Operand) == TOK_SEMI && |
|
3956 !report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT)) |
|
3957 { |
|
3958 return null(); |
|
3959 } |
|
3960 |
|
3961 StmtInfoPC stmtInfo(context); |
|
3962 PushStatementPC(pc, &stmtInfo, STMT_IF); |
|
3963 Node thenBranch = statement(); |
|
3964 if (!thenBranch) |
|
3965 return null(); |
|
3966 |
|
3967 Node elseBranch; |
|
3968 if (tokenStream.matchToken(TOK_ELSE, TokenStream::Operand)) { |
|
3969 stmtInfo.type = STMT_ELSE; |
|
3970 elseBranch = statement(); |
|
3971 if (!elseBranch) |
|
3972 return null(); |
|
3973 } else { |
|
3974 elseBranch = null(); |
|
3975 } |
|
3976 |
|
3977 PopStatementPC(tokenStream, pc); |
|
3978 return handler.newIfStatement(begin, cond, thenBranch, elseBranch); |
|
3979 } |
|
3980 |
|
3981 template <typename ParseHandler> |
|
3982 typename ParseHandler::Node |
|
3983 Parser<ParseHandler>::doWhileStatement() |
|
3984 { |
|
3985 uint32_t begin = pos().begin; |
|
3986 StmtInfoPC stmtInfo(context); |
|
3987 PushStatementPC(pc, &stmtInfo, STMT_DO_LOOP); |
|
3988 Node body = statement(); |
|
3989 if (!body) |
|
3990 return null(); |
|
3991 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO); |
|
3992 Node cond = condition(); |
|
3993 if (!cond) |
|
3994 return null(); |
|
3995 PopStatementPC(tokenStream, pc); |
|
3996 |
|
3997 if (versionNumber() == JSVERSION_ECMA_3) { |
|
3998 // Pedantically require a semicolon or line break, following ES3. |
|
3999 // Bug 880329 proposes removing this case. |
|
4000 if (!MatchOrInsertSemicolon(tokenStream)) |
|
4001 return null(); |
|
4002 } else { |
|
4003 // The semicolon after do-while is even more optional than most |
|
4004 // semicolons in JS. Web compat required this by 2004: |
|
4005 // http://bugzilla.mozilla.org/show_bug.cgi?id=238945 |
|
4006 // ES3 and ES5 disagreed, but ES6 conforms to Web reality: |
|
4007 // https://bugs.ecmascript.org/show_bug.cgi?id=157 |
|
4008 (void) tokenStream.matchToken(TOK_SEMI); |
|
4009 } |
|
4010 |
|
4011 return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end)); |
|
4012 } |
|
4013 |
|
4014 template <typename ParseHandler> |
|
4015 typename ParseHandler::Node |
|
4016 Parser<ParseHandler>::whileStatement() |
|
4017 { |
|
4018 uint32_t begin = pos().begin; |
|
4019 StmtInfoPC stmtInfo(context); |
|
4020 PushStatementPC(pc, &stmtInfo, STMT_WHILE_LOOP); |
|
4021 Node cond = condition(); |
|
4022 if (!cond) |
|
4023 return null(); |
|
4024 Node body = statement(); |
|
4025 if (!body) |
|
4026 return null(); |
|
4027 PopStatementPC(tokenStream, pc); |
|
4028 return handler.newWhileStatement(begin, cond, body); |
|
4029 } |
|
4030 |
|
4031 template <typename ParseHandler> |
|
4032 bool |
|
4033 Parser<ParseHandler>::matchInOrOf(bool *isForOfp) |
|
4034 { |
|
4035 if (tokenStream.matchToken(TOK_IN)) { |
|
4036 *isForOfp = false; |
|
4037 return true; |
|
4038 } |
|
4039 if (tokenStream.matchContextualKeyword(context->names().of)) { |
|
4040 *isForOfp = true; |
|
4041 return true; |
|
4042 } |
|
4043 return false; |
|
4044 } |
|
4045 |
|
4046 template <> |
|
4047 bool |
|
4048 Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion version, |
|
4049 bool isForDecl, bool isForEach, |
|
4050 ParseNodeKind headKind) |
|
4051 { |
|
4052 if (isForDecl) { |
|
4053 if (pn1->pn_count > 1) |
|
4054 return false; |
|
4055 if (pn1->isOp(JSOP_DEFCONST)) |
|
4056 return false; |
|
4057 |
|
4058 // In JS 1.7 only, for (var [K, V] in EXPR) has a special meaning. |
|
4059 // Hence all other destructuring decls are banned there. |
|
4060 if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN) { |
|
4061 ParseNode *lhs = pn1->pn_head; |
|
4062 if (lhs->isKind(PNK_ASSIGN)) |
|
4063 lhs = lhs->pn_left; |
|
4064 |
|
4065 if (lhs->isKind(PNK_OBJECT)) |
|
4066 return false; |
|
4067 if (lhs->isKind(PNK_ARRAY) && lhs->pn_count != 2) |
|
4068 return false; |
|
4069 } |
|
4070 return true; |
|
4071 } |
|
4072 |
|
4073 switch (pn1->getKind()) { |
|
4074 case PNK_NAME: |
|
4075 case PNK_DOT: |
|
4076 case PNK_CALL: |
|
4077 case PNK_ELEM: |
|
4078 return true; |
|
4079 |
|
4080 case PNK_ARRAY: |
|
4081 case PNK_OBJECT: |
|
4082 // In JS 1.7 only, for ([K, V] in EXPR) has a special meaning. |
|
4083 // Hence all other destructuring left-hand sides are banned there. |
|
4084 if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN) |
|
4085 return pn1->isKind(PNK_ARRAY) && pn1->pn_count == 2; |
|
4086 return true; |
|
4087 |
|
4088 default: |
|
4089 return false; |
|
4090 } |
|
4091 } |
|
4092 |
|
4093 template <> |
|
4094 ParseNode * |
|
4095 Parser<FullParseHandler>::forStatement() |
|
4096 { |
|
4097 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); |
|
4098 uint32_t begin = pos().begin; |
|
4099 |
|
4100 StmtInfoPC forStmt(context); |
|
4101 PushStatementPC(pc, &forStmt, STMT_FOR_LOOP); |
|
4102 |
|
4103 bool isForEach = false; |
|
4104 unsigned iflags = 0; |
|
4105 |
|
4106 if (allowsForEachIn() && tokenStream.matchContextualKeyword(context->names().each)) { |
|
4107 iflags = JSITER_FOREACH; |
|
4108 isForEach = true; |
|
4109 } |
|
4110 |
|
4111 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); |
|
4112 |
|
4113 /* |
|
4114 * True if we have 'for (var/let/const ...)', except in the oddball case |
|
4115 * where 'let' begins a let-expression in 'for (let (...) ...)'. |
|
4116 */ |
|
4117 bool isForDecl = false; |
|
4118 |
|
4119 /* Non-null when isForDecl is true for a 'for (let ...)' statement. */ |
|
4120 RootedStaticBlockObject blockObj(context); |
|
4121 |
|
4122 /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */ |
|
4123 ParseNode *pn1; |
|
4124 |
|
4125 { |
|
4126 TokenKind tt = tokenStream.peekToken(TokenStream::Operand); |
|
4127 if (tt == TOK_SEMI) { |
|
4128 pn1 = nullptr; |
|
4129 } else { |
|
4130 /* |
|
4131 * Set pn1 to a var list or an initializing expression. |
|
4132 * |
|
4133 * Set the parsingForInit flag during parsing of the first clause |
|
4134 * of the for statement. This flag will be used by the RelExpr |
|
4135 * production; if it is set, then the 'in' keyword will not be |
|
4136 * recognized as an operator, leaving it available to be parsed as |
|
4137 * part of a for/in loop. |
|
4138 * |
|
4139 * A side effect of this restriction is that (unparenthesized) |
|
4140 * expressions involving an 'in' operator are illegal in the init |
|
4141 * clause of an ordinary for loop. |
|
4142 */ |
|
4143 pc->parsingForInit = true; |
|
4144 if (tt == TOK_VAR || tt == TOK_CONST) { |
|
4145 isForDecl = true; |
|
4146 tokenStream.consumeKnownToken(tt); |
|
4147 pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST); |
|
4148 } |
|
4149 else if (tt == TOK_LET) { |
|
4150 handler.disableSyntaxParser(); |
|
4151 (void) tokenStream.getToken(); |
|
4152 if (tokenStream.peekToken() == TOK_LP) { |
|
4153 pn1 = letBlock(LetExpresion); |
|
4154 } else { |
|
4155 isForDecl = true; |
|
4156 blockObj = StaticBlockObject::create(context); |
|
4157 if (!blockObj) |
|
4158 return null(); |
|
4159 pn1 = variables(PNK_LET, nullptr, blockObj, DontHoistVars); |
|
4160 } |
|
4161 } |
|
4162 else { |
|
4163 pn1 = expr(); |
|
4164 } |
|
4165 pc->parsingForInit = false; |
|
4166 if (!pn1) |
|
4167 return null(); |
|
4168 } |
|
4169 } |
|
4170 |
|
4171 JS_ASSERT_IF(isForDecl, pn1->isArity(PN_LIST)); |
|
4172 JS_ASSERT(!!blockObj == (isForDecl && pn1->isOp(JSOP_NOP))); |
|
4173 |
|
4174 // The form 'for (let <vars>; <expr2>; <expr3>) <stmt>' generates an |
|
4175 // implicit block even if stmt is not a BlockStatement. |
|
4176 // If the loop has that exact form, then: |
|
4177 // - forLetImpliedBlock is the node for the implicit block scope. |
|
4178 // - forLetDecl is the node for the decl 'let <vars>'. |
|
4179 // Otherwise both are null. |
|
4180 ParseNode *forLetImpliedBlock = nullptr; |
|
4181 ParseNode *forLetDecl = nullptr; |
|
4182 |
|
4183 // If non-null, the node for the decl 'var v = expr1' in the weirdo form |
|
4184 // 'for (var v = expr1 in expr2) stmt'. |
|
4185 ParseNode *hoistedVar = nullptr; |
|
4186 |
|
4187 /* |
|
4188 * We can be sure that it's a for/in loop if there's still an 'in' |
|
4189 * keyword here, even if JavaScript recognizes 'in' as an operator, |
|
4190 * as we've excluded 'in' from being parsed in RelExpr by setting |
|
4191 * pc->parsingForInit. |
|
4192 */ |
|
4193 StmtInfoPC letStmt(context); /* used if blockObj != nullptr. */ |
|
4194 ParseNode *pn2, *pn3; /* forHead->pn_kid2 and pn_kid3. */ |
|
4195 ParseNodeKind headKind = PNK_FORHEAD; |
|
4196 if (pn1) { |
|
4197 bool isForOf; |
|
4198 if (matchInOrOf(&isForOf)) |
|
4199 headKind = isForOf ? PNK_FOROF : PNK_FORIN; |
|
4200 } |
|
4201 |
|
4202 if (headKind == PNK_FOROF || headKind == PNK_FORIN) { |
|
4203 /* |
|
4204 * Parse the rest of the for/in or for/of head. |
|
4205 * |
|
4206 * Here pn1 is everything to the left of 'in' or 'of'. At the end of |
|
4207 * this block, pn1 is a decl or nullptr, pn2 is the assignment target |
|
4208 * that receives the enumeration value each iteration, and pn3 is the |
|
4209 * rhs of 'in'. |
|
4210 */ |
|
4211 if (headKind == PNK_FOROF) { |
|
4212 forStmt.type = STMT_FOR_OF_LOOP; |
|
4213 forStmt.type = (headKind == PNK_FOROF) ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP; |
|
4214 if (isForEach) { |
|
4215 report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP); |
|
4216 return null(); |
|
4217 } |
|
4218 } else { |
|
4219 forStmt.type = STMT_FOR_IN_LOOP; |
|
4220 iflags |= JSITER_ENUMERATE; |
|
4221 } |
|
4222 |
|
4223 /* Check that the left side of the 'in' or 'of' is valid. */ |
|
4224 if (!isValidForStatementLHS(pn1, versionNumber(), isForDecl, isForEach, headKind)) { |
|
4225 report(ParseError, false, pn1, JSMSG_BAD_FOR_LEFTSIDE); |
|
4226 return null(); |
|
4227 } |
|
4228 |
|
4229 /* |
|
4230 * After the following if-else, pn2 will point to the name or |
|
4231 * destructuring pattern on in's left. pn1 will point to the decl, if |
|
4232 * any, else nullptr. Note that the "declaration with initializer" case |
|
4233 * rewrites the loop-head, moving the decl and setting pn1 to nullptr. |
|
4234 */ |
|
4235 if (isForDecl) { |
|
4236 pn2 = pn1->pn_head; |
|
4237 if ((pn2->isKind(PNK_NAME) && pn2->maybeExpr()) || pn2->isKind(PNK_ASSIGN)) { |
|
4238 /* |
|
4239 * Declaration with initializer. |
|
4240 * |
|
4241 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'var' or |
|
4242 * 'const' to hoist the initializer or the entire decl out of |
|
4243 * the loop head. |
|
4244 */ |
|
4245 if (headKind == PNK_FOROF) { |
|
4246 report(ParseError, false, pn2, JSMSG_INVALID_FOR_OF_INIT); |
|
4247 return null(); |
|
4248 } |
|
4249 if (blockObj) { |
|
4250 report(ParseError, false, pn2, JSMSG_INVALID_FOR_IN_INIT); |
|
4251 return null(); |
|
4252 } |
|
4253 |
|
4254 hoistedVar = pn1; |
|
4255 |
|
4256 /* |
|
4257 * All of 'var x = i' is hoisted above 'for (x in o)'. |
|
4258 * |
|
4259 * Request JSOP_POP here since the var is for a simple |
|
4260 * name (it is not a destructuring binding's left-hand |
|
4261 * side) and it has an initializer. |
|
4262 */ |
|
4263 pn1->pn_xflags |= PNX_POPVAR; |
|
4264 pn1 = nullptr; |
|
4265 |
|
4266 if (pn2->isKind(PNK_ASSIGN)) { |
|
4267 pn2 = pn2->pn_left; |
|
4268 JS_ASSERT(pn2->isKind(PNK_ARRAY) || pn2->isKind(PNK_OBJECT) || |
|
4269 pn2->isKind(PNK_NAME)); |
|
4270 } |
|
4271 } |
|
4272 } else { |
|
4273 /* Not a declaration. */ |
|
4274 JS_ASSERT(!blockObj); |
|
4275 pn2 = pn1; |
|
4276 pn1 = nullptr; |
|
4277 |
|
4278 if (!checkAndMarkAsAssignmentLhs(pn2, PlainAssignment)) |
|
4279 return null(); |
|
4280 } |
|
4281 |
|
4282 pn3 = (headKind == PNK_FOROF) ? assignExpr() : expr(); |
|
4283 if (!pn3) |
|
4284 return null(); |
|
4285 |
|
4286 if (blockObj) { |
|
4287 /* |
|
4288 * Now that the pn3 has been parsed, push the let scope. To hold |
|
4289 * the blockObj for the emitter, wrap the PNK_LEXICALSCOPE node |
|
4290 * created by PushLetScope around the for's initializer. This also |
|
4291 * serves to indicate the let-decl to the emitter. |
|
4292 */ |
|
4293 ParseNode *block = pushLetScope(blockObj, &letStmt); |
|
4294 if (!block) |
|
4295 return null(); |
|
4296 letStmt.isForLetBlock = true; |
|
4297 block->pn_expr = pn1; |
|
4298 block->pn_pos = pn1->pn_pos; |
|
4299 pn1 = block; |
|
4300 } |
|
4301 |
|
4302 if (isForDecl) { |
|
4303 /* |
|
4304 * pn2 is part of a declaration. Make a copy that can be passed to |
|
4305 * EmitAssignment. Take care to do this after PushLetScope. |
|
4306 */ |
|
4307 pn2 = cloneLeftHandSide(pn2); |
|
4308 if (!pn2) |
|
4309 return null(); |
|
4310 } |
|
4311 |
|
4312 switch (pn2->getKind()) { |
|
4313 case PNK_NAME: |
|
4314 /* Beware 'for (arguments in ...)' with or without a 'var'. */ |
|
4315 pn2->markAsAssigned(); |
|
4316 break; |
|
4317 |
|
4318 case PNK_ASSIGN: |
|
4319 MOZ_ASSUME_UNREACHABLE("forStatement TOK_ASSIGN"); |
|
4320 |
|
4321 case PNK_ARRAY: |
|
4322 case PNK_OBJECT: |
|
4323 if (versionNumber() == JSVERSION_1_7) { |
|
4324 /* |
|
4325 * Destructuring for-in requires [key, value] enumeration |
|
4326 * in JS1.7. |
|
4327 */ |
|
4328 if (!isForEach && headKind == PNK_FORIN) |
|
4329 iflags |= JSITER_FOREACH | JSITER_KEYVALUE; |
|
4330 } |
|
4331 break; |
|
4332 |
|
4333 default:; |
|
4334 } |
|
4335 } else { |
|
4336 if (isForEach) { |
|
4337 reportWithOffset(ParseError, false, begin, JSMSG_BAD_FOR_EACH_LOOP); |
|
4338 return null(); |
|
4339 } |
|
4340 |
|
4341 headKind = PNK_FORHEAD; |
|
4342 |
|
4343 if (blockObj) { |
|
4344 /* |
|
4345 * Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }' |
|
4346 * to induce the correct scoping for A. |
|
4347 */ |
|
4348 forLetImpliedBlock = pushLetScope(blockObj, &letStmt); |
|
4349 if (!forLetImpliedBlock) |
|
4350 return null(); |
|
4351 letStmt.isForLetBlock = true; |
|
4352 |
|
4353 forLetDecl = pn1; |
|
4354 pn1 = nullptr; |
|
4355 } |
|
4356 |
|
4357 /* Parse the loop condition or null into pn2. */ |
|
4358 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); |
|
4359 if (tokenStream.peekToken(TokenStream::Operand) == TOK_SEMI) { |
|
4360 pn2 = nullptr; |
|
4361 } else { |
|
4362 pn2 = expr(); |
|
4363 if (!pn2) |
|
4364 return null(); |
|
4365 } |
|
4366 |
|
4367 /* Parse the update expression or null into pn3. */ |
|
4368 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND); |
|
4369 if (tokenStream.peekToken(TokenStream::Operand) == TOK_RP) { |
|
4370 pn3 = nullptr; |
|
4371 } else { |
|
4372 pn3 = expr(); |
|
4373 if (!pn3) |
|
4374 return null(); |
|
4375 } |
|
4376 } |
|
4377 |
|
4378 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); |
|
4379 |
|
4380 TokenPos headPos(begin, pos().end); |
|
4381 ParseNode *forHead = handler.newForHead(headKind, pn1, pn2, pn3, headPos); |
|
4382 if (!forHead) |
|
4383 return null(); |
|
4384 |
|
4385 /* Parse the loop body. */ |
|
4386 ParseNode *body = statement(); |
|
4387 if (!body) |
|
4388 return null(); |
|
4389 |
|
4390 if (blockObj) |
|
4391 PopStatementPC(tokenStream, pc); |
|
4392 PopStatementPC(tokenStream, pc); |
|
4393 |
|
4394 ParseNode *forLoop = handler.newForStatement(begin, forHead, body, iflags); |
|
4395 if (!forLoop) |
|
4396 return null(); |
|
4397 |
|
4398 if (hoistedVar) { |
|
4399 ParseNode *pnseq = handler.newList(PNK_SEQ, hoistedVar); |
|
4400 if (!pnseq) |
|
4401 return null(); |
|
4402 pnseq->pn_pos = forLoop->pn_pos; |
|
4403 pnseq->append(forLoop); |
|
4404 return pnseq; |
|
4405 } |
|
4406 if (forLetImpliedBlock) { |
|
4407 forLetImpliedBlock->pn_expr = forLoop; |
|
4408 forLetImpliedBlock->pn_pos = forLoop->pn_pos; |
|
4409 ParseNode *let = handler.newBinary(PNK_LET, forLetDecl, forLetImpliedBlock); |
|
4410 if (!let) |
|
4411 return null(); |
|
4412 let->pn_pos = forLoop->pn_pos; |
|
4413 return let; |
|
4414 } |
|
4415 return forLoop; |
|
4416 } |
|
4417 |
|
4418 template <> |
|
4419 SyntaxParseHandler::Node |
|
4420 Parser<SyntaxParseHandler>::forStatement() |
|
4421 { |
|
4422 /* |
|
4423 * 'for' statement parsing is fantastically complicated and requires being |
|
4424 * able to inspect the parse tree for previous parts of the 'for'. Syntax |
|
4425 * parsing of 'for' statements is thus done separately, and only handles |
|
4426 * the types of 'for' statements likely to be seen in web content. |
|
4427 */ |
|
4428 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); |
|
4429 |
|
4430 StmtInfoPC forStmt(context); |
|
4431 PushStatementPC(pc, &forStmt, STMT_FOR_LOOP); |
|
4432 |
|
4433 /* Don't parse 'for each' loops. */ |
|
4434 if (allowsForEachIn()) { |
|
4435 TokenKind tt = tokenStream.peekToken(); |
|
4436 // Not all "yield" tokens are names, but the ones that aren't names are |
|
4437 // invalid in this context anyway. |
|
4438 if (tt == TOK_NAME || tt == TOK_YIELD) { |
|
4439 JS_ALWAYS_FALSE(abortIfSyntaxParser()); |
|
4440 return null(); |
|
4441 } |
|
4442 } |
|
4443 |
|
4444 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); |
|
4445 |
|
4446 /* True if we have 'for (var ...)'. */ |
|
4447 bool isForDecl = false; |
|
4448 bool simpleForDecl = true; |
|
4449 |
|
4450 /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */ |
|
4451 Node lhsNode; |
|
4452 |
|
4453 { |
|
4454 TokenKind tt = tokenStream.peekToken(TokenStream::Operand); |
|
4455 if (tt == TOK_SEMI) { |
|
4456 lhsNode = null(); |
|
4457 } else { |
|
4458 /* Set lhsNode to a var list or an initializing expression. */ |
|
4459 pc->parsingForInit = true; |
|
4460 if (tt == TOK_VAR) { |
|
4461 isForDecl = true; |
|
4462 tokenStream.consumeKnownToken(tt); |
|
4463 lhsNode = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST, &simpleForDecl); |
|
4464 } |
|
4465 else if (tt == TOK_CONST || tt == TOK_LET) { |
|
4466 JS_ALWAYS_FALSE(abortIfSyntaxParser()); |
|
4467 return null(); |
|
4468 } |
|
4469 else { |
|
4470 lhsNode = expr(); |
|
4471 } |
|
4472 if (!lhsNode) |
|
4473 return null(); |
|
4474 pc->parsingForInit = false; |
|
4475 } |
|
4476 } |
|
4477 |
|
4478 /* |
|
4479 * We can be sure that it's a for/in loop if there's still an 'in' |
|
4480 * keyword here, even if JavaScript recognizes 'in' as an operator, |
|
4481 * as we've excluded 'in' from being parsed in RelExpr by setting |
|
4482 * pc->parsingForInit. |
|
4483 */ |
|
4484 bool isForOf; |
|
4485 if (lhsNode && matchInOrOf(&isForOf)) { |
|
4486 /* Parse the rest of the for/in or for/of head. */ |
|
4487 forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP; |
|
4488 |
|
4489 /* Check that the left side of the 'in' or 'of' is valid. */ |
|
4490 if (!isForDecl && |
|
4491 lhsNode != SyntaxParseHandler::NodeName && |
|
4492 lhsNode != SyntaxParseHandler::NodeGetProp && |
|
4493 lhsNode != SyntaxParseHandler::NodeLValue) |
|
4494 { |
|
4495 JS_ALWAYS_FALSE(abortIfSyntaxParser()); |
|
4496 return null(); |
|
4497 } |
|
4498 |
|
4499 if (!simpleForDecl) { |
|
4500 JS_ALWAYS_FALSE(abortIfSyntaxParser()); |
|
4501 return null(); |
|
4502 } |
|
4503 |
|
4504 if (!isForDecl && !checkAndMarkAsAssignmentLhs(lhsNode, PlainAssignment)) |
|
4505 return null(); |
|
4506 |
|
4507 if (!expr()) |
|
4508 return null(); |
|
4509 } else { |
|
4510 /* Parse the loop condition or null. */ |
|
4511 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); |
|
4512 if (tokenStream.peekToken(TokenStream::Operand) != TOK_SEMI) { |
|
4513 if (!expr()) |
|
4514 return null(); |
|
4515 } |
|
4516 |
|
4517 /* Parse the update expression or null. */ |
|
4518 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND); |
|
4519 if (tokenStream.peekToken(TokenStream::Operand) != TOK_RP) { |
|
4520 if (!expr()) |
|
4521 return null(); |
|
4522 } |
|
4523 } |
|
4524 |
|
4525 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); |
|
4526 |
|
4527 /* Parse the loop body. */ |
|
4528 if (!statement()) |
|
4529 return null(); |
|
4530 |
|
4531 PopStatementPC(tokenStream, pc); |
|
4532 return SyntaxParseHandler::NodeGeneric; |
|
4533 } |
|
4534 |
|
4535 template <typename ParseHandler> |
|
4536 typename ParseHandler::Node |
|
4537 Parser<ParseHandler>::switchStatement() |
|
4538 { |
|
4539 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH)); |
|
4540 uint32_t begin = pos().begin; |
|
4541 |
|
4542 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH); |
|
4543 |
|
4544 Node discriminant = exprInParens(); |
|
4545 if (!discriminant) |
|
4546 return null(); |
|
4547 |
|
4548 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH); |
|
4549 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH); |
|
4550 |
|
4551 StmtInfoPC stmtInfo(context); |
|
4552 PushStatementPC(pc, &stmtInfo, STMT_SWITCH); |
|
4553 |
|
4554 if (!GenerateBlockId(tokenStream, pc, pc->topStmt->blockid)) |
|
4555 return null(); |
|
4556 |
|
4557 Node caseList = handler.newStatementList(pc->blockid(), pos()); |
|
4558 if (!caseList) |
|
4559 return null(); |
|
4560 |
|
4561 Node saveBlock = pc->blockNode; |
|
4562 pc->blockNode = caseList; |
|
4563 |
|
4564 bool seenDefault = false; |
|
4565 TokenKind tt; |
|
4566 while ((tt = tokenStream.getToken()) != TOK_RC) { |
|
4567 uint32_t caseBegin = pos().begin; |
|
4568 |
|
4569 Node caseExpr; |
|
4570 switch (tt) { |
|
4571 case TOK_DEFAULT: |
|
4572 if (seenDefault) { |
|
4573 report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS); |
|
4574 return null(); |
|
4575 } |
|
4576 seenDefault = true; |
|
4577 caseExpr = null(); // The default case has pn_left == nullptr. |
|
4578 break; |
|
4579 |
|
4580 case TOK_CASE: |
|
4581 caseExpr = expr(); |
|
4582 if (!caseExpr) |
|
4583 return null(); |
|
4584 break; |
|
4585 |
|
4586 case TOK_ERROR: |
|
4587 return null(); |
|
4588 |
|
4589 default: |
|
4590 report(ParseError, false, null(), JSMSG_BAD_SWITCH); |
|
4591 return null(); |
|
4592 } |
|
4593 |
|
4594 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE); |
|
4595 |
|
4596 Node body = handler.newStatementList(pc->blockid(), pos()); |
|
4597 if (!body) |
|
4598 return null(); |
|
4599 |
|
4600 while ((tt = tokenStream.peekToken(TokenStream::Operand)) != TOK_RC && |
|
4601 tt != TOK_CASE && tt != TOK_DEFAULT) { |
|
4602 if (tt == TOK_ERROR) |
|
4603 return null(); |
|
4604 Node stmt = statement(); |
|
4605 if (!stmt) |
|
4606 return null(); |
|
4607 handler.addList(body, stmt); |
|
4608 } |
|
4609 |
|
4610 Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body); |
|
4611 if (!casepn) |
|
4612 return null(); |
|
4613 handler.addList(caseList, casepn); |
|
4614 } |
|
4615 |
|
4616 /* |
|
4617 * Handle the case where there was a let declaration in any case in |
|
4618 * the switch body, but not within an inner block. If it replaced |
|
4619 * pc->blockNode with a new block node then we must refresh caseList and |
|
4620 * then restore pc->blockNode. |
|
4621 */ |
|
4622 if (pc->blockNode != caseList) |
|
4623 caseList = pc->blockNode; |
|
4624 pc->blockNode = saveBlock; |
|
4625 |
|
4626 PopStatementPC(tokenStream, pc); |
|
4627 |
|
4628 handler.setEndPosition(caseList, pos().end); |
|
4629 |
|
4630 return handler.newSwitchStatement(begin, discriminant, caseList); |
|
4631 } |
|
4632 |
|
4633 template <typename ParseHandler> |
|
4634 typename ParseHandler::Node |
|
4635 Parser<ParseHandler>::continueStatement() |
|
4636 { |
|
4637 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_CONTINUE)); |
|
4638 uint32_t begin = pos().begin; |
|
4639 |
|
4640 RootedPropertyName label(context); |
|
4641 if (!matchLabel(&label)) |
|
4642 return null(); |
|
4643 |
|
4644 StmtInfoPC *stmt = pc->topStmt; |
|
4645 if (label) { |
|
4646 for (StmtInfoPC *stmt2 = nullptr; ; stmt = stmt->down) { |
|
4647 if (!stmt) { |
|
4648 report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND); |
|
4649 return null(); |
|
4650 } |
|
4651 if (stmt->type == STMT_LABEL) { |
|
4652 if (stmt->label == label) { |
|
4653 if (!stmt2 || !stmt2->isLoop()) { |
|
4654 report(ParseError, false, null(), JSMSG_BAD_CONTINUE); |
|
4655 return null(); |
|
4656 } |
|
4657 break; |
|
4658 } |
|
4659 } else { |
|
4660 stmt2 = stmt; |
|
4661 } |
|
4662 } |
|
4663 } else { |
|
4664 for (; ; stmt = stmt->down) { |
|
4665 if (!stmt) { |
|
4666 report(ParseError, false, null(), JSMSG_BAD_CONTINUE); |
|
4667 return null(); |
|
4668 } |
|
4669 if (stmt->isLoop()) |
|
4670 break; |
|
4671 } |
|
4672 } |
|
4673 |
|
4674 if (!MatchOrInsertSemicolon(tokenStream)) |
|
4675 return null(); |
|
4676 |
|
4677 return handler.newContinueStatement(label, TokenPos(begin, pos().end)); |
|
4678 } |
|
4679 |
|
4680 template <typename ParseHandler> |
|
4681 typename ParseHandler::Node |
|
4682 Parser<ParseHandler>::breakStatement() |
|
4683 { |
|
4684 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_BREAK)); |
|
4685 uint32_t begin = pos().begin; |
|
4686 |
|
4687 RootedPropertyName label(context); |
|
4688 if (!matchLabel(&label)) |
|
4689 return null(); |
|
4690 StmtInfoPC *stmt = pc->topStmt; |
|
4691 if (label) { |
|
4692 for (; ; stmt = stmt->down) { |
|
4693 if (!stmt) { |
|
4694 report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND); |
|
4695 return null(); |
|
4696 } |
|
4697 if (stmt->type == STMT_LABEL && stmt->label == label) |
|
4698 break; |
|
4699 } |
|
4700 } else { |
|
4701 for (; ; stmt = stmt->down) { |
|
4702 if (!stmt) { |
|
4703 report(ParseError, false, null(), JSMSG_TOUGH_BREAK); |
|
4704 return null(); |
|
4705 } |
|
4706 if (stmt->isLoop() || stmt->type == STMT_SWITCH) |
|
4707 break; |
|
4708 } |
|
4709 } |
|
4710 |
|
4711 if (!MatchOrInsertSemicolon(tokenStream)) |
|
4712 return null(); |
|
4713 |
|
4714 return handler.newBreakStatement(label, TokenPos(begin, pos().end)); |
|
4715 } |
|
4716 |
|
4717 template <typename ParseHandler> |
|
4718 typename ParseHandler::Node |
|
4719 Parser<ParseHandler>::returnStatement() |
|
4720 { |
|
4721 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN)); |
|
4722 uint32_t begin = pos().begin; |
|
4723 |
|
4724 if (!pc->sc->isFunctionBox()) { |
|
4725 report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str); |
|
4726 return null(); |
|
4727 } |
|
4728 |
|
4729 // Parse an optional operand. |
|
4730 // |
|
4731 // This is ugly, but we don't want to require a semicolon. |
|
4732 Node exprNode; |
|
4733 switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) { |
|
4734 case TOK_ERROR: |
|
4735 return null(); |
|
4736 case TOK_EOF: |
|
4737 case TOK_EOL: |
|
4738 case TOK_SEMI: |
|
4739 case TOK_RC: |
|
4740 exprNode = null(); |
|
4741 pc->funHasReturnVoid = true; |
|
4742 break; |
|
4743 default: { |
|
4744 exprNode = expr(); |
|
4745 if (!exprNode) |
|
4746 return null(); |
|
4747 pc->funHasReturnExpr = true; |
|
4748 } |
|
4749 } |
|
4750 |
|
4751 if (!MatchOrInsertSemicolon(tokenStream)) |
|
4752 return null(); |
|
4753 |
|
4754 Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end)); |
|
4755 if (!pn) |
|
4756 return null(); |
|
4757 |
|
4758 if (options().extraWarningsOption && pc->funHasReturnExpr && pc->funHasReturnVoid && |
|
4759 !reportBadReturn(pn, ParseExtraWarning, |
|
4760 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE)) |
|
4761 { |
|
4762 return null(); |
|
4763 } |
|
4764 |
|
4765 if (pc->isLegacyGenerator() && exprNode) { |
|
4766 /* Disallow "return v;" in legacy generators. */ |
|
4767 reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN, |
|
4768 JSMSG_BAD_ANON_GENERATOR_RETURN); |
|
4769 return null(); |
|
4770 } |
|
4771 |
|
4772 return pn; |
|
4773 } |
|
4774 |
|
4775 template <typename ParseHandler> |
|
4776 typename ParseHandler::Node |
|
4777 Parser<ParseHandler>::yieldExpression() |
|
4778 { |
|
4779 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD)); |
|
4780 uint32_t begin = pos().begin; |
|
4781 |
|
4782 switch (pc->generatorKind()) { |
|
4783 case StarGenerator: |
|
4784 { |
|
4785 JS_ASSERT(pc->sc->isFunctionBox()); |
|
4786 |
|
4787 pc->lastYieldOffset = begin; |
|
4788 |
|
4789 ParseNodeKind kind = tokenStream.matchToken(TOK_MUL) ? PNK_YIELD_STAR : PNK_YIELD; |
|
4790 |
|
4791 // ES6 generators require a value. |
|
4792 Node exprNode = assignExpr(); |
|
4793 if (!exprNode) |
|
4794 return null(); |
|
4795 |
|
4796 return handler.newUnary(kind, JSOP_NOP, begin, exprNode); |
|
4797 } |
|
4798 |
|
4799 case NotGenerator: |
|
4800 // We are in code that has not seen a yield, but we are in JS 1.7 or |
|
4801 // later. Try to transition to being a legacy generator. |
|
4802 JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7); |
|
4803 JS_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset); |
|
4804 |
|
4805 if (!abortIfSyntaxParser()) |
|
4806 return null(); |
|
4807 |
|
4808 if (!pc->sc->isFunctionBox()) { |
|
4809 report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str); |
|
4810 return null(); |
|
4811 } |
|
4812 |
|
4813 pc->sc->asFunctionBox()->setGeneratorKind(LegacyGenerator); |
|
4814 |
|
4815 if (pc->funHasReturnExpr) { |
|
4816 /* As in Python (see PEP-255), disallow return v; in generators. */ |
|
4817 reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN, |
|
4818 JSMSG_BAD_ANON_GENERATOR_RETURN); |
|
4819 return null(); |
|
4820 } |
|
4821 // Fall through. |
|
4822 |
|
4823 case LegacyGenerator: |
|
4824 { |
|
4825 // We are in a legacy generator: a function that has already seen a |
|
4826 // yield, or in a legacy generator comprehension. |
|
4827 JS_ASSERT(pc->sc->isFunctionBox()); |
|
4828 |
|
4829 pc->lastYieldOffset = begin; |
|
4830 |
|
4831 // Legacy generators do not require a value. |
|
4832 Node exprNode; |
|
4833 switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) { |
|
4834 case TOK_ERROR: |
|
4835 return null(); |
|
4836 case TOK_EOF: |
|
4837 case TOK_EOL: |
|
4838 case TOK_SEMI: |
|
4839 case TOK_RC: |
|
4840 case TOK_RB: |
|
4841 case TOK_RP: |
|
4842 case TOK_COLON: |
|
4843 case TOK_COMMA: |
|
4844 // No value. |
|
4845 exprNode = null(); |
|
4846 // ES6 does not permit yield without an operand. We should |
|
4847 // encourage users of yield expressions of this kind to pass an |
|
4848 // operand, to bring users closer to standard syntax. |
|
4849 if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND)) |
|
4850 return null(); |
|
4851 break; |
|
4852 default: |
|
4853 exprNode = assignExpr(); |
|
4854 if (!exprNode) |
|
4855 return null(); |
|
4856 } |
|
4857 |
|
4858 return handler.newUnary(PNK_YIELD, JSOP_NOP, begin, exprNode); |
|
4859 } |
|
4860 } |
|
4861 |
|
4862 MOZ_ASSUME_UNREACHABLE("yieldExpr"); |
|
4863 } |
|
4864 |
|
4865 template <> |
|
4866 ParseNode * |
|
4867 Parser<FullParseHandler>::withStatement() |
|
4868 { |
|
4869 // test262/ch12/12.10/12.10-0-1.js fails if we try to parse with-statements |
|
4870 // in syntax-parse mode. See bug 892583. |
|
4871 if (handler.syntaxParser) { |
|
4872 handler.disableSyntaxParser(); |
|
4873 abortedSyntaxParse = true; |
|
4874 return null(); |
|
4875 } |
|
4876 |
|
4877 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH)); |
|
4878 uint32_t begin = pos().begin; |
|
4879 |
|
4880 // In most cases, we want the constructs forbidden in strict mode code to be |
|
4881 // a subset of those that JSOPTION_EXTRA_WARNINGS warns about, and we should |
|
4882 // use reportStrictModeError. However, 'with' is the sole instance of a |
|
4883 // construct that is forbidden in strict mode code, but doesn't even merit a |
|
4884 // warning under JSOPTION_EXTRA_WARNINGS. See |
|
4885 // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1. |
|
4886 if (pc->sc->strict && !report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH)) |
|
4887 return null(); |
|
4888 |
|
4889 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH); |
|
4890 Node objectExpr = exprInParens(); |
|
4891 if (!objectExpr) |
|
4892 return null(); |
|
4893 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH); |
|
4894 |
|
4895 bool oldParsingWith = pc->parsingWith; |
|
4896 pc->parsingWith = true; |
|
4897 |
|
4898 StmtInfoPC stmtInfo(context); |
|
4899 PushStatementPC(pc, &stmtInfo, STMT_WITH); |
|
4900 Rooted<StaticWithObject *> staticWith(context, StaticWithObject::create(context)); |
|
4901 if (!staticWith) |
|
4902 return null(); |
|
4903 staticWith->initEnclosingNestedScopeFromParser(pc->staticScope); |
|
4904 FinishPushNestedScope(pc, &stmtInfo, *staticWith); |
|
4905 |
|
4906 Node innerBlock = statement(); |
|
4907 if (!innerBlock) |
|
4908 return null(); |
|
4909 |
|
4910 PopStatementPC(tokenStream, pc); |
|
4911 |
|
4912 pc->sc->setBindingsAccessedDynamically(); |
|
4913 pc->parsingWith = oldParsingWith; |
|
4914 |
|
4915 /* |
|
4916 * Make sure to deoptimize lexical dependencies inside the |with| |
|
4917 * to safely optimize binding globals (see bug 561923). |
|
4918 */ |
|
4919 for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) { |
|
4920 DefinitionNode defn = r.front().value().get<FullParseHandler>(); |
|
4921 DefinitionNode lexdep = handler.resolve(defn); |
|
4922 handler.deoptimizeUsesWithin(lexdep, TokenPos(begin, pos().begin)); |
|
4923 } |
|
4924 |
|
4925 ObjectBox *staticWithBox = newObjectBox(staticWith); |
|
4926 if (!staticWithBox) |
|
4927 return null(); |
|
4928 return handler.newWithStatement(begin, objectExpr, innerBlock, staticWithBox); |
|
4929 } |
|
4930 |
|
4931 template <> |
|
4932 SyntaxParseHandler::Node |
|
4933 Parser<SyntaxParseHandler>::withStatement() |
|
4934 { |
|
4935 JS_ALWAYS_FALSE(abortIfSyntaxParser()); |
|
4936 return null(); |
|
4937 } |
|
4938 |
|
4939 template <typename ParseHandler> |
|
4940 typename ParseHandler::Node |
|
4941 Parser<ParseHandler>::labeledStatement() |
|
4942 { |
|
4943 uint32_t begin = pos().begin; |
|
4944 RootedPropertyName label(context, tokenStream.currentName()); |
|
4945 for (StmtInfoPC *stmt = pc->topStmt; stmt; stmt = stmt->down) { |
|
4946 if (stmt->type == STMT_LABEL && stmt->label == label) { |
|
4947 report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL); |
|
4948 return null(); |
|
4949 } |
|
4950 } |
|
4951 |
|
4952 tokenStream.consumeKnownToken(TOK_COLON); |
|
4953 |
|
4954 /* Push a label struct and parse the statement. */ |
|
4955 StmtInfoPC stmtInfo(context); |
|
4956 PushStatementPC(pc, &stmtInfo, STMT_LABEL); |
|
4957 stmtInfo.label = label; |
|
4958 Node pn = statement(); |
|
4959 if (!pn) |
|
4960 return null(); |
|
4961 |
|
4962 /* Pop the label, set pn_expr, and return early. */ |
|
4963 PopStatementPC(tokenStream, pc); |
|
4964 |
|
4965 return handler.newLabeledStatement(label, pn, begin); |
|
4966 } |
|
4967 |
|
4968 template <typename ParseHandler> |
|
4969 typename ParseHandler::Node |
|
4970 Parser<ParseHandler>::throwStatement() |
|
4971 { |
|
4972 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_THROW)); |
|
4973 uint32_t begin = pos().begin; |
|
4974 |
|
4975 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */ |
|
4976 TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand); |
|
4977 if (tt == TOK_ERROR) |
|
4978 return null(); |
|
4979 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) { |
|
4980 report(ParseError, false, null(), JSMSG_SYNTAX_ERROR); |
|
4981 return null(); |
|
4982 } |
|
4983 |
|
4984 Node throwExpr = expr(); |
|
4985 if (!throwExpr) |
|
4986 return null(); |
|
4987 |
|
4988 if (!MatchOrInsertSemicolon(tokenStream)) |
|
4989 return null(); |
|
4990 |
|
4991 return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end)); |
|
4992 } |
|
4993 |
|
4994 template <typename ParseHandler> |
|
4995 typename ParseHandler::Node |
|
4996 Parser<ParseHandler>::tryStatement() |
|
4997 { |
|
4998 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_TRY)); |
|
4999 uint32_t begin = pos().begin; |
|
5000 |
|
5001 /* |
|
5002 * try nodes are ternary. |
|
5003 * kid1 is the try statement |
|
5004 * kid2 is the catch node list or null |
|
5005 * kid3 is the finally statement |
|
5006 * |
|
5007 * catch nodes are ternary. |
|
5008 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC) |
|
5009 * kid2 is the catch guard or null if no guard |
|
5010 * kid3 is the catch block |
|
5011 * |
|
5012 * catch lvalue nodes are either: |
|
5013 * TOK_NAME for a single identifier |
|
5014 * TOK_RB or TOK_RC for a destructuring left-hand side |
|
5015 * |
|
5016 * finally nodes are TOK_LC statement lists. |
|
5017 */ |
|
5018 |
|
5019 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); |
|
5020 StmtInfoPC stmtInfo(context); |
|
5021 if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_TRY, pc)) |
|
5022 return null(); |
|
5023 Node innerBlock = statements(); |
|
5024 if (!innerBlock) |
|
5025 return null(); |
|
5026 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY); |
|
5027 PopStatementPC(tokenStream, pc); |
|
5028 |
|
5029 bool hasUnconditionalCatch = false; |
|
5030 Node catchList = null(); |
|
5031 TokenKind tt = tokenStream.getToken(); |
|
5032 if (tt == TOK_CATCH) { |
|
5033 catchList = handler.newList(PNK_CATCH); |
|
5034 if (!catchList) |
|
5035 return null(); |
|
5036 |
|
5037 do { |
|
5038 Node pnblock; |
|
5039 BindData<ParseHandler> data(context); |
|
5040 |
|
5041 /* Check for another catch after unconditional catch. */ |
|
5042 if (hasUnconditionalCatch) { |
|
5043 report(ParseError, false, null(), JSMSG_CATCH_AFTER_GENERAL); |
|
5044 return null(); |
|
5045 } |
|
5046 |
|
5047 /* |
|
5048 * Create a lexical scope node around the whole catch clause, |
|
5049 * including the head. |
|
5050 */ |
|
5051 pnblock = pushLexicalScope(&stmtInfo); |
|
5052 if (!pnblock) |
|
5053 return null(); |
|
5054 stmtInfo.type = STMT_CATCH; |
|
5055 |
|
5056 /* |
|
5057 * Legal catch forms are: |
|
5058 * catch (lhs) |
|
5059 * catch (lhs if <boolean_expression>) |
|
5060 * where lhs is a name or a destructuring left-hand side. |
|
5061 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD) |
|
5062 */ |
|
5063 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); |
|
5064 |
|
5065 /* |
|
5066 * Contrary to ECMA Ed. 3, the catch variable is lexically |
|
5067 * scoped, not a property of a new Object instance. This is |
|
5068 * an intentional change that anticipates ECMA Ed. 4. |
|
5069 */ |
|
5070 data.initLet(HoistVars, pc->staticScope->template as<StaticBlockObject>(), |
|
5071 JSMSG_TOO_MANY_CATCH_VARS); |
|
5072 JS_ASSERT(data.let.blockObj); |
|
5073 |
|
5074 tt = tokenStream.getToken(); |
|
5075 Node catchName; |
|
5076 switch (tt) { |
|
5077 case TOK_LB: |
|
5078 case TOK_LC: |
|
5079 catchName = destructuringExpr(&data, tt); |
|
5080 if (!catchName) |
|
5081 return null(); |
|
5082 break; |
|
5083 |
|
5084 case TOK_YIELD: |
|
5085 if (!checkYieldNameValidity()) |
|
5086 return null(); |
|
5087 // Fall through. |
|
5088 case TOK_NAME: |
|
5089 { |
|
5090 RootedPropertyName label(context, tokenStream.currentName()); |
|
5091 catchName = newBindingNode(label, false); |
|
5092 if (!catchName) |
|
5093 return null(); |
|
5094 data.pn = catchName; |
|
5095 if (!data.binder(&data, label, this)) |
|
5096 return null(); |
|
5097 break; |
|
5098 } |
|
5099 |
|
5100 default: |
|
5101 report(ParseError, false, null(), JSMSG_CATCH_IDENTIFIER); |
|
5102 return null(); |
|
5103 } |
|
5104 |
|
5105 Node catchGuard = null(); |
|
5106 #if JS_HAS_CATCH_GUARD |
|
5107 /* |
|
5108 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)') |
|
5109 * to avoid conflicting with the JS2/ECMAv4 type annotation |
|
5110 * catchguard syntax. |
|
5111 */ |
|
5112 if (tokenStream.matchToken(TOK_IF)) { |
|
5113 catchGuard = expr(); |
|
5114 if (!catchGuard) |
|
5115 return null(); |
|
5116 } |
|
5117 #endif |
|
5118 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH); |
|
5119 |
|
5120 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); |
|
5121 Node catchBody = statements(); |
|
5122 if (!catchBody) |
|
5123 return null(); |
|
5124 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH); |
|
5125 PopStatementPC(tokenStream, pc); |
|
5126 |
|
5127 if (!catchGuard) |
|
5128 hasUnconditionalCatch = true; |
|
5129 |
|
5130 if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody)) |
|
5131 return null(); |
|
5132 handler.setEndPosition(catchList, pos().end); |
|
5133 handler.setEndPosition(pnblock, pos().end); |
|
5134 |
|
5135 tt = tokenStream.getToken(TokenStream::Operand); |
|
5136 } while (tt == TOK_CATCH); |
|
5137 } |
|
5138 |
|
5139 Node finallyBlock = null(); |
|
5140 |
|
5141 if (tt == TOK_FINALLY) { |
|
5142 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY); |
|
5143 if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_FINALLY, pc)) |
|
5144 return null(); |
|
5145 finallyBlock = statements(); |
|
5146 if (!finallyBlock) |
|
5147 return null(); |
|
5148 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY); |
|
5149 PopStatementPC(tokenStream, pc); |
|
5150 } else { |
|
5151 tokenStream.ungetToken(); |
|
5152 } |
|
5153 if (!catchList && !finallyBlock) { |
|
5154 report(ParseError, false, null(), JSMSG_CATCH_OR_FINALLY); |
|
5155 return null(); |
|
5156 } |
|
5157 |
|
5158 return handler.newTryStatement(begin, innerBlock, catchList, finallyBlock); |
|
5159 } |
|
5160 |
|
5161 template <typename ParseHandler> |
|
5162 typename ParseHandler::Node |
|
5163 Parser<ParseHandler>::debuggerStatement() |
|
5164 { |
|
5165 TokenPos p; |
|
5166 p.begin = pos().begin; |
|
5167 if (!MatchOrInsertSemicolon(tokenStream)) |
|
5168 return null(); |
|
5169 p.end = pos().end; |
|
5170 |
|
5171 pc->sc->setBindingsAccessedDynamically(); |
|
5172 pc->sc->setHasDebuggerStatement(); |
|
5173 |
|
5174 return handler.newDebuggerStatement(p); |
|
5175 } |
|
5176 |
|
5177 template <typename ParseHandler> |
|
5178 typename ParseHandler::Node |
|
5179 Parser<ParseHandler>::statement(bool canHaveDirectives) |
|
5180 { |
|
5181 JS_CHECK_RECURSION(context, return null()); |
|
5182 |
|
5183 switch (TokenKind tt = tokenStream.getToken(TokenStream::Operand)) { |
|
5184 case TOK_LC: |
|
5185 return blockStatement(); |
|
5186 |
|
5187 case TOK_CONST: |
|
5188 if (!abortIfSyntaxParser()) |
|
5189 return null(); |
|
5190 // FALL THROUGH |
|
5191 case TOK_VAR: { |
|
5192 Node pn = variables(tt == TOK_CONST ? PNK_CONST : PNK_VAR); |
|
5193 if (!pn) |
|
5194 return null(); |
|
5195 |
|
5196 // Tell js_EmitTree to generate a final POP. |
|
5197 handler.setListFlag(pn, PNX_POPVAR); |
|
5198 |
|
5199 if (!MatchOrInsertSemicolon(tokenStream)) |
|
5200 return null(); |
|
5201 return pn; |
|
5202 } |
|
5203 |
|
5204 case TOK_LET: |
|
5205 return letStatement(); |
|
5206 case TOK_IMPORT: |
|
5207 return importDeclaration(); |
|
5208 case TOK_EXPORT: |
|
5209 return exportDeclaration(); |
|
5210 case TOK_SEMI: |
|
5211 return handler.newEmptyStatement(pos()); |
|
5212 case TOK_IF: |
|
5213 return ifStatement(); |
|
5214 case TOK_DO: |
|
5215 return doWhileStatement(); |
|
5216 case TOK_WHILE: |
|
5217 return whileStatement(); |
|
5218 case TOK_FOR: |
|
5219 return forStatement(); |
|
5220 case TOK_SWITCH: |
|
5221 return switchStatement(); |
|
5222 case TOK_CONTINUE: |
|
5223 return continueStatement(); |
|
5224 case TOK_BREAK: |
|
5225 return breakStatement(); |
|
5226 case TOK_RETURN: |
|
5227 return returnStatement(); |
|
5228 case TOK_WITH: |
|
5229 return withStatement(); |
|
5230 case TOK_THROW: |
|
5231 return throwStatement(); |
|
5232 case TOK_TRY: |
|
5233 return tryStatement(); |
|
5234 case TOK_FUNCTION: |
|
5235 return functionStmt(); |
|
5236 case TOK_DEBUGGER: |
|
5237 return debuggerStatement(); |
|
5238 |
|
5239 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */ |
|
5240 case TOK_CATCH: |
|
5241 report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY); |
|
5242 return null(); |
|
5243 |
|
5244 case TOK_FINALLY: |
|
5245 report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY); |
|
5246 return null(); |
|
5247 |
|
5248 case TOK_ERROR: |
|
5249 return null(); |
|
5250 |
|
5251 case TOK_STRING: |
|
5252 if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) { |
|
5253 if (!abortIfSyntaxParser()) |
|
5254 return null(); |
|
5255 if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL)) |
|
5256 return null(); |
|
5257 } |
|
5258 return expressionStatement(); |
|
5259 |
|
5260 case TOK_YIELD: |
|
5261 if (tokenStream.peekToken() == TOK_COLON) { |
|
5262 if (!checkYieldNameValidity()) |
|
5263 return null(); |
|
5264 return labeledStatement(); |
|
5265 } |
|
5266 return expressionStatement(); |
|
5267 |
|
5268 case TOK_NAME: |
|
5269 if (tokenStream.peekToken() == TOK_COLON) |
|
5270 return labeledStatement(); |
|
5271 return expressionStatement(); |
|
5272 |
|
5273 default: |
|
5274 return expressionStatement(); |
|
5275 } |
|
5276 } |
|
5277 |
|
5278 template <typename ParseHandler> |
|
5279 typename ParseHandler::Node |
|
5280 Parser<ParseHandler>::expr() |
|
5281 { |
|
5282 Node pn = assignExpr(); |
|
5283 if (pn && tokenStream.matchToken(TOK_COMMA)) { |
|
5284 Node seq = handler.newList(PNK_COMMA, pn); |
|
5285 if (!seq) |
|
5286 return null(); |
|
5287 do { |
|
5288 if (handler.isUnparenthesizedYield(pn)) { |
|
5289 report(ParseError, false, pn, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str); |
|
5290 return null(); |
|
5291 } |
|
5292 |
|
5293 pn = assignExpr(); |
|
5294 if (!pn) |
|
5295 return null(); |
|
5296 handler.addList(seq, pn); |
|
5297 } while (tokenStream.matchToken(TOK_COMMA)); |
|
5298 return seq; |
|
5299 } |
|
5300 return pn; |
|
5301 } |
|
5302 |
|
5303 static const JSOp ParseNodeKindToJSOp[] = { |
|
5304 JSOP_OR, |
|
5305 JSOP_AND, |
|
5306 JSOP_BITOR, |
|
5307 JSOP_BITXOR, |
|
5308 JSOP_BITAND, |
|
5309 JSOP_STRICTEQ, |
|
5310 JSOP_EQ, |
|
5311 JSOP_STRICTNE, |
|
5312 JSOP_NE, |
|
5313 JSOP_LT, |
|
5314 JSOP_LE, |
|
5315 JSOP_GT, |
|
5316 JSOP_GE, |
|
5317 JSOP_INSTANCEOF, |
|
5318 JSOP_IN, |
|
5319 JSOP_LSH, |
|
5320 JSOP_RSH, |
|
5321 JSOP_URSH, |
|
5322 JSOP_ADD, |
|
5323 JSOP_SUB, |
|
5324 JSOP_MUL, |
|
5325 JSOP_DIV, |
|
5326 JSOP_MOD |
|
5327 }; |
|
5328 |
|
5329 static inline JSOp |
|
5330 BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk) |
|
5331 { |
|
5332 JS_ASSERT(pnk >= PNK_BINOP_FIRST); |
|
5333 JS_ASSERT(pnk <= PNK_BINOP_LAST); |
|
5334 return ParseNodeKindToJSOp[pnk - PNK_BINOP_FIRST]; |
|
5335 } |
|
5336 |
|
5337 static bool |
|
5338 IsBinaryOpToken(TokenKind tok, bool parsingForInit) |
|
5339 { |
|
5340 return tok == TOK_IN ? !parsingForInit : TokenKindIsBinaryOp(tok); |
|
5341 } |
|
5342 |
|
5343 static ParseNodeKind |
|
5344 BinaryOpTokenKindToParseNodeKind(TokenKind tok) |
|
5345 { |
|
5346 JS_ASSERT(TokenKindIsBinaryOp(tok)); |
|
5347 return ParseNodeKind(PNK_BINOP_FIRST + (tok - TOK_BINOP_FIRST)); |
|
5348 } |
|
5349 |
|
5350 static const int PrecedenceTable[] = { |
|
5351 1, /* PNK_OR */ |
|
5352 2, /* PNK_AND */ |
|
5353 3, /* PNK_BITOR */ |
|
5354 4, /* PNK_BITXOR */ |
|
5355 5, /* PNK_BITAND */ |
|
5356 6, /* PNK_STRICTEQ */ |
|
5357 6, /* PNK_EQ */ |
|
5358 6, /* PNK_STRICTNE */ |
|
5359 6, /* PNK_NE */ |
|
5360 7, /* PNK_LT */ |
|
5361 7, /* PNK_LE */ |
|
5362 7, /* PNK_GT */ |
|
5363 7, /* PNK_GE */ |
|
5364 7, /* PNK_INSTANCEOF */ |
|
5365 7, /* PNK_IN */ |
|
5366 8, /* PNK_LSH */ |
|
5367 8, /* PNK_RSH */ |
|
5368 8, /* PNK_URSH */ |
|
5369 9, /* PNK_ADD */ |
|
5370 9, /* PNK_SUB */ |
|
5371 10, /* PNK_STAR */ |
|
5372 10, /* PNK_DIV */ |
|
5373 10 /* PNK_MOD */ |
|
5374 }; |
|
5375 |
|
5376 static const int PRECEDENCE_CLASSES = 10; |
|
5377 |
|
5378 static int |
|
5379 Precedence(ParseNodeKind pnk) { |
|
5380 // Everything binds tighter than PNK_LIMIT, because we want to reduce all |
|
5381 // nodes to a single node when we reach a token that is not another binary |
|
5382 // operator. |
|
5383 if (pnk == PNK_LIMIT) |
|
5384 return 0; |
|
5385 |
|
5386 JS_ASSERT(pnk >= PNK_BINOP_FIRST); |
|
5387 JS_ASSERT(pnk <= PNK_BINOP_LAST); |
|
5388 return PrecedenceTable[pnk - PNK_BINOP_FIRST]; |
|
5389 } |
|
5390 |
|
5391 template <typename ParseHandler> |
|
5392 MOZ_ALWAYS_INLINE typename ParseHandler::Node |
|
5393 Parser<ParseHandler>::orExpr1() |
|
5394 { |
|
5395 // Shift-reduce parser for the left-associative binary operator part of |
|
5396 // the JS syntax. |
|
5397 |
|
5398 // Conceptually there's just one stack, a stack of pairs (lhs, op). |
|
5399 // It's implemented using two separate arrays, though. |
|
5400 Node nodeStack[PRECEDENCE_CLASSES]; |
|
5401 ParseNodeKind kindStack[PRECEDENCE_CLASSES]; |
|
5402 int depth = 0; |
|
5403 |
|
5404 bool oldParsingForInit = pc->parsingForInit; |
|
5405 pc->parsingForInit = false; |
|
5406 |
|
5407 Node pn; |
|
5408 for (;;) { |
|
5409 pn = unaryExpr(); |
|
5410 if (!pn) |
|
5411 return pn; |
|
5412 |
|
5413 // If a binary operator follows, consume it and compute the |
|
5414 // corresponding operator. |
|
5415 TokenKind tok = tokenStream.getToken(); |
|
5416 if (tok == TOK_ERROR) |
|
5417 return null(); |
|
5418 ParseNodeKind pnk; |
|
5419 if (IsBinaryOpToken(tok, oldParsingForInit)) { |
|
5420 pnk = BinaryOpTokenKindToParseNodeKind(tok); |
|
5421 } else { |
|
5422 tok = TOK_EOF; |
|
5423 pnk = PNK_LIMIT; |
|
5424 } |
|
5425 |
|
5426 // If pnk has precedence less than or equal to another operator on the |
|
5427 // stack, reduce. This combines nodes on the stack until we form the |
|
5428 // actual lhs of pnk. |
|
5429 // |
|
5430 // The >= in this condition works because all the operators in question |
|
5431 // are left-associative; if any were not, the case where two operators |
|
5432 // have equal precedence would need to be handled specially, and the |
|
5433 // stack would need to be a Vector. |
|
5434 while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) { |
|
5435 depth--; |
|
5436 ParseNodeKind combiningPnk = kindStack[depth]; |
|
5437 JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk); |
|
5438 pn = handler.newBinaryOrAppend(combiningPnk, nodeStack[depth], pn, pc, combiningOp); |
|
5439 if (!pn) |
|
5440 return pn; |
|
5441 } |
|
5442 |
|
5443 if (pnk == PNK_LIMIT) |
|
5444 break; |
|
5445 |
|
5446 nodeStack[depth] = pn; |
|
5447 kindStack[depth] = pnk; |
|
5448 depth++; |
|
5449 JS_ASSERT(depth <= PRECEDENCE_CLASSES); |
|
5450 } |
|
5451 |
|
5452 JS_ASSERT(depth == 0); |
|
5453 pc->parsingForInit = oldParsingForInit; |
|
5454 return pn; |
|
5455 } |
|
5456 |
|
5457 template <typename ParseHandler> |
|
5458 MOZ_ALWAYS_INLINE typename ParseHandler::Node |
|
5459 Parser<ParseHandler>::condExpr1() |
|
5460 { |
|
5461 Node condition = orExpr1(); |
|
5462 if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK)) |
|
5463 return condition; |
|
5464 |
|
5465 /* |
|
5466 * Always accept the 'in' operator in the middle clause of a ternary, |
|
5467 * where it's unambiguous, even if we might be parsing the init of a |
|
5468 * for statement. |
|
5469 */ |
|
5470 bool oldParsingForInit = pc->parsingForInit; |
|
5471 pc->parsingForInit = false; |
|
5472 Node thenExpr = assignExpr(); |
|
5473 pc->parsingForInit = oldParsingForInit; |
|
5474 if (!thenExpr) |
|
5475 return null(); |
|
5476 |
|
5477 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND); |
|
5478 |
|
5479 Node elseExpr = assignExpr(); |
|
5480 if (!elseExpr) |
|
5481 return null(); |
|
5482 |
|
5483 tokenStream.getToken(); /* read one token past the end */ |
|
5484 return handler.newConditional(condition, thenExpr, elseExpr); |
|
5485 } |
|
5486 |
|
5487 template <> |
|
5488 bool |
|
5489 Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentFlavor flavor) |
|
5490 { |
|
5491 switch (pn->getKind()) { |
|
5492 case PNK_NAME: |
|
5493 if (!checkStrictAssignment(pn, flavor)) |
|
5494 return false; |
|
5495 if (flavor == KeyedDestructuringAssignment) { |
|
5496 /* |
|
5497 * We may be called on a name node that has already been |
|
5498 * specialized, in the very weird "for (var [x] = i in o) ..." |
|
5499 * case. See bug 558633. |
|
5500 */ |
|
5501 if (!(js_CodeSpec[pn->getOp()].format & JOF_SET)) |
|
5502 pn->setOp(JSOP_SETNAME); |
|
5503 } else { |
|
5504 pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME); |
|
5505 } |
|
5506 pn->markAsAssigned(); |
|
5507 break; |
|
5508 |
|
5509 case PNK_DOT: |
|
5510 case PNK_ELEM: |
|
5511 break; |
|
5512 |
|
5513 case PNK_ARRAY: |
|
5514 case PNK_OBJECT: |
|
5515 if (flavor == CompoundAssignment) { |
|
5516 report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS); |
|
5517 return false; |
|
5518 } |
|
5519 if (!checkDestructuring(nullptr, pn)) |
|
5520 return false; |
|
5521 break; |
|
5522 |
|
5523 case PNK_CALL: |
|
5524 if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS)) |
|
5525 return false; |
|
5526 break; |
|
5527 |
|
5528 default: |
|
5529 report(ParseError, false, pn, JSMSG_BAD_LEFTSIDE_OF_ASS); |
|
5530 return false; |
|
5531 } |
|
5532 return true; |
|
5533 } |
|
5534 |
|
5535 template <> |
|
5536 bool |
|
5537 Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor) |
|
5538 { |
|
5539 /* Full syntax checking of valid assignment LHS terms requires a parse tree. */ |
|
5540 if (pn != SyntaxParseHandler::NodeName && |
|
5541 pn != SyntaxParseHandler::NodeGetProp && |
|
5542 pn != SyntaxParseHandler::NodeLValue) |
|
5543 { |
|
5544 return abortIfSyntaxParser(); |
|
5545 } |
|
5546 return checkStrictAssignment(pn, flavor); |
|
5547 } |
|
5548 |
|
5549 template <typename ParseHandler> |
|
5550 typename ParseHandler::Node |
|
5551 Parser<ParseHandler>::assignExpr() |
|
5552 { |
|
5553 JS_CHECK_RECURSION(context, return null()); |
|
5554 |
|
5555 // It's very common at this point to have a "detectably simple" expression, |
|
5556 // i.e. a name/number/string token followed by one of the following tokens |
|
5557 // that obviously isn't part of an expression: , ; : ) ] } |
|
5558 // |
|
5559 // (In Parsemark this happens 81.4% of the time; in code with large |
|
5560 // numeric arrays, such as some Kraken benchmarks, it happens more often.) |
|
5561 // |
|
5562 // In such cases, we can avoid the full expression parsing route through |
|
5563 // assignExpr(), condExpr1(), orExpr1(), unaryExpr(), memberExpr(), and |
|
5564 // primaryExpr(). |
|
5565 |
|
5566 TokenKind tt = tokenStream.getToken(TokenStream::Operand); |
|
5567 |
|
5568 if (tt == TOK_NAME && tokenStream.nextTokenEndsExpr()) |
|
5569 return identifierName(); |
|
5570 |
|
5571 if (tt == TOK_NUMBER && tokenStream.nextTokenEndsExpr()) |
|
5572 return newNumber(tokenStream.currentToken()); |
|
5573 |
|
5574 if (tt == TOK_STRING && tokenStream.nextTokenEndsExpr()) |
|
5575 return stringLiteral(); |
|
5576 |
|
5577 if (tt == TOK_YIELD && (versionNumber() >= JSVERSION_1_7 || pc->isGenerator())) |
|
5578 return yieldExpression(); |
|
5579 |
|
5580 tokenStream.ungetToken(); |
|
5581 |
|
5582 // Save the tokenizer state in case we find an arrow function and have to |
|
5583 // rewind. |
|
5584 TokenStream::Position start(keepAtoms); |
|
5585 tokenStream.tell(&start); |
|
5586 |
|
5587 Node lhs = condExpr1(); |
|
5588 if (!lhs) |
|
5589 return null(); |
|
5590 |
|
5591 ParseNodeKind kind; |
|
5592 JSOp op; |
|
5593 switch (tokenStream.currentToken().type) { |
|
5594 case TOK_ASSIGN: kind = PNK_ASSIGN; op = JSOP_NOP; break; |
|
5595 case TOK_ADDASSIGN: kind = PNK_ADDASSIGN; op = JSOP_ADD; break; |
|
5596 case TOK_SUBASSIGN: kind = PNK_SUBASSIGN; op = JSOP_SUB; break; |
|
5597 case TOK_BITORASSIGN: kind = PNK_BITORASSIGN; op = JSOP_BITOR; break; |
|
5598 case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; op = JSOP_BITXOR; break; |
|
5599 case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; op = JSOP_BITAND; break; |
|
5600 case TOK_LSHASSIGN: kind = PNK_LSHASSIGN; op = JSOP_LSH; break; |
|
5601 case TOK_RSHASSIGN: kind = PNK_RSHASSIGN; op = JSOP_RSH; break; |
|
5602 case TOK_URSHASSIGN: kind = PNK_URSHASSIGN; op = JSOP_URSH; break; |
|
5603 case TOK_MULASSIGN: kind = PNK_MULASSIGN; op = JSOP_MUL; break; |
|
5604 case TOK_DIVASSIGN: kind = PNK_DIVASSIGN; op = JSOP_DIV; break; |
|
5605 case TOK_MODASSIGN: kind = PNK_MODASSIGN; op = JSOP_MOD; break; |
|
5606 |
|
5607 case TOK_ARROW: { |
|
5608 tokenStream.seek(start); |
|
5609 if (!abortIfSyntaxParser()) |
|
5610 return null(); |
|
5611 |
|
5612 if (tokenStream.getToken() == TOK_ERROR) |
|
5613 return null(); |
|
5614 tokenStream.ungetToken(); |
|
5615 |
|
5616 return functionDef(NullPtr(), start, Normal, Arrow, NotGenerator); |
|
5617 } |
|
5618 |
|
5619 default: |
|
5620 JS_ASSERT(!tokenStream.isCurrentTokenAssignment()); |
|
5621 tokenStream.ungetToken(); |
|
5622 return lhs; |
|
5623 } |
|
5624 |
|
5625 AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment; |
|
5626 if (!checkAndMarkAsAssignmentLhs(lhs, flavor)) |
|
5627 return null(); |
|
5628 |
|
5629 Node rhs = assignExpr(); |
|
5630 if (!rhs) |
|
5631 return null(); |
|
5632 |
|
5633 return handler.newBinaryOrAppend(kind, lhs, rhs, pc, op); |
|
5634 } |
|
5635 |
|
5636 static const char incop_name_str[][10] = {"increment", "decrement"}; |
|
5637 |
|
5638 template <> |
|
5639 bool |
|
5640 Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode *kid, TokenKind tt, bool preorder) |
|
5641 { |
|
5642 // Check. |
|
5643 if (!kid->isKind(PNK_NAME) && |
|
5644 !kid->isKind(PNK_DOT) && |
|
5645 !kid->isKind(PNK_ELEM) && |
|
5646 !(kid->isKind(PNK_CALL) && |
|
5647 (kid->isOp(JSOP_CALL) || kid->isOp(JSOP_SPREADCALL) || |
|
5648 kid->isOp(JSOP_EVAL) || kid->isOp(JSOP_SPREADEVAL) || |
|
5649 kid->isOp(JSOP_FUNCALL) || |
|
5650 kid->isOp(JSOP_FUNAPPLY)))) |
|
5651 { |
|
5652 report(ParseError, false, null(), JSMSG_BAD_OPERAND, incop_name_str[tt == TOK_DEC]); |
|
5653 return false; |
|
5654 } |
|
5655 |
|
5656 if (!checkStrictAssignment(kid, IncDecAssignment)) |
|
5657 return false; |
|
5658 |
|
5659 // Mark. |
|
5660 if (kid->isKind(PNK_NAME)) { |
|
5661 kid->markAsAssigned(); |
|
5662 } else if (kid->isKind(PNK_CALL)) { |
|
5663 if (!makeSetCall(kid, JSMSG_BAD_INCOP_OPERAND)) |
|
5664 return false; |
|
5665 } |
|
5666 return true; |
|
5667 } |
|
5668 |
|
5669 template <> |
|
5670 bool |
|
5671 Parser<SyntaxParseHandler>::checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder) |
|
5672 { |
|
5673 // To the extent of what we support in syntax-parse mode, the rules for |
|
5674 // inc/dec operands are the same as for assignment. There are differences, |
|
5675 // such as destructuring; but if we hit any of those cases, we'll abort and |
|
5676 // reparse in full mode. |
|
5677 return checkAndMarkAsAssignmentLhs(kid, IncDecAssignment); |
|
5678 } |
|
5679 |
|
5680 template <typename ParseHandler> |
|
5681 typename ParseHandler::Node |
|
5682 Parser<ParseHandler>::unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin) |
|
5683 { |
|
5684 Node kid = unaryExpr(); |
|
5685 if (!kid) |
|
5686 return null(); |
|
5687 return handler.newUnary(kind, op, begin, kid); |
|
5688 } |
|
5689 |
|
5690 template <typename ParseHandler> |
|
5691 typename ParseHandler::Node |
|
5692 Parser<ParseHandler>::unaryExpr() |
|
5693 { |
|
5694 Node pn, pn2; |
|
5695 |
|
5696 JS_CHECK_RECURSION(context, return null()); |
|
5697 |
|
5698 TokenKind tt = tokenStream.getToken(TokenStream::Operand); |
|
5699 uint32_t begin = pos().begin; |
|
5700 switch (tt) { |
|
5701 case TOK_TYPEOF: |
|
5702 return unaryOpExpr(PNK_TYPEOF, JSOP_TYPEOF, begin); |
|
5703 case TOK_VOID: |
|
5704 return unaryOpExpr(PNK_VOID, JSOP_VOID, begin); |
|
5705 case TOK_NOT: |
|
5706 return unaryOpExpr(PNK_NOT, JSOP_NOT, begin); |
|
5707 case TOK_BITNOT: |
|
5708 return unaryOpExpr(PNK_BITNOT, JSOP_BITNOT, begin); |
|
5709 case TOK_ADD: |
|
5710 return unaryOpExpr(PNK_POS, JSOP_POS, begin); |
|
5711 case TOK_SUB: |
|
5712 return unaryOpExpr(PNK_NEG, JSOP_NEG, begin); |
|
5713 |
|
5714 case TOK_INC: |
|
5715 case TOK_DEC: |
|
5716 { |
|
5717 TokenKind tt2 = tokenStream.getToken(TokenStream::Operand); |
|
5718 pn2 = memberExpr(tt2, true); |
|
5719 if (!pn2) |
|
5720 return null(); |
|
5721 if (!checkAndMarkAsIncOperand(pn2, tt, true)) |
|
5722 return null(); |
|
5723 return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT, |
|
5724 JSOP_NOP, |
|
5725 begin, |
|
5726 pn2); |
|
5727 } |
|
5728 |
|
5729 case TOK_DELETE: { |
|
5730 Node expr = unaryExpr(); |
|
5731 if (!expr) |
|
5732 return null(); |
|
5733 |
|
5734 // Per spec, deleting any unary expression is valid -- it simply |
|
5735 // returns true -- except for one case that is illegal in strict mode. |
|
5736 if (handler.isName(expr)) { |
|
5737 if (!report(ParseStrictError, pc->sc->strict, expr, JSMSG_DEPRECATED_DELETE_OPERAND)) |
|
5738 return null(); |
|
5739 pc->sc->setBindingsAccessedDynamically(); |
|
5740 } |
|
5741 |
|
5742 return handler.newDelete(begin, expr); |
|
5743 } |
|
5744 |
|
5745 case TOK_ERROR: |
|
5746 return null(); |
|
5747 |
|
5748 default: |
|
5749 pn = memberExpr(tt, true); |
|
5750 if (!pn) |
|
5751 return null(); |
|
5752 |
|
5753 /* Don't look across a newline boundary for a postfix incop. */ |
|
5754 tt = tokenStream.peekTokenSameLine(TokenStream::Operand); |
|
5755 if (tt == TOK_INC || tt == TOK_DEC) { |
|
5756 tokenStream.consumeKnownToken(tt); |
|
5757 if (!checkAndMarkAsIncOperand(pn, tt, false)) |
|
5758 return null(); |
|
5759 return handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT, |
|
5760 JSOP_NOP, |
|
5761 begin, |
|
5762 pn); |
|
5763 } |
|
5764 return pn; |
|
5765 } |
|
5766 MOZ_ASSUME_UNREACHABLE("unaryExpr"); |
|
5767 } |
|
5768 |
|
5769 /* |
|
5770 * A dedicated helper for transplanting the legacy comprehension expression E in |
|
5771 * |
|
5772 * [E for (V in I)] // legacy array comprehension |
|
5773 * (E for (V in I)) // legacy generator expression |
|
5774 * |
|
5775 * from its initial location in the AST, on the left of the 'for', to its final |
|
5776 * position on the right. To avoid a separate pass we do this by adjusting the |
|
5777 * blockids and name binding links that were established when E was parsed. |
|
5778 * |
|
5779 * A legacy generator expression desugars like so: |
|
5780 * |
|
5781 * (E for (V in I)) => (function () { for (var V in I) yield E; })() |
|
5782 * |
|
5783 * so the transplanter must adjust static level as well as blockid. E's source |
|
5784 * coordinates in root->pn_pos are critical to deciding which binding links to |
|
5785 * preserve and which to cut. |
|
5786 * |
|
5787 * NB: This is not a general tree transplanter -- it knows in particular that |
|
5788 * the one or more bindings induced by V have not yet been created. |
|
5789 */ |
|
5790 class LegacyCompExprTransplanter |
|
5791 { |
|
5792 ParseNode *root; |
|
5793 Parser<FullParseHandler> *parser; |
|
5794 ParseContext<FullParseHandler> *outerpc; |
|
5795 GeneratorKind comprehensionKind; |
|
5796 unsigned adjust; |
|
5797 HashSet<Definition *> visitedImplicitArguments; |
|
5798 |
|
5799 public: |
|
5800 LegacyCompExprTransplanter(ParseNode *pn, Parser<FullParseHandler> *parser, |
|
5801 ParseContext<FullParseHandler> *outerpc, |
|
5802 GeneratorKind kind, unsigned adj) |
|
5803 : root(pn), parser(parser), outerpc(outerpc), comprehensionKind(kind), adjust(adj), |
|
5804 visitedImplicitArguments(parser->context) |
|
5805 {} |
|
5806 |
|
5807 bool init() { |
|
5808 return visitedImplicitArguments.init(); |
|
5809 } |
|
5810 |
|
5811 bool transplant(ParseNode *pn); |
|
5812 }; |
|
5813 |
|
5814 /* |
|
5815 * Any definitions nested within the legacy comprehension expression of a |
|
5816 * generator expression must move "down" one static level, which of course |
|
5817 * increases the upvar-frame-skip count. |
|
5818 */ |
|
5819 template <typename ParseHandler> |
|
5820 static bool |
|
5821 BumpStaticLevel(TokenStream &ts, ParseNode *pn, ParseContext<ParseHandler> *pc) |
|
5822 { |
|
5823 if (pn->pn_cookie.isFree()) |
|
5824 return true; |
|
5825 |
|
5826 unsigned level = unsigned(pn->pn_cookie.level()) + 1; |
|
5827 JS_ASSERT(level >= pc->staticLevel); |
|
5828 return pn->pn_cookie.set(ts, level, pn->pn_cookie.slot()); |
|
5829 } |
|
5830 |
|
5831 template <typename ParseHandler> |
|
5832 static bool |
|
5833 AdjustBlockId(TokenStream &ts, ParseNode *pn, unsigned adjust, ParseContext<ParseHandler> *pc) |
|
5834 { |
|
5835 JS_ASSERT(pn->isArity(PN_LIST) || pn->isArity(PN_CODE) || pn->isArity(PN_NAME)); |
|
5836 if (BlockIdLimit - pn->pn_blockid <= adjust + 1) { |
|
5837 ts.reportError(JSMSG_NEED_DIET, "program"); |
|
5838 return false; |
|
5839 } |
|
5840 pn->pn_blockid += adjust; |
|
5841 if (pn->pn_blockid >= pc->blockidGen) |
|
5842 pc->blockidGen = pn->pn_blockid + 1; |
|
5843 return true; |
|
5844 } |
|
5845 |
|
5846 bool |
|
5847 LegacyCompExprTransplanter::transplant(ParseNode *pn) |
|
5848 { |
|
5849 ParseContext<FullParseHandler> *pc = parser->pc; |
|
5850 |
|
5851 bool isGenexp = comprehensionKind != NotGenerator; |
|
5852 |
|
5853 if (!pn) |
|
5854 return true; |
|
5855 |
|
5856 switch (pn->getArity()) { |
|
5857 case PN_LIST: |
|
5858 for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
|
5859 if (!transplant(pn2)) |
|
5860 return false; |
|
5861 } |
|
5862 if (pn->pn_pos >= root->pn_pos) { |
|
5863 if (!AdjustBlockId(parser->tokenStream, pn, adjust, pc)) |
|
5864 return false; |
|
5865 } |
|
5866 break; |
|
5867 |
|
5868 case PN_TERNARY: |
|
5869 if (!transplant(pn->pn_kid1) || |
|
5870 !transplant(pn->pn_kid2) || |
|
5871 !transplant(pn->pn_kid3)) |
|
5872 return false; |
|
5873 break; |
|
5874 |
|
5875 case PN_BINARY: |
|
5876 case PN_BINARY_OBJ: |
|
5877 if (!transplant(pn->pn_left)) |
|
5878 return false; |
|
5879 |
|
5880 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */ |
|
5881 if (pn->pn_right != pn->pn_left) { |
|
5882 if (!transplant(pn->pn_right)) |
|
5883 return false; |
|
5884 } |
|
5885 break; |
|
5886 |
|
5887 case PN_UNARY: |
|
5888 if (!transplant(pn->pn_kid)) |
|
5889 return false; |
|
5890 break; |
|
5891 |
|
5892 case PN_CODE: |
|
5893 case PN_NAME: |
|
5894 if (!transplant(pn->maybeExpr())) |
|
5895 return false; |
|
5896 |
|
5897 if (pn->isDefn()) { |
|
5898 if (isGenexp && !BumpStaticLevel(parser->tokenStream, pn, pc)) |
|
5899 return false; |
|
5900 } else if (pn->isUsed()) { |
|
5901 JS_ASSERT(pn->pn_cookie.isFree()); |
|
5902 |
|
5903 Definition *dn = pn->pn_lexdef; |
|
5904 JS_ASSERT(dn->isDefn()); |
|
5905 |
|
5906 /* |
|
5907 * Adjust the definition's block id only if it is a placeholder not |
|
5908 * to the left of the root node, and if pn is the last use visited |
|
5909 * in the legacy comprehension expression (to avoid adjusting the |
|
5910 * blockid multiple times). |
|
5911 * |
|
5912 * Non-placeholder definitions within the legacy comprehension |
|
5913 * expression will be visited further below. |
|
5914 */ |
|
5915 if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) { |
|
5916 if (isGenexp && !BumpStaticLevel(parser->tokenStream, dn, pc)) |
|
5917 return false; |
|
5918 if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc)) |
|
5919 return false; |
|
5920 } |
|
5921 |
|
5922 RootedAtom atom(parser->context, pn->pn_atom); |
|
5923 #ifdef DEBUG |
|
5924 StmtInfoPC *stmt = LexicalLookup(pc, atom, nullptr, (StmtInfoPC *)nullptr); |
|
5925 JS_ASSERT(!stmt || stmt != pc->topStmt); |
|
5926 #endif |
|
5927 if (isGenexp && !dn->isOp(JSOP_CALLEE)) { |
|
5928 JS_ASSERT(!pc->decls().lookupFirst(atom)); |
|
5929 |
|
5930 if (dn->pn_pos < root->pn_pos) { |
|
5931 /* |
|
5932 * The variable originally appeared to be a use of a |
|
5933 * definition or placeholder outside the generator, but now |
|
5934 * we know it is scoped within the legacy comprehension |
|
5935 * tail's clauses. Make it (along with any other uses within |
|
5936 * the generator) a use of a new placeholder in the |
|
5937 * generator's lexdeps. |
|
5938 */ |
|
5939 Definition *dn2 = parser->handler.newPlaceholder(atom, parser->pc->blockid(), |
|
5940 parser->pos()); |
|
5941 if (!dn2) |
|
5942 return false; |
|
5943 dn2->pn_pos = root->pn_pos; |
|
5944 |
|
5945 /* |
|
5946 * Change all uses of |dn| that lie within the generator's |
|
5947 * |yield| expression into uses of dn2. |
|
5948 */ |
|
5949 ParseNode **pnup = &dn->dn_uses; |
|
5950 ParseNode *pnu; |
|
5951 while ((pnu = *pnup) != nullptr && pnu->pn_pos >= root->pn_pos) { |
|
5952 pnu->pn_lexdef = dn2; |
|
5953 dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS; |
|
5954 pnup = &pnu->pn_link; |
|
5955 } |
|
5956 dn2->dn_uses = dn->dn_uses; |
|
5957 dn->dn_uses = *pnup; |
|
5958 *pnup = nullptr; |
|
5959 DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(dn2); |
|
5960 if (!pc->lexdeps->put(atom, def)) |
|
5961 return false; |
|
5962 if (dn->isClosed()) |
|
5963 dn2->pn_dflags |= PND_CLOSED; |
|
5964 } else if (dn->isPlaceholder()) { |
|
5965 /* |
|
5966 * The variable first occurs free in the 'yield' expression; |
|
5967 * move the existing placeholder node (and all its uses) |
|
5968 * from the parent's lexdeps into the generator's lexdeps. |
|
5969 */ |
|
5970 outerpc->lexdeps->remove(atom); |
|
5971 DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(dn); |
|
5972 if (!pc->lexdeps->put(atom, def)) |
|
5973 return false; |
|
5974 } else if (dn->isImplicitArguments()) { |
|
5975 /* |
|
5976 * Implicit 'arguments' Definition nodes (see |
|
5977 * PND_IMPLICITARGUMENTS in Parser::functionBody) are only |
|
5978 * reachable via the lexdefs of their uses. Unfortunately, |
|
5979 * there may be multiple uses, so we need to maintain a set |
|
5980 * to only bump the definition once. |
|
5981 */ |
|
5982 if (isGenexp && !visitedImplicitArguments.has(dn)) { |
|
5983 if (!BumpStaticLevel(parser->tokenStream, dn, pc)) |
|
5984 return false; |
|
5985 if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc)) |
|
5986 return false; |
|
5987 if (!visitedImplicitArguments.put(dn)) |
|
5988 return false; |
|
5989 } |
|
5990 } |
|
5991 } |
|
5992 } |
|
5993 |
|
5994 if (pn->pn_pos >= root->pn_pos) { |
|
5995 if (!AdjustBlockId(parser->tokenStream, pn, adjust, pc)) |
|
5996 return false; |
|
5997 } |
|
5998 break; |
|
5999 |
|
6000 case PN_NULLARY: |
|
6001 /* Nothing. */ |
|
6002 break; |
|
6003 } |
|
6004 return true; |
|
6005 } |
|
6006 |
|
6007 // Parsing legacy (JS1.7-style) comprehensions is terrible: we parse the head |
|
6008 // expression as if it's part of a comma expression, then when we see the "for" |
|
6009 // we transplant the parsed expression into the inside of a constructed |
|
6010 // for-of/for-in/for-each tail. Transplanting an already-parsed expression is |
|
6011 // tricky, but the LegacyCompExprTransplanter handles most of that. |
|
6012 // |
|
6013 // The one remaining thing to patch up is the block scope depth. We need to |
|
6014 // compute the maximum block scope depth of a function, so we know how much |
|
6015 // space to reserve in the fixed part of a stack frame. Normally this is done |
|
6016 // whenever we leave a statement, via AccumulateBlockScopeDepth. However if the |
|
6017 // head has a let expression, we need to re-assign that depth to the tail of the |
|
6018 // comprehension. |
|
6019 // |
|
6020 // Thing is, we don't actually know what that depth is, because the only |
|
6021 // information we keep is the maximum nested depth within a statement, so we |
|
6022 // just conservatively propagate the maximum nested depth from the top statement |
|
6023 // to the comprehension tail. |
|
6024 // |
|
6025 template <typename ParseHandler> |
|
6026 static unsigned |
|
6027 LegacyComprehensionHeadBlockScopeDepth(ParseContext<ParseHandler> *pc) |
|
6028 { |
|
6029 return pc->topStmt ? pc->topStmt->innerBlockScopeDepth : pc->blockScopeDepth; |
|
6030 } |
|
6031 |
|
6032 /* |
|
6033 * Starting from a |for| keyword after the first array initialiser element or |
|
6034 * an expression in an open parenthesis, parse the tail of the comprehension |
|
6035 * or generator expression signified by this |for| keyword in context. |
|
6036 * |
|
6037 * Return null on failure, else return the top-most parse node for the array |
|
6038 * comprehension or generator expression, with a unary node as the body of the |
|
6039 * (possibly nested) for-loop, initialized by |kind, op, kid|. |
|
6040 */ |
|
6041 template <> |
|
6042 ParseNode * |
|
6043 Parser<FullParseHandler>::legacyComprehensionTail(ParseNode *bodyStmt, unsigned blockid, |
|
6044 GeneratorKind comprehensionKind, |
|
6045 ParseContext<FullParseHandler> *outerpc, |
|
6046 unsigned innerBlockScopeDepth) |
|
6047 { |
|
6048 /* |
|
6049 * If we saw any inner functions while processing the generator expression |
|
6050 * then they may have upvars referring to the let vars in this generator |
|
6051 * which were not correctly processed. Bail out and start over without |
|
6052 * allowing lazy parsing. |
|
6053 */ |
|
6054 if (handler.syntaxParser) { |
|
6055 handler.disableSyntaxParser(); |
|
6056 abortedSyntaxParse = true; |
|
6057 return nullptr; |
|
6058 } |
|
6059 |
|
6060 unsigned adjust; |
|
6061 ParseNode *pn, *pn2, *pn3, **pnp; |
|
6062 StmtInfoPC stmtInfo(context); |
|
6063 BindData<FullParseHandler> data(context); |
|
6064 TokenKind tt; |
|
6065 |
|
6066 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); |
|
6067 |
|
6068 bool isGenexp = comprehensionKind != NotGenerator; |
|
6069 |
|
6070 if (isGenexp) { |
|
6071 JS_ASSERT(comprehensionKind == LegacyGenerator); |
|
6072 /* |
|
6073 * Generator expression desugars to an immediately applied lambda that |
|
6074 * yields the next value from a for-in loop (possibly nested, and with |
|
6075 * optional if guard). Make pn be the TOK_LC body node. |
|
6076 */ |
|
6077 pn = pushLexicalScope(&stmtInfo); |
|
6078 if (!pn) |
|
6079 return null(); |
|
6080 adjust = pn->pn_blockid - blockid; |
|
6081 } else { |
|
6082 /* |
|
6083 * Make a parse-node and literal object representing the block scope of |
|
6084 * this array comprehension. Our caller in primaryExpr, the TOK_LB case |
|
6085 * aka the array initialiser case, has passed the blockid to claim for |
|
6086 * the comprehension's block scope. We allocate that id or one above it |
|
6087 * here, by calling PushLexicalScope. |
|
6088 * |
|
6089 * In the case of a comprehension expression that has nested blocks |
|
6090 * (e.g., let expressions), we will allocate a higher blockid but then |
|
6091 * slide all blocks "to the right" to make room for the comprehension's |
|
6092 * block scope. |
|
6093 */ |
|
6094 adjust = pc->blockid(); |
|
6095 pn = pushLexicalScope(&stmtInfo); |
|
6096 if (!pn) |
|
6097 return null(); |
|
6098 |
|
6099 JS_ASSERT(blockid <= pn->pn_blockid); |
|
6100 JS_ASSERT(blockid < pc->blockidGen); |
|
6101 JS_ASSERT(pc->bodyid < blockid); |
|
6102 pn->pn_blockid = stmtInfo.blockid = blockid; |
|
6103 JS_ASSERT(adjust < blockid); |
|
6104 adjust = blockid - adjust; |
|
6105 } |
|
6106 |
|
6107 handler.setBeginPosition(pn, bodyStmt); |
|
6108 |
|
6109 pnp = &pn->pn_expr; |
|
6110 |
|
6111 LegacyCompExprTransplanter transplanter(bodyStmt, this, outerpc, comprehensionKind, adjust); |
|
6112 if (!transplanter.init()) |
|
6113 return null(); |
|
6114 |
|
6115 if (!transplanter.transplant(bodyStmt)) |
|
6116 return null(); |
|
6117 |
|
6118 JS_ASSERT(pc->staticScope && pc->staticScope == pn->pn_objbox->object); |
|
6119 data.initLet(HoistVars, pc->staticScope->as<StaticBlockObject>(), JSMSG_ARRAY_INIT_TOO_BIG); |
|
6120 |
|
6121 do { |
|
6122 /* |
|
6123 * FOR node is binary, left is loop control and right is body. Use |
|
6124 * index to count each block-local let-variable on the left-hand side |
|
6125 * of the in/of. |
|
6126 */ |
|
6127 pn2 = BinaryNode::create(PNK_FOR, &handler); |
|
6128 if (!pn2) |
|
6129 return null(); |
|
6130 |
|
6131 pn2->setOp(JSOP_ITER); |
|
6132 pn2->pn_iflags = JSITER_ENUMERATE; |
|
6133 if (allowsForEachIn() && tokenStream.matchContextualKeyword(context->names().each)) |
|
6134 pn2->pn_iflags |= JSITER_FOREACH; |
|
6135 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); |
|
6136 |
|
6137 uint32_t startYieldOffset = pc->lastYieldOffset; |
|
6138 |
|
6139 RootedPropertyName name(context); |
|
6140 tt = tokenStream.getToken(); |
|
6141 switch (tt) { |
|
6142 case TOK_LB: |
|
6143 case TOK_LC: |
|
6144 pc->inDeclDestructuring = true; |
|
6145 pn3 = primaryExpr(tt); |
|
6146 pc->inDeclDestructuring = false; |
|
6147 if (!pn3) |
|
6148 return null(); |
|
6149 break; |
|
6150 |
|
6151 case TOK_NAME: |
|
6152 name = tokenStream.currentName(); |
|
6153 |
|
6154 /* |
|
6155 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to |
|
6156 * JSOP_GETLOCAL here, because we don't yet know the block's depth |
|
6157 * in the operand stack frame. The code generator computes that, |
|
6158 * and it tries to bind all names to slots, so we must let it do |
|
6159 * the deed. |
|
6160 */ |
|
6161 pn3 = newBindingNode(name, false); |
|
6162 if (!pn3) |
|
6163 return null(); |
|
6164 break; |
|
6165 |
|
6166 default: |
|
6167 report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME); |
|
6168 |
|
6169 case TOK_ERROR: |
|
6170 return null(); |
|
6171 } |
|
6172 |
|
6173 bool isForOf; |
|
6174 if (!matchInOrOf(&isForOf)) { |
|
6175 report(ParseError, false, null(), JSMSG_IN_AFTER_FOR_NAME); |
|
6176 return null(); |
|
6177 } |
|
6178 ParseNodeKind headKind = PNK_FORIN; |
|
6179 if (isForOf) { |
|
6180 if (pn2->pn_iflags != JSITER_ENUMERATE) { |
|
6181 JS_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE)); |
|
6182 report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP); |
|
6183 return null(); |
|
6184 } |
|
6185 pn2->pn_iflags = 0; |
|
6186 headKind = PNK_FOROF; |
|
6187 } |
|
6188 |
|
6189 ParseNode *pn4 = expr(); |
|
6190 if (!pn4) |
|
6191 return null(); |
|
6192 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); |
|
6193 |
|
6194 if (isGenexp && pc->lastYieldOffset != startYieldOffset) { |
|
6195 reportWithOffset(ParseError, false, pc->lastYieldOffset, |
|
6196 JSMSG_BAD_GENEXP_BODY, js_yield_str); |
|
6197 return null(); |
|
6198 } |
|
6199 |
|
6200 switch (tt) { |
|
6201 case TOK_LB: |
|
6202 case TOK_LC: |
|
6203 if (!checkDestructuring(&data, pn3)) |
|
6204 return null(); |
|
6205 |
|
6206 if (versionNumber() == JSVERSION_1_7 && |
|
6207 !(pn2->pn_iflags & JSITER_FOREACH) && |
|
6208 !isForOf) |
|
6209 { |
|
6210 /* Destructuring requires [key, value] enumeration in JS1.7. */ |
|
6211 if (!pn3->isKind(PNK_ARRAY) || pn3->pn_count != 2) { |
|
6212 report(ParseError, false, null(), JSMSG_BAD_FOR_LEFTSIDE); |
|
6213 return null(); |
|
6214 } |
|
6215 |
|
6216 JS_ASSERT(pn2->isOp(JSOP_ITER)); |
|
6217 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE); |
|
6218 JS_ASSERT(headKind == PNK_FORIN); |
|
6219 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE; |
|
6220 } |
|
6221 break; |
|
6222 |
|
6223 case TOK_NAME: |
|
6224 data.pn = pn3; |
|
6225 if (!data.binder(&data, name, this)) |
|
6226 return null(); |
|
6227 break; |
|
6228 |
|
6229 default:; |
|
6230 } |
|
6231 |
|
6232 /* |
|
6233 * Synthesize a declaration. Every definition must appear in the parse |
|
6234 * tree in order for ComprehensionTranslator to work. |
|
6235 */ |
|
6236 ParseNode *vars = ListNode::create(PNK_VAR, &handler); |
|
6237 if (!vars) |
|
6238 return null(); |
|
6239 vars->setOp(JSOP_NOP); |
|
6240 vars->pn_pos = pn3->pn_pos; |
|
6241 vars->makeEmpty(); |
|
6242 vars->append(pn3); |
|
6243 |
|
6244 /* Definitions can't be passed directly to EmitAssignment as lhs. */ |
|
6245 pn3 = cloneLeftHandSide(pn3); |
|
6246 if (!pn3) |
|
6247 return null(); |
|
6248 |
|
6249 pn2->pn_left = handler.newTernary(headKind, vars, pn3, pn4); |
|
6250 if (!pn2->pn_left) |
|
6251 return null(); |
|
6252 *pnp = pn2; |
|
6253 pnp = &pn2->pn_right; |
|
6254 } while (tokenStream.matchToken(TOK_FOR)); |
|
6255 |
|
6256 if (tokenStream.matchToken(TOK_IF)) { |
|
6257 pn2 = TernaryNode::create(PNK_IF, &handler); |
|
6258 if (!pn2) |
|
6259 return null(); |
|
6260 pn2->pn_kid1 = condition(); |
|
6261 if (!pn2->pn_kid1) |
|
6262 return null(); |
|
6263 *pnp = pn2; |
|
6264 pnp = &pn2->pn_kid2; |
|
6265 } |
|
6266 |
|
6267 *pnp = bodyStmt; |
|
6268 |
|
6269 pc->topStmt->innerBlockScopeDepth += innerBlockScopeDepth; |
|
6270 PopStatementPC(tokenStream, pc); |
|
6271 |
|
6272 handler.setEndPosition(pn, pos().end); |
|
6273 |
|
6274 return pn; |
|
6275 } |
|
6276 |
|
6277 template <> |
|
6278 SyntaxParseHandler::Node |
|
6279 Parser<SyntaxParseHandler>::legacyComprehensionTail(SyntaxParseHandler::Node bodyStmt, |
|
6280 unsigned blockid, |
|
6281 GeneratorKind comprehensionKind, |
|
6282 ParseContext<SyntaxParseHandler> *outerpc, |
|
6283 unsigned innerBlockScopeDepth) |
|
6284 { |
|
6285 abortIfSyntaxParser(); |
|
6286 return null(); |
|
6287 } |
|
6288 |
|
6289 template <> |
|
6290 ParseNode* |
|
6291 Parser<FullParseHandler>::legacyArrayComprehension(ParseNode *array) |
|
6292 { |
|
6293 array->setKind(PNK_ARRAYCOMP); |
|
6294 |
|
6295 // Remove the single element from array's linked list, leaving us with an |
|
6296 // empty array literal and a comprehension expression. |
|
6297 JS_ASSERT(array->pn_count == 1); |
|
6298 ParseNode *bodyExpr = array->last(); |
|
6299 array->pn_count = 0; |
|
6300 array->pn_tail = &array->pn_head; |
|
6301 *array->pn_tail = nullptr; |
|
6302 |
|
6303 ParseNode *arrayPush = handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, |
|
6304 bodyExpr->pn_pos.begin, bodyExpr); |
|
6305 if (!arrayPush) |
|
6306 return null(); |
|
6307 |
|
6308 ParseNode *comp = legacyComprehensionTail(arrayPush, array->pn_blockid, NotGenerator, |
|
6309 nullptr, LegacyComprehensionHeadBlockScopeDepth(pc)); |
|
6310 if (!comp) |
|
6311 return null(); |
|
6312 |
|
6313 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION); |
|
6314 |
|
6315 TokenPos p = handler.getPosition(array); |
|
6316 p.end = pos().end; |
|
6317 return handler.newArrayComprehension(comp, array->pn_blockid, p); |
|
6318 } |
|
6319 |
|
6320 template <> |
|
6321 SyntaxParseHandler::Node |
|
6322 Parser<SyntaxParseHandler>::legacyArrayComprehension(Node array) |
|
6323 { |
|
6324 abortIfSyntaxParser(); |
|
6325 return null(); |
|
6326 } |
|
6327 |
|
6328 template <typename ParseHandler> |
|
6329 typename ParseHandler::Node |
|
6330 Parser<ParseHandler>::generatorComprehensionLambda(GeneratorKind comprehensionKind, |
|
6331 unsigned begin, Node innerStmt) |
|
6332 { |
|
6333 JS_ASSERT(comprehensionKind == LegacyGenerator || comprehensionKind == StarGenerator); |
|
6334 JS_ASSERT(!!innerStmt == (comprehensionKind == LegacyGenerator)); |
|
6335 |
|
6336 Node genfn = handler.newFunctionDefinition(); |
|
6337 if (!genfn) |
|
6338 return null(); |
|
6339 handler.setOp(genfn, JSOP_LAMBDA); |
|
6340 |
|
6341 ParseContext<ParseHandler> *outerpc = pc; |
|
6342 |
|
6343 // If we are off the main thread, the generator meta-objects have |
|
6344 // already been created by js::StartOffThreadParseScript, so cx will not |
|
6345 // be necessary. |
|
6346 RootedObject proto(context); |
|
6347 if (comprehensionKind == StarGenerator) { |
|
6348 JSContext *cx = context->maybeJSContext(); |
|
6349 proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global()); |
|
6350 if (!proto) |
|
6351 return null(); |
|
6352 } |
|
6353 |
|
6354 RootedFunction fun(context, newFunction(outerpc, /* atom = */ NullPtr(), Expression, proto)); |
|
6355 if (!fun) |
|
6356 return null(); |
|
6357 |
|
6358 // Create box for fun->object early to root it. |
|
6359 Directives directives(/* strict = */ outerpc->sc->strict); |
|
6360 FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives, comprehensionKind); |
|
6361 if (!genFunbox) |
|
6362 return null(); |
|
6363 |
|
6364 ParseContext<ParseHandler> genpc(this, outerpc, genfn, genFunbox, |
|
6365 /* newDirectives = */ nullptr, |
|
6366 outerpc->staticLevel + 1, outerpc->blockidGen, |
|
6367 /* blockScopeDepth = */ 0); |
|
6368 if (!genpc.init(tokenStream)) |
|
6369 return null(); |
|
6370 |
|
6371 /* |
|
6372 * We assume conservatively that any deoptimization flags in pc->sc |
|
6373 * come from the kid. So we propagate these flags into genfn. For code |
|
6374 * simplicity we also do not detect if the flags were only set in the |
|
6375 * kid and could be removed from pc->sc. |
|
6376 */ |
|
6377 genFunbox->anyCxFlags = outerpc->sc->anyCxFlags; |
|
6378 if (outerpc->sc->isFunctionBox()) |
|
6379 genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags; |
|
6380 |
|
6381 JS_ASSERT(genFunbox->generatorKind() == comprehensionKind); |
|
6382 genFunbox->inGenexpLambda = true; |
|
6383 handler.setBlockId(genfn, genpc.bodyid); |
|
6384 |
|
6385 Node body; |
|
6386 |
|
6387 if (comprehensionKind == StarGenerator) { |
|
6388 body = comprehension(StarGenerator); |
|
6389 if (!body) |
|
6390 return null(); |
|
6391 } else { |
|
6392 JS_ASSERT(comprehensionKind == LegacyGenerator); |
|
6393 body = legacyComprehensionTail(innerStmt, outerpc->blockid(), LegacyGenerator, |
|
6394 outerpc, LegacyComprehensionHeadBlockScopeDepth(outerpc)); |
|
6395 if (!body) |
|
6396 return null(); |
|
6397 } |
|
6398 |
|
6399 if (comprehensionKind == StarGenerator) |
|
6400 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); |
|
6401 |
|
6402 handler.setBeginPosition(body, begin); |
|
6403 handler.setEndPosition(body, pos().end); |
|
6404 |
|
6405 handler.setBeginPosition(genfn, begin); |
|
6406 handler.setEndPosition(genfn, pos().end); |
|
6407 |
|
6408 // Note that if we ever start syntax-parsing generators, we will also |
|
6409 // need to propagate the closed-over variable set to the inner |
|
6410 // lazyscript, as in finishFunctionDefinition. |
|
6411 handler.setFunctionBody(genfn, body); |
|
6412 |
|
6413 PropagateTransitiveParseFlags(genFunbox, outerpc->sc); |
|
6414 |
|
6415 if (!leaveFunction(genfn, outerpc)) |
|
6416 return null(); |
|
6417 |
|
6418 return genfn; |
|
6419 } |
|
6420 |
|
6421 #if JS_HAS_GENERATOR_EXPRS |
|
6422 |
|
6423 /* |
|
6424 * Starting from a |for| keyword after an expression, parse the comprehension |
|
6425 * tail completing this generator expression. Wrap the expression at kid in a |
|
6426 * generator function that is immediately called to evaluate to the generator |
|
6427 * iterator that is the value of this legacy generator expression. |
|
6428 * |
|
6429 * |kid| must be the expression before the |for| keyword; we return an |
|
6430 * application of a generator function that includes the |for| loops and |
|
6431 * |if| guards, with |kid| as the operand of a |yield| expression as the |
|
6432 * innermost loop body. |
|
6433 * |
|
6434 * Note how unlike Python, we do not evaluate the expression to the right of |
|
6435 * the first |in| in the chain of |for| heads. Instead, a generator expression |
|
6436 * is merely sugar for a generator function expression and its application. |
|
6437 */ |
|
6438 template <> |
|
6439 ParseNode * |
|
6440 Parser<FullParseHandler>::legacyGeneratorExpr(ParseNode *expr) |
|
6441 { |
|
6442 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); |
|
6443 |
|
6444 /* Create a |yield| node for |kid|. */ |
|
6445 ParseNode *yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, expr->pn_pos.begin, expr); |
|
6446 if (!yieldExpr) |
|
6447 return null(); |
|
6448 yieldExpr->setInParens(true); |
|
6449 |
|
6450 // A statement to wrap the yield expression. |
|
6451 ParseNode *yieldStmt = handler.newExprStatement(yieldExpr, expr->pn_pos.end); |
|
6452 if (!yieldStmt) |
|
6453 return null(); |
|
6454 |
|
6455 /* Make a new node for the desugared generator function. */ |
|
6456 ParseNode *genfn = generatorComprehensionLambda(LegacyGenerator, expr->pn_pos.begin, yieldStmt); |
|
6457 if (!genfn) |
|
6458 return null(); |
|
6459 |
|
6460 /* |
|
6461 * Our result is a call expression that invokes the anonymous generator |
|
6462 * function object. |
|
6463 */ |
|
6464 ParseNode *result = ListNode::create(PNK_GENEXP, &handler); |
|
6465 if (!result) |
|
6466 return null(); |
|
6467 result->setOp(JSOP_CALL); |
|
6468 result->pn_pos.begin = genfn->pn_pos.begin; |
|
6469 result->initList(genfn); |
|
6470 return result; |
|
6471 } |
|
6472 |
|
6473 template <> |
|
6474 SyntaxParseHandler::Node |
|
6475 Parser<SyntaxParseHandler>::legacyGeneratorExpr(Node kid) |
|
6476 { |
|
6477 JS_ALWAYS_FALSE(abortIfSyntaxParser()); |
|
6478 return SyntaxParseHandler::NodeFailure; |
|
6479 } |
|
6480 |
|
6481 static const char js_generator_str[] = "generator"; |
|
6482 |
|
6483 #endif /* JS_HAS_GENERATOR_EXPRS */ |
|
6484 |
|
6485 template <typename ParseHandler> |
|
6486 typename ParseHandler::Node |
|
6487 Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind) |
|
6488 { |
|
6489 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); |
|
6490 |
|
6491 uint32_t begin = pos().begin; |
|
6492 |
|
6493 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); |
|
6494 |
|
6495 // FIXME: Destructuring binding (bug 980828). |
|
6496 |
|
6497 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME); |
|
6498 RootedPropertyName name(context, tokenStream.currentName()); |
|
6499 if (name == context->names().let) { |
|
6500 report(ParseError, false, null(), JSMSG_LET_COMP_BINDING); |
|
6501 return null(); |
|
6502 } |
|
6503 if (!tokenStream.matchContextualKeyword(context->names().of)) { |
|
6504 report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME); |
|
6505 return null(); |
|
6506 } |
|
6507 |
|
6508 Node rhs = assignExpr(); |
|
6509 if (!rhs) |
|
6510 return null(); |
|
6511 |
|
6512 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE); |
|
6513 |
|
6514 TokenPos headPos(begin, pos().end); |
|
6515 |
|
6516 StmtInfoPC stmtInfo(context); |
|
6517 BindData<ParseHandler> data(context); |
|
6518 RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context)); |
|
6519 if (!blockObj) |
|
6520 return null(); |
|
6521 data.initLet(DontHoistVars, *blockObj, JSMSG_TOO_MANY_LOCALS); |
|
6522 Node lhs = newName(name); |
|
6523 if (!lhs) |
|
6524 return null(); |
|
6525 Node decls = handler.newList(PNK_LET, lhs, JSOP_NOP); |
|
6526 if (!decls) |
|
6527 return null(); |
|
6528 data.pn = lhs; |
|
6529 if (!data.binder(&data, name, this)) |
|
6530 return null(); |
|
6531 Node letScope = pushLetScope(blockObj, &stmtInfo); |
|
6532 if (!letScope) |
|
6533 return null(); |
|
6534 handler.setLexicalScopeBody(letScope, decls); |
|
6535 |
|
6536 Node assignLhs = newName(name); |
|
6537 if (!assignLhs) |
|
6538 return null(); |
|
6539 if (!noteNameUse(name, assignLhs)) |
|
6540 return null(); |
|
6541 handler.setOp(assignLhs, JSOP_SETNAME); |
|
6542 |
|
6543 Node head = handler.newForHead(PNK_FOROF, letScope, assignLhs, rhs, headPos); |
|
6544 if (!head) |
|
6545 return null(); |
|
6546 |
|
6547 Node tail = comprehensionTail(comprehensionKind); |
|
6548 if (!tail) |
|
6549 return null(); |
|
6550 |
|
6551 PopStatementPC(tokenStream, pc); |
|
6552 |
|
6553 return handler.newForStatement(begin, head, tail, JSOP_ITER); |
|
6554 } |
|
6555 |
|
6556 template <typename ParseHandler> |
|
6557 typename ParseHandler::Node |
|
6558 Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind) |
|
6559 { |
|
6560 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_IF)); |
|
6561 |
|
6562 uint32_t begin = pos().begin; |
|
6563 |
|
6564 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND); |
|
6565 Node cond = assignExpr(); |
|
6566 if (!cond) |
|
6567 return null(); |
|
6568 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); |
|
6569 |
|
6570 /* Check for (a = b) and warn about possible (a == b) mistype. */ |
|
6571 if (handler.isOperationWithoutParens(cond, PNK_ASSIGN) && |
|
6572 !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) |
|
6573 { |
|
6574 return null(); |
|
6575 } |
|
6576 |
|
6577 Node then = comprehensionTail(comprehensionKind); |
|
6578 if (!then) |
|
6579 return null(); |
|
6580 |
|
6581 return handler.newIfStatement(begin, cond, then, null()); |
|
6582 } |
|
6583 |
|
6584 template <typename ParseHandler> |
|
6585 typename ParseHandler::Node |
|
6586 Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind) |
|
6587 { |
|
6588 JS_CHECK_RECURSION(context, return null()); |
|
6589 |
|
6590 if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand)) |
|
6591 return comprehensionFor(comprehensionKind); |
|
6592 |
|
6593 if (tokenStream.matchToken(TOK_IF, TokenStream::Operand)) |
|
6594 return comprehensionIf(comprehensionKind); |
|
6595 |
|
6596 uint32_t begin = pos().begin; |
|
6597 |
|
6598 Node bodyExpr = assignExpr(); |
|
6599 if (!bodyExpr) |
|
6600 return null(); |
|
6601 |
|
6602 if (comprehensionKind == NotGenerator) |
|
6603 return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr); |
|
6604 |
|
6605 JS_ASSERT(comprehensionKind == StarGenerator); |
|
6606 Node yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, begin, bodyExpr); |
|
6607 if (!yieldExpr) |
|
6608 return null(); |
|
6609 handler.setInParens(yieldExpr); |
|
6610 |
|
6611 return handler.newExprStatement(yieldExpr, pos().end); |
|
6612 } |
|
6613 |
|
6614 // Parse an ES6 generator or array comprehension, starting at the first 'for'. |
|
6615 // The caller is responsible for matching the ending TOK_RP or TOK_RB. |
|
6616 template <typename ParseHandler> |
|
6617 typename ParseHandler::Node |
|
6618 Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind) |
|
6619 { |
|
6620 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); |
|
6621 |
|
6622 uint32_t startYieldOffset = pc->lastYieldOffset; |
|
6623 |
|
6624 Node body = comprehensionFor(comprehensionKind); |
|
6625 if (!body) |
|
6626 return null(); |
|
6627 |
|
6628 if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) { |
|
6629 reportWithOffset(ParseError, false, pc->lastYieldOffset, |
|
6630 JSMSG_BAD_GENEXP_BODY, js_yield_str); |
|
6631 return null(); |
|
6632 } |
|
6633 |
|
6634 return body; |
|
6635 } |
|
6636 |
|
6637 template <typename ParseHandler> |
|
6638 typename ParseHandler::Node |
|
6639 Parser<ParseHandler>::arrayComprehension(uint32_t begin) |
|
6640 { |
|
6641 Node inner = comprehension(NotGenerator); |
|
6642 if (!inner) |
|
6643 return null(); |
|
6644 |
|
6645 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION); |
|
6646 |
|
6647 Node comp = handler.newList(PNK_ARRAYCOMP, inner); |
|
6648 if (!comp) |
|
6649 return null(); |
|
6650 |
|
6651 handler.setBeginPosition(comp, begin); |
|
6652 handler.setEndPosition(comp, pos().end); |
|
6653 |
|
6654 return comp; |
|
6655 } |
|
6656 |
|
6657 template <typename ParseHandler> |
|
6658 typename ParseHandler::Node |
|
6659 Parser<ParseHandler>::generatorComprehension(uint32_t begin) |
|
6660 { |
|
6661 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); |
|
6662 |
|
6663 // We have no problem parsing generator comprehensions inside lazy |
|
6664 // functions, but the bytecode emitter currently can't handle them that way, |
|
6665 // because when it goes to emit the code for the inner generator function, |
|
6666 // it expects outer functions to have non-lazy scripts. |
|
6667 if (!abortIfSyntaxParser()) |
|
6668 return null(); |
|
6669 |
|
6670 Node genfn = generatorComprehensionLambda(StarGenerator, begin, null()); |
|
6671 if (!genfn) |
|
6672 return null(); |
|
6673 |
|
6674 Node result = handler.newList(PNK_GENEXP, genfn, JSOP_CALL); |
|
6675 if (!result) |
|
6676 return null(); |
|
6677 handler.setBeginPosition(result, begin); |
|
6678 handler.setEndPosition(result, pos().end); |
|
6679 |
|
6680 return result; |
|
6681 } |
|
6682 |
|
6683 template <typename ParseHandler> |
|
6684 typename ParseHandler::Node |
|
6685 Parser<ParseHandler>::assignExprWithoutYield(unsigned msg) |
|
6686 { |
|
6687 uint32_t startYieldOffset = pc->lastYieldOffset; |
|
6688 Node res = assignExpr(); |
|
6689 if (res && pc->lastYieldOffset != startYieldOffset) { |
|
6690 reportWithOffset(ParseError, false, pc->lastYieldOffset, |
|
6691 msg, js_yield_str); |
|
6692 return null(); |
|
6693 } |
|
6694 return res; |
|
6695 } |
|
6696 |
|
6697 template <typename ParseHandler> |
|
6698 bool |
|
6699 Parser<ParseHandler>::argumentList(Node listNode, bool *isSpread) |
|
6700 { |
|
6701 if (tokenStream.matchToken(TOK_RP, TokenStream::Operand)) { |
|
6702 handler.setEndPosition(listNode, pos().end); |
|
6703 return true; |
|
6704 } |
|
6705 |
|
6706 uint32_t startYieldOffset = pc->lastYieldOffset; |
|
6707 bool arg0 = true; |
|
6708 |
|
6709 do { |
|
6710 bool spread = false; |
|
6711 uint32_t begin = 0; |
|
6712 if (tokenStream.matchToken(TOK_TRIPLEDOT, TokenStream::Operand)) { |
|
6713 spread = true; |
|
6714 begin = pos().begin; |
|
6715 *isSpread = true; |
|
6716 } |
|
6717 |
|
6718 Node argNode = assignExpr(); |
|
6719 if (!argNode) |
|
6720 return false; |
|
6721 if (spread) { |
|
6722 argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode); |
|
6723 if (!argNode) |
|
6724 return null(); |
|
6725 } |
|
6726 |
|
6727 if (handler.isOperationWithoutParens(argNode, PNK_YIELD) && |
|
6728 tokenStream.peekToken() == TOK_COMMA) { |
|
6729 report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str); |
|
6730 return false; |
|
6731 } |
|
6732 #if JS_HAS_GENERATOR_EXPRS |
|
6733 if (!spread && tokenStream.matchToken(TOK_FOR)) { |
|
6734 if (pc->lastYieldOffset != startYieldOffset) { |
|
6735 reportWithOffset(ParseError, false, pc->lastYieldOffset, |
|
6736 JSMSG_BAD_GENEXP_BODY, js_yield_str); |
|
6737 return false; |
|
6738 } |
|
6739 argNode = legacyGeneratorExpr(argNode); |
|
6740 if (!argNode) |
|
6741 return false; |
|
6742 if (!arg0 || tokenStream.peekToken() == TOK_COMMA) { |
|
6743 report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str); |
|
6744 return false; |
|
6745 } |
|
6746 } |
|
6747 #endif |
|
6748 arg0 = false; |
|
6749 |
|
6750 handler.addList(listNode, argNode); |
|
6751 } while (tokenStream.matchToken(TOK_COMMA)); |
|
6752 |
|
6753 if (tokenStream.getToken() != TOK_RP) { |
|
6754 report(ParseError, false, null(), JSMSG_PAREN_AFTER_ARGS); |
|
6755 return false; |
|
6756 } |
|
6757 handler.setEndPosition(listNode, pos().end); |
|
6758 return true; |
|
6759 } |
|
6760 |
|
6761 template <typename ParseHandler> |
|
6762 typename ParseHandler::Node |
|
6763 Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax) |
|
6764 { |
|
6765 JS_ASSERT(tokenStream.isCurrentTokenType(tt)); |
|
6766 |
|
6767 Node lhs; |
|
6768 |
|
6769 JS_CHECK_RECURSION(context, return null()); |
|
6770 |
|
6771 /* Check for new expression first. */ |
|
6772 if (tt == TOK_NEW) { |
|
6773 lhs = handler.newList(PNK_NEW, null(), JSOP_NEW); |
|
6774 if (!lhs) |
|
6775 return null(); |
|
6776 |
|
6777 tt = tokenStream.getToken(TokenStream::Operand); |
|
6778 Node ctorExpr = memberExpr(tt, false); |
|
6779 if (!ctorExpr) |
|
6780 return null(); |
|
6781 |
|
6782 handler.addList(lhs, ctorExpr); |
|
6783 |
|
6784 if (tokenStream.matchToken(TOK_LP)) { |
|
6785 bool isSpread = false; |
|
6786 if (!argumentList(lhs, &isSpread)) |
|
6787 return null(); |
|
6788 if (isSpread) |
|
6789 handler.setOp(lhs, JSOP_SPREADNEW); |
|
6790 } |
|
6791 } else { |
|
6792 lhs = primaryExpr(tt); |
|
6793 if (!lhs) |
|
6794 return null(); |
|
6795 } |
|
6796 |
|
6797 while ((tt = tokenStream.getToken()) > TOK_EOF) { |
|
6798 Node nextMember; |
|
6799 if (tt == TOK_DOT) { |
|
6800 tt = tokenStream.getToken(TokenStream::KeywordIsName); |
|
6801 if (tt == TOK_ERROR) |
|
6802 return null(); |
|
6803 if (tt == TOK_NAME) { |
|
6804 PropertyName *field = tokenStream.currentName(); |
|
6805 nextMember = handler.newPropertyAccess(lhs, field, pos().end); |
|
6806 if (!nextMember) |
|
6807 return null(); |
|
6808 } else { |
|
6809 report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT); |
|
6810 return null(); |
|
6811 } |
|
6812 } else if (tt == TOK_LB) { |
|
6813 Node propExpr = expr(); |
|
6814 if (!propExpr) |
|
6815 return null(); |
|
6816 |
|
6817 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); |
|
6818 |
|
6819 nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end); |
|
6820 if (!nextMember) |
|
6821 return null(); |
|
6822 } else if (allowCallSyntax && tt == TOK_LP) { |
|
6823 JSOp op = JSOP_CALL; |
|
6824 nextMember = handler.newList(PNK_CALL, null(), JSOP_CALL); |
|
6825 if (!nextMember) |
|
6826 return null(); |
|
6827 |
|
6828 if (JSAtom *atom = handler.isName(lhs)) { |
|
6829 if (atom == context->names().eval) { |
|
6830 /* Select JSOP_EVAL and flag pc as heavyweight. */ |
|
6831 op = JSOP_EVAL; |
|
6832 pc->sc->setBindingsAccessedDynamically(); |
|
6833 |
|
6834 /* |
|
6835 * In non-strict mode code, direct calls to eval can add |
|
6836 * variables to the call object. |
|
6837 */ |
|
6838 if (pc->sc->isFunctionBox() && !pc->sc->strict) |
|
6839 pc->sc->asFunctionBox()->setHasExtensibleScope(); |
|
6840 } |
|
6841 } else if (JSAtom *atom = handler.isGetProp(lhs)) { |
|
6842 /* Select JSOP_FUNAPPLY given foo.apply(...). */ |
|
6843 if (atom == context->names().apply) { |
|
6844 op = JSOP_FUNAPPLY; |
|
6845 if (pc->sc->isFunctionBox()) |
|
6846 pc->sc->asFunctionBox()->usesApply = true; |
|
6847 } else if (atom == context->names().call) { |
|
6848 op = JSOP_FUNCALL; |
|
6849 } |
|
6850 } |
|
6851 |
|
6852 handler.setBeginPosition(nextMember, lhs); |
|
6853 handler.addList(nextMember, lhs); |
|
6854 |
|
6855 bool isSpread = false; |
|
6856 if (!argumentList(nextMember, &isSpread)) |
|
6857 return null(); |
|
6858 if (isSpread) |
|
6859 op = (op == JSOP_EVAL ? JSOP_SPREADEVAL : JSOP_SPREADCALL); |
|
6860 handler.setOp(nextMember, op); |
|
6861 } else { |
|
6862 tokenStream.ungetToken(); |
|
6863 return lhs; |
|
6864 } |
|
6865 |
|
6866 lhs = nextMember; |
|
6867 } |
|
6868 if (tt == TOK_ERROR) |
|
6869 return null(); |
|
6870 return lhs; |
|
6871 } |
|
6872 |
|
6873 template <typename ParseHandler> |
|
6874 typename ParseHandler::Node |
|
6875 Parser<ParseHandler>::newName(PropertyName *name) |
|
6876 { |
|
6877 return handler.newName(name, pc->blockid(), pos()); |
|
6878 } |
|
6879 |
|
6880 template <typename ParseHandler> |
|
6881 typename ParseHandler::Node |
|
6882 Parser<ParseHandler>::identifierName() |
|
6883 { |
|
6884 RootedPropertyName name(context, tokenStream.currentName()); |
|
6885 Node pn = newName(name); |
|
6886 if (!pn) |
|
6887 return null(); |
|
6888 |
|
6889 if (!pc->inDeclDestructuring && !noteNameUse(name, pn)) |
|
6890 return null(); |
|
6891 |
|
6892 return pn; |
|
6893 } |
|
6894 |
|
6895 template <typename ParseHandler> |
|
6896 typename ParseHandler::Node |
|
6897 Parser<ParseHandler>::stringLiteral() |
|
6898 { |
|
6899 JSAtom *atom = tokenStream.currentToken().atom(); |
|
6900 |
|
6901 // Large strings are fast to parse but slow to compress. Stop compression on |
|
6902 // them, so we don't wait for a long time for compression to finish at the |
|
6903 // end of compilation. |
|
6904 const size_t HUGE_STRING = 50000; |
|
6905 if (sct && sct->active() && atom->length() >= HUGE_STRING) |
|
6906 sct->abort(); |
|
6907 |
|
6908 return handler.newStringLiteral(atom, pos()); |
|
6909 } |
|
6910 |
|
6911 template <typename ParseHandler> |
|
6912 typename ParseHandler::Node |
|
6913 Parser<ParseHandler>::newRegExp() |
|
6914 { |
|
6915 // Create the regexp even when doing a syntax parse, to check the regexp's syntax. |
|
6916 const jschar *chars = tokenStream.getTokenbuf().begin(); |
|
6917 size_t length = tokenStream.getTokenbuf().length(); |
|
6918 RegExpFlag flags = tokenStream.currentToken().regExpFlags(); |
|
6919 |
|
6920 Rooted<RegExpObject*> reobj(context); |
|
6921 if (RegExpStatics *res = context->global()->getRegExpStatics()) |
|
6922 reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream); |
|
6923 else |
|
6924 reobj = RegExpObject::createNoStatics(context, chars, length, flags, &tokenStream); |
|
6925 |
|
6926 if (!reobj) |
|
6927 return null(); |
|
6928 |
|
6929 return handler.newRegExp(reobj, pos(), *this); |
|
6930 } |
|
6931 |
|
6932 template <typename ParseHandler> |
|
6933 typename ParseHandler::Node |
|
6934 Parser<ParseHandler>::arrayInitializer() |
|
6935 { |
|
6936 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LB)); |
|
6937 |
|
6938 uint32_t begin = pos().begin; |
|
6939 Node literal = handler.newArrayLiteral(begin, pc->blockidGen); |
|
6940 if (!literal) |
|
6941 return null(); |
|
6942 |
|
6943 if (tokenStream.matchToken(TOK_RB, TokenStream::Operand)) { |
|
6944 /* |
|
6945 * Mark empty arrays as non-constant, since we cannot easily |
|
6946 * determine their type. |
|
6947 */ |
|
6948 handler.setListFlag(literal, PNX_NONCONST); |
|
6949 } else if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand)) { |
|
6950 // ES6 array comprehension. |
|
6951 return arrayComprehension(begin); |
|
6952 } else { |
|
6953 bool spread = false, missingTrailingComma = false; |
|
6954 uint32_t index = 0; |
|
6955 for (; ; index++) { |
|
6956 if (index == JSObject::NELEMENTS_LIMIT) { |
|
6957 report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG); |
|
6958 return null(); |
|
6959 } |
|
6960 |
|
6961 TokenKind tt = tokenStream.peekToken(TokenStream::Operand); |
|
6962 if (tt == TOK_RB) |
|
6963 break; |
|
6964 |
|
6965 if (tt == TOK_COMMA) { |
|
6966 tokenStream.consumeKnownToken(TOK_COMMA); |
|
6967 if (!handler.addElision(literal, pos())) |
|
6968 return null(); |
|
6969 } else if (tt == TOK_TRIPLEDOT) { |
|
6970 spread = true; |
|
6971 tokenStream.consumeKnownToken(TOK_TRIPLEDOT); |
|
6972 uint32_t begin = pos().begin; |
|
6973 Node inner = assignExpr(); |
|
6974 if (!inner) |
|
6975 return null(); |
|
6976 if (!handler.addSpreadElement(literal, begin, inner)) |
|
6977 return null(); |
|
6978 } else { |
|
6979 Node element = assignExpr(); |
|
6980 if (!element) |
|
6981 return null(); |
|
6982 if (foldConstants && !FoldConstants(context, &element, this)) |
|
6983 return null(); |
|
6984 if (!handler.addArrayElement(literal, element)) |
|
6985 return null(); |
|
6986 } |
|
6987 |
|
6988 if (tt != TOK_COMMA) { |
|
6989 /* If we didn't already match TOK_COMMA in above case. */ |
|
6990 if (!tokenStream.matchToken(TOK_COMMA)) { |
|
6991 missingTrailingComma = true; |
|
6992 break; |
|
6993 } |
|
6994 } |
|
6995 } |
|
6996 |
|
6997 /* |
|
6998 * At this point, (index == 0 && missingTrailingComma) implies one |
|
6999 * element initialiser was parsed. |
|
7000 * |
|
7001 * A legacy array comprehension of the form: |
|
7002 * |
|
7003 * [i * j for (i in o) for (j in p) if (i != j)] |
|
7004 * |
|
7005 * translates to roughly the following let expression: |
|
7006 * |
|
7007 * let (array = new Array, i, j) { |
|
7008 * for (i in o) let { |
|
7009 * for (j in p) |
|
7010 * if (i != j) |
|
7011 * array.push(i * j) |
|
7012 * } |
|
7013 * array |
|
7014 * } |
|
7015 * |
|
7016 * where array is a nameless block-local variable. The "roughly" means |
|
7017 * that an implementation may optimize away the array.push. A legacy |
|
7018 * array comprehension opens exactly one block scope, no matter how many |
|
7019 * for heads it contains. |
|
7020 * |
|
7021 * Each let () {...} or for (let ...) ... compiles to: |
|
7022 * |
|
7023 * JSOP_PUSHN <N> // Push space for block-scoped locals. |
|
7024 * (JSOP_PUSHBLOCKSCOPE <O>) // If a local is aliased, push on scope |
|
7025 * // chain. |
|
7026 * ... |
|
7027 * JSOP_DEBUGLEAVEBLOCK // Invalidate any DebugScope proxies. |
|
7028 * JSOP_POPBLOCKSCOPE? // Pop off scope chain, if needed. |
|
7029 * JSOP_POPN <N> // Pop space for block-scoped locals. |
|
7030 * |
|
7031 * where <o> is a literal object representing the block scope, |
|
7032 * with <n> properties, naming each var declared in the block. |
|
7033 * |
|
7034 * Each var declaration in a let-block binds a name in <o> at compile |
|
7035 * time. A block-local var is accessed by the JSOP_GETLOCAL and |
|
7036 * JSOP_SETLOCAL ops. These ops have an immediate operand, the local |
|
7037 * slot's stack index from fp->spbase. |
|
7038 * |
|
7039 * The legacy array comprehension iteration step, array.push(i * j) in |
|
7040 * the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>, where |
|
7041 * <array> is the index of array's stack slot. |
|
7042 */ |
|
7043 if (index == 0 && !spread && tokenStream.matchToken(TOK_FOR) && missingTrailingComma) |
|
7044 return legacyArrayComprehension(literal); |
|
7045 |
|
7046 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST); |
|
7047 } |
|
7048 handler.setEndPosition(literal, pos().end); |
|
7049 return literal; |
|
7050 } |
|
7051 |
|
7052 static JSAtom* |
|
7053 DoubleToAtom(ExclusiveContext *cx, double value) |
|
7054 { |
|
7055 // This is safe because doubles can not be moved. |
|
7056 Value tmp = DoubleValue(value); |
|
7057 return ToAtom<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp)); |
|
7058 } |
|
7059 |
|
7060 template <typename ParseHandler> |
|
7061 typename ParseHandler::Node |
|
7062 Parser<ParseHandler>::objectLiteral() |
|
7063 { |
|
7064 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); |
|
7065 |
|
7066 /* |
|
7067 * A map from property names we've seen thus far to a mask of property |
|
7068 * assignment types. |
|
7069 */ |
|
7070 AtomIndexMap seen; |
|
7071 |
|
7072 enum AssignmentType { |
|
7073 GET = 0x1, |
|
7074 SET = 0x2, |
|
7075 VALUE = 0x4 | GET | SET |
|
7076 }; |
|
7077 |
|
7078 Node literal = handler.newObjectLiteral(pos().begin); |
|
7079 if (!literal) |
|
7080 return null(); |
|
7081 |
|
7082 RootedAtom atom(context); |
|
7083 for (;;) { |
|
7084 TokenKind ltok = tokenStream.getToken(TokenStream::KeywordIsName); |
|
7085 if (ltok == TOK_RC) |
|
7086 break; |
|
7087 |
|
7088 JSOp op = JSOP_INITPROP; |
|
7089 Node propname; |
|
7090 switch (ltok) { |
|
7091 case TOK_NUMBER: |
|
7092 atom = DoubleToAtom(context, tokenStream.currentToken().number()); |
|
7093 if (!atom) |
|
7094 return null(); |
|
7095 propname = newNumber(tokenStream.currentToken()); |
|
7096 break; |
|
7097 |
|
7098 case TOK_NAME: { |
|
7099 atom = tokenStream.currentName(); |
|
7100 if (atom == context->names().get) { |
|
7101 op = JSOP_INITPROP_GETTER; |
|
7102 } else if (atom == context->names().set) { |
|
7103 op = JSOP_INITPROP_SETTER; |
|
7104 } else { |
|
7105 propname = handler.newIdentifier(atom, pos()); |
|
7106 if (!propname) |
|
7107 return null(); |
|
7108 break; |
|
7109 } |
|
7110 |
|
7111 // We have parsed |get| or |set|. Look for an accessor property |
|
7112 // name next. |
|
7113 TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName); |
|
7114 if (tt == TOK_NAME) { |
|
7115 atom = tokenStream.currentName(); |
|
7116 propname = newName(atom->asPropertyName()); |
|
7117 if (!propname) |
|
7118 return null(); |
|
7119 } else if (tt == TOK_STRING) { |
|
7120 atom = tokenStream.currentToken().atom(); |
|
7121 |
|
7122 uint32_t index; |
|
7123 if (atom->isIndex(&index)) { |
|
7124 propname = handler.newNumber(index, NoDecimal, pos()); |
|
7125 if (!propname) |
|
7126 return null(); |
|
7127 atom = DoubleToAtom(context, index); |
|
7128 if (!atom) |
|
7129 return null(); |
|
7130 } else { |
|
7131 propname = stringLiteral(); |
|
7132 if (!propname) |
|
7133 return null(); |
|
7134 } |
|
7135 } else if (tt == TOK_NUMBER) { |
|
7136 atom = DoubleToAtom(context, tokenStream.currentToken().number()); |
|
7137 if (!atom) |
|
7138 return null(); |
|
7139 propname = newNumber(tokenStream.currentToken()); |
|
7140 if (!propname) |
|
7141 return null(); |
|
7142 } else { |
|
7143 // Not an accessor property after all. |
|
7144 tokenStream.ungetToken(); |
|
7145 propname = handler.newIdentifier(atom, pos()); |
|
7146 if (!propname) |
|
7147 return null(); |
|
7148 op = JSOP_INITPROP; |
|
7149 break; |
|
7150 } |
|
7151 |
|
7152 JS_ASSERT(op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER); |
|
7153 break; |
|
7154 } |
|
7155 |
|
7156 case TOK_STRING: { |
|
7157 atom = tokenStream.currentToken().atom(); |
|
7158 uint32_t index; |
|
7159 if (atom->isIndex(&index)) { |
|
7160 propname = handler.newNumber(index, NoDecimal, pos()); |
|
7161 if (!propname) |
|
7162 return null(); |
|
7163 } else { |
|
7164 propname = stringLiteral(); |
|
7165 if (!propname) |
|
7166 return null(); |
|
7167 } |
|
7168 break; |
|
7169 } |
|
7170 |
|
7171 default: |
|
7172 report(ParseError, false, null(), JSMSG_BAD_PROP_ID); |
|
7173 return null(); |
|
7174 } |
|
7175 |
|
7176 if (op == JSOP_INITPROP) { |
|
7177 TokenKind tt = tokenStream.getToken(); |
|
7178 Node propexpr; |
|
7179 if (tt == TOK_COLON) { |
|
7180 propexpr = assignExpr(); |
|
7181 if (!propexpr) |
|
7182 return null(); |
|
7183 |
|
7184 if (foldConstants && !FoldConstants(context, &propexpr, this)) |
|
7185 return null(); |
|
7186 |
|
7187 /* |
|
7188 * Treat initializers which mutate __proto__ as non-constant, |
|
7189 * so that we can later assume singleton objects delegate to |
|
7190 * the default Object.prototype. |
|
7191 */ |
|
7192 if (!handler.isConstant(propexpr) || atom == context->names().proto) |
|
7193 handler.setListFlag(literal, PNX_NONCONST); |
|
7194 |
|
7195 if (!handler.addPropertyDefinition(literal, propname, propexpr)) |
|
7196 return null(); |
|
7197 } |
|
7198 else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) { |
|
7199 /* |
|
7200 * Support, e.g., |var {x, y} = o| as destructuring shorthand |
|
7201 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8. |
|
7202 */ |
|
7203 if (!abortIfSyntaxParser()) |
|
7204 return null(); |
|
7205 tokenStream.ungetToken(); |
|
7206 if (!tokenStream.checkForKeyword(atom->charsZ(), atom->length(), nullptr)) |
|
7207 return null(); |
|
7208 PropertyName *name = handler.isName(propname); |
|
7209 JS_ASSERT(atom); |
|
7210 propname = newName(name); |
|
7211 if (!propname) |
|
7212 return null(); |
|
7213 if (!handler.addShorthandPropertyDefinition(literal, propname)) |
|
7214 return null(); |
|
7215 } |
|
7216 else { |
|
7217 report(ParseError, false, null(), JSMSG_COLON_AFTER_ID); |
|
7218 return null(); |
|
7219 } |
|
7220 } else { |
|
7221 /* NB: Getter function in { get x(){} } is unnamed. */ |
|
7222 Rooted<PropertyName*> funName(context, nullptr); |
|
7223 TokenStream::Position start(keepAtoms); |
|
7224 tokenStream.tell(&start); |
|
7225 Node accessor = functionDef(funName, start, op == JSOP_INITPROP_GETTER ? Getter : Setter, |
|
7226 Expression, NotGenerator); |
|
7227 if (!accessor) |
|
7228 return null(); |
|
7229 if (!handler.addAccessorPropertyDefinition(literal, propname, accessor, op)) |
|
7230 return null(); |
|
7231 } |
|
7232 |
|
7233 /* |
|
7234 * Check for duplicate property names. Duplicate data properties |
|
7235 * only conflict in strict mode. Duplicate getter or duplicate |
|
7236 * setter halves always conflict. A data property conflicts with |
|
7237 * any part of an accessor property. |
|
7238 */ |
|
7239 AssignmentType assignType; |
|
7240 if (op == JSOP_INITPROP) |
|
7241 assignType = VALUE; |
|
7242 else if (op == JSOP_INITPROP_GETTER) |
|
7243 assignType = GET; |
|
7244 else if (op == JSOP_INITPROP_SETTER) |
|
7245 assignType = SET; |
|
7246 else |
|
7247 MOZ_ASSUME_UNREACHABLE("bad opcode in object initializer"); |
|
7248 |
|
7249 AtomIndexAddPtr p = seen.lookupForAdd(atom); |
|
7250 if (p) { |
|
7251 jsatomid index = p.value(); |
|
7252 AssignmentType oldAssignType = AssignmentType(index); |
|
7253 if ((oldAssignType & assignType) && |
|
7254 (oldAssignType != VALUE || assignType != VALUE || pc->sc->needStrictChecks())) |
|
7255 { |
|
7256 JSAutoByteString name; |
|
7257 if (!AtomToPrintableString(context, atom, &name)) |
|
7258 return null(); |
|
7259 |
|
7260 ParseReportKind reportKind = |
|
7261 (oldAssignType == VALUE && assignType == VALUE && !pc->sc->needStrictChecks()) |
|
7262 ? ParseWarning |
|
7263 : (pc->sc->needStrictChecks() ? ParseStrictError : ParseError); |
|
7264 if (!report(reportKind, pc->sc->strict, null(), |
|
7265 JSMSG_DUPLICATE_PROPERTY, name.ptr())) |
|
7266 { |
|
7267 return null(); |
|
7268 } |
|
7269 } |
|
7270 p.value() = assignType | oldAssignType; |
|
7271 } else { |
|
7272 if (!seen.add(p, atom, assignType)) |
|
7273 return null(); |
|
7274 } |
|
7275 |
|
7276 TokenKind tt = tokenStream.getToken(); |
|
7277 if (tt == TOK_RC) |
|
7278 break; |
|
7279 if (tt != TOK_COMMA) { |
|
7280 report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST); |
|
7281 return null(); |
|
7282 } |
|
7283 } |
|
7284 |
|
7285 handler.setEndPosition(literal, pos().end); |
|
7286 return literal; |
|
7287 } |
|
7288 |
|
7289 template <typename ParseHandler> |
|
7290 typename ParseHandler::Node |
|
7291 Parser<ParseHandler>::primaryExpr(TokenKind tt) |
|
7292 { |
|
7293 JS_ASSERT(tokenStream.isCurrentTokenType(tt)); |
|
7294 JS_CHECK_RECURSION(context, return null()); |
|
7295 |
|
7296 switch (tt) { |
|
7297 case TOK_FUNCTION: |
|
7298 return functionExpr(); |
|
7299 |
|
7300 case TOK_LB: |
|
7301 return arrayInitializer(); |
|
7302 |
|
7303 case TOK_LC: |
|
7304 return objectLiteral(); |
|
7305 |
|
7306 case TOK_LET: |
|
7307 return letBlock(LetExpresion); |
|
7308 |
|
7309 case TOK_LP: |
|
7310 return parenExprOrGeneratorComprehension(); |
|
7311 |
|
7312 case TOK_STRING: |
|
7313 return stringLiteral(); |
|
7314 |
|
7315 case TOK_YIELD: |
|
7316 if (!checkYieldNameValidity()) |
|
7317 return null(); |
|
7318 // Fall through. |
|
7319 case TOK_NAME: |
|
7320 return identifierName(); |
|
7321 |
|
7322 case TOK_REGEXP: |
|
7323 return newRegExp(); |
|
7324 |
|
7325 case TOK_NUMBER: |
|
7326 return newNumber(tokenStream.currentToken()); |
|
7327 |
|
7328 case TOK_TRUE: |
|
7329 return handler.newBooleanLiteral(true, pos()); |
|
7330 case TOK_FALSE: |
|
7331 return handler.newBooleanLiteral(false, pos()); |
|
7332 case TOK_THIS: |
|
7333 return handler.newThisLiteral(pos()); |
|
7334 case TOK_NULL: |
|
7335 return handler.newNullLiteral(pos()); |
|
7336 |
|
7337 case TOK_RP: |
|
7338 // Not valid expression syntax, but this is valid in an arrow function |
|
7339 // with no params: `() => body`. |
|
7340 if (tokenStream.peekToken() == TOK_ARROW) { |
|
7341 tokenStream.ungetToken(); // put back right paren |
|
7342 |
|
7343 // Now just return something that will allow parsing to continue. |
|
7344 // It doesn't matter what; when we reach the =>, we will rewind and |
|
7345 // reparse the whole arrow function. See Parser::assignExpr. |
|
7346 return handler.newNullLiteral(pos()); |
|
7347 } |
|
7348 report(ParseError, false, null(), JSMSG_SYNTAX_ERROR); |
|
7349 return null(); |
|
7350 |
|
7351 case TOK_TRIPLEDOT: |
|
7352 // Not valid expression syntax, but this is valid in an arrow function |
|
7353 // with a rest param: `(a, b, ...rest) => body`. |
|
7354 if (tokenStream.matchToken(TOK_NAME) && |
|
7355 tokenStream.matchToken(TOK_RP) && |
|
7356 tokenStream.peekToken() == TOK_ARROW) |
|
7357 { |
|
7358 tokenStream.ungetToken(); // put back right paren |
|
7359 |
|
7360 // Return an arbitrary expression node. See case TOK_RP above. |
|
7361 return handler.newNullLiteral(pos()); |
|
7362 } |
|
7363 report(ParseError, false, null(), JSMSG_SYNTAX_ERROR); |
|
7364 return null(); |
|
7365 |
|
7366 case TOK_ERROR: |
|
7367 /* The scanner or one of its subroutines reported the error. */ |
|
7368 return null(); |
|
7369 |
|
7370 default: |
|
7371 report(ParseError, false, null(), JSMSG_SYNTAX_ERROR); |
|
7372 return null(); |
|
7373 } |
|
7374 } |
|
7375 |
|
7376 template <typename ParseHandler> |
|
7377 typename ParseHandler::Node |
|
7378 Parser<ParseHandler>::parenExprOrGeneratorComprehension() |
|
7379 { |
|
7380 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP)); |
|
7381 uint32_t begin = pos().begin; |
|
7382 uint32_t startYieldOffset = pc->lastYieldOffset; |
|
7383 |
|
7384 if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand)) |
|
7385 return generatorComprehension(begin); |
|
7386 |
|
7387 /* |
|
7388 * Always accept the 'in' operator in a parenthesized expression, |
|
7389 * where it's unambiguous, even if we might be parsing the init of a |
|
7390 * for statement. |
|
7391 */ |
|
7392 bool oldParsingForInit = pc->parsingForInit; |
|
7393 pc->parsingForInit = false; |
|
7394 Node pn = expr(); |
|
7395 pc->parsingForInit = oldParsingForInit; |
|
7396 |
|
7397 if (!pn) |
|
7398 return null(); |
|
7399 |
|
7400 #if JS_HAS_GENERATOR_EXPRS |
|
7401 if (tokenStream.matchToken(TOK_FOR)) { |
|
7402 if (pc->lastYieldOffset != startYieldOffset) { |
|
7403 reportWithOffset(ParseError, false, pc->lastYieldOffset, |
|
7404 JSMSG_BAD_GENEXP_BODY, js_yield_str); |
|
7405 return null(); |
|
7406 } |
|
7407 if (handler.isOperationWithoutParens(pn, PNK_COMMA)) { |
|
7408 report(ParseError, false, null(), |
|
7409 JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str); |
|
7410 return null(); |
|
7411 } |
|
7412 pn = legacyGeneratorExpr(pn); |
|
7413 if (!pn) |
|
7414 return null(); |
|
7415 handler.setBeginPosition(pn, begin); |
|
7416 if (tokenStream.getToken() != TOK_RP) { |
|
7417 report(ParseError, false, null(), |
|
7418 JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str); |
|
7419 return null(); |
|
7420 } |
|
7421 handler.setEndPosition(pn, pos().end); |
|
7422 handler.setInParens(pn); |
|
7423 return pn; |
|
7424 } |
|
7425 #endif /* JS_HAS_GENERATOR_EXPRS */ |
|
7426 |
|
7427 pn = handler.setInParens(pn); |
|
7428 |
|
7429 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); |
|
7430 |
|
7431 return pn; |
|
7432 } |
|
7433 |
|
7434 // Legacy generator comprehensions can sometimes appear without parentheses. |
|
7435 // For example: |
|
7436 // |
|
7437 // foo(x for (x in bar)) |
|
7438 // |
|
7439 // In this case the parens are part of the call, and not part of the generator |
|
7440 // comprehension. This can happen in these contexts: |
|
7441 // |
|
7442 // if (_) |
|
7443 // while (_) {} |
|
7444 // do {} while (_) |
|
7445 // switch (_) {} |
|
7446 // with (_) {} |
|
7447 // foo(_) // must be first and only argument |
|
7448 // |
|
7449 // This is not the case for ES6 generator comprehensions; they must always be in |
|
7450 // parentheses. |
|
7451 |
|
7452 template <typename ParseHandler> |
|
7453 typename ParseHandler::Node |
|
7454 Parser<ParseHandler>::exprInParens() |
|
7455 { |
|
7456 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP)); |
|
7457 uint32_t begin = pos().begin; |
|
7458 uint32_t startYieldOffset = pc->lastYieldOffset; |
|
7459 |
|
7460 /* |
|
7461 * Always accept the 'in' operator in a parenthesized expression, |
|
7462 * where it's unambiguous, even if we might be parsing the init of a |
|
7463 * for statement. |
|
7464 */ |
|
7465 bool oldParsingForInit = pc->parsingForInit; |
|
7466 pc->parsingForInit = false; |
|
7467 Node pn = expr(); |
|
7468 pc->parsingForInit = oldParsingForInit; |
|
7469 |
|
7470 if (!pn) |
|
7471 return null(); |
|
7472 |
|
7473 #if JS_HAS_GENERATOR_EXPRS |
|
7474 if (tokenStream.matchToken(TOK_FOR)) { |
|
7475 if (pc->lastYieldOffset != startYieldOffset) { |
|
7476 reportWithOffset(ParseError, false, pc->lastYieldOffset, |
|
7477 JSMSG_BAD_GENEXP_BODY, js_yield_str); |
|
7478 return null(); |
|
7479 } |
|
7480 if (handler.isOperationWithoutParens(pn, PNK_COMMA)) { |
|
7481 report(ParseError, false, null(), |
|
7482 JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str); |
|
7483 return null(); |
|
7484 } |
|
7485 pn = legacyGeneratorExpr(pn); |
|
7486 if (!pn) |
|
7487 return null(); |
|
7488 handler.setBeginPosition(pn, begin); |
|
7489 } |
|
7490 #endif /* JS_HAS_GENERATOR_EXPRS */ |
|
7491 |
|
7492 return pn; |
|
7493 } |
|
7494 |
|
7495 template class Parser<FullParseHandler>; |
|
7496 template class Parser<SyntaxParseHandler>; |
|
7497 |
|
7498 } /* namespace frontend */ |
|
7499 } /* namespace js */ |