1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Netscape Communications Corporation.
22 : * Portions created by the Initial Developer are Copyright (C) 1998
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * JS parser.
43 : *
44 : * This is a recursive-descent parser for the JavaScript language specified by
45 : * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
46 : * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
47 : * induced by the recursive parsing (not precise syntax trees, see Parser.h).
48 : * After tree construction, it rewrites trees to fold constants and evaluate
49 : * compile-time expressions. Finally, it calls js::frontend::EmitTree (see
50 : * BytecodeEmitter.h) to generate bytecode.
51 : *
52 : * This parser attempts no error recovery.
53 : */
54 :
55 : #include "frontend/Parser.h"
56 :
57 : #include <stdlib.h>
58 : #include <string.h>
59 : #include "jstypes.h"
60 : #include "jsutil.h"
61 : #include "jsapi.h"
62 : #include "jsarray.h"
63 : #include "jsatom.h"
64 : #include "jscntxt.h"
65 : #include "jsversion.h"
66 : #include "jsfun.h"
67 : #include "jsgc.h"
68 : #include "jsgcmark.h"
69 : #include "jsinterp.h"
70 : #include "jsiter.h"
71 : #include "jslock.h"
72 : #include "jsnum.h"
73 : #include "jsobj.h"
74 : #include "jsopcode.h"
75 : #include "jsscope.h"
76 : #include "jsscript.h"
77 : #include "jsstr.h"
78 :
79 : #include "frontend/BytecodeCompiler.h"
80 : #include "frontend/BytecodeEmitter.h"
81 : #include "frontend/FoldConstants.h"
82 : #include "frontend/ParseMaps.h"
83 : #include "frontend/TokenStream.h"
84 :
85 : #if JS_HAS_XML_SUPPORT
86 : #include "jsxml.h"
87 : #endif
88 :
89 : #include "jsatominlines.h"
90 : #include "jsscriptinlines.h"
91 :
92 : #include "frontend/BytecodeEmitter-inl.h"
93 : #include "frontend/ParseMaps-inl.h"
94 : #include "frontend/ParseNode-inl.h"
95 : #include "vm/RegExpObject-inl.h"
96 :
97 : using namespace js;
98 : using namespace js::gc;
99 : using namespace js::frontend;
100 :
101 : /*
102 : * Insist that the next token be of type tt, or report errno and return null.
103 : * NB: this macro uses cx and ts from its lexical environment.
104 : */
105 : #define MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, __flags) \
106 : JS_BEGIN_MACRO \
107 : if (tokenStream.getToken((__flags)) != tt) { \
108 : reportErrorNumber(NULL, JSREPORT_ERROR, errno); \
109 : return NULL; \
110 : } \
111 : JS_END_MACRO
112 : #define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, 0)
113 :
114 109194 : Parser::Parser(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin,
115 : StackFrame *cfp, bool foldConstants)
116 : : AutoGCRooter(cx, PARSER),
117 : context(cx),
118 : tokenStream(cx, prin, originPrin),
119 : principals(NULL),
120 : originPrincipals(NULL),
121 : callerFrame(cfp),
122 : callerVarObj(cfp ? &cfp->varObj() : NULL),
123 : allocator(cx),
124 : functionCount(0),
125 : traceListHead(NULL),
126 : tc(NULL),
127 : keepAtoms(cx->runtime),
128 109194 : foldConstants(foldConstants)
129 : {
130 109194 : cx->activeCompilations++;
131 109194 : PodArrayZero(tempFreeList);
132 109194 : setPrincipals(prin, originPrin);
133 109194 : JS_ASSERT_IF(cfp, cfp->isScriptFrame());
134 109194 : }
135 :
136 : bool
137 109194 : Parser::init(const jschar *base, size_t length, const char *filename, unsigned lineno,
138 : JSVersion version)
139 : {
140 109194 : JSContext *cx = context;
141 109194 : if (!cx->ensureParseMapPool())
142 0 : return false;
143 109194 : tempPoolMark = cx->tempLifoAlloc().mark();
144 109194 : if (!tokenStream.init(base, length, filename, lineno, version)) {
145 0 : cx->tempLifoAlloc().release(tempPoolMark);
146 0 : return false;
147 : }
148 109194 : return true;
149 : }
150 :
151 218388 : Parser::~Parser()
152 : {
153 109194 : JSContext *cx = context;
154 109194 : if (principals)
155 64 : JS_DropPrincipals(cx->runtime, principals);
156 109194 : if (originPrincipals)
157 60 : JS_DropPrincipals(cx->runtime, originPrincipals);
158 109194 : cx->tempLifoAlloc().release(tempPoolMark);
159 109194 : cx->activeCompilations--;
160 109194 : }
161 :
162 : void
163 109194 : Parser::setPrincipals(JSPrincipals *prin, JSPrincipals *originPrin)
164 : {
165 109194 : JS_ASSERT(!principals && !originPrincipals);
166 109194 : principals = prin;
167 109194 : if (principals)
168 64 : JS_HoldPrincipals(principals);
169 109194 : originPrincipals = originPrin;
170 109194 : if (originPrincipals)
171 60 : JS_HoldPrincipals(originPrincipals);
172 109194 : }
173 :
174 : ObjectBox *
175 140461 : Parser::newObjectBox(JSObject *obj)
176 : {
177 : /*
178 : * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
179 : * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
180 : * arenas containing the entries must be alive until we are done with
181 : * scanning, parsing and code generation for the whole script or top-level
182 : * function.
183 : */
184 140461 : ObjectBox *objbox = context->tempLifoAlloc().new_<ObjectBox>();
185 140461 : if (!objbox) {
186 0 : js_ReportOutOfMemory(context);
187 0 : return NULL;
188 : }
189 140461 : objbox->traceLink = traceListHead;
190 140461 : traceListHead = objbox;
191 140461 : objbox->emitLink = NULL;
192 140461 : objbox->object = obj;
193 140461 : objbox->isFunctionBox = false;
194 140461 : return objbox;
195 : }
196 :
197 : FunctionBox *
198 156778 : Parser::newFunctionBox(JSObject *obj, ParseNode *fn, TreeContext *tc)
199 : {
200 156778 : JS_ASSERT(obj);
201 156778 : JS_ASSERT(obj->isFunction());
202 :
203 : /*
204 : * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
205 : * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
206 : * arenas containing the entries must be alive until we are done with
207 : * scanning, parsing and code generation for the whole script or top-level
208 : * function.
209 : */
210 156778 : FunctionBox *funbox = context->tempLifoAlloc().newPod<FunctionBox>();
211 156778 : if (!funbox) {
212 0 : js_ReportOutOfMemory(context);
213 0 : return NULL;
214 : }
215 156778 : funbox->traceLink = traceListHead;
216 156778 : traceListHead = funbox;
217 156778 : funbox->emitLink = NULL;
218 156778 : funbox->object = obj;
219 156778 : funbox->isFunctionBox = true;
220 156778 : funbox->node = fn;
221 156778 : funbox->siblings = tc->functionList;
222 156778 : tc->functionList = funbox;
223 156778 : ++tc->parser->functionCount;
224 156778 : funbox->kids = NULL;
225 156778 : funbox->parent = tc->funbox;
226 156778 : funbox->methods = NULL;
227 156778 : new (&funbox->bindings) Bindings(context);
228 156778 : funbox->queued = false;
229 156778 : funbox->inLoop = false;
230 323592 : for (StmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
231 168461 : if (STMT_IS_LOOP(stmt)) {
232 1647 : funbox->inLoop = true;
233 1647 : break;
234 : }
235 : }
236 156778 : funbox->level = tc->staticLevel;
237 156778 : funbox->tcflags = (TCF_IN_FUNCTION | (tc->flags & (TCF_COMPILE_N_GO | TCF_STRICT_MODE_CODE)));
238 156778 : if (tc->innermostWith)
239 180 : funbox->tcflags |= TCF_IN_WITH;
240 156778 : if (!tc->inFunction()) {
241 144837 : JSObject *scope = tc->scopeChain();
242 441768 : while (scope) {
243 152094 : if (scope->isWith())
244 0 : funbox->tcflags |= TCF_IN_WITH;
245 152094 : scope = scope->enclosingScope();
246 : }
247 : }
248 156778 : return funbox;
249 : }
250 :
251 : void
252 15 : Parser::trace(JSTracer *trc)
253 : {
254 15 : ObjectBox *objbox = traceListHead;
255 30 : while (objbox) {
256 0 : MarkObjectRoot(trc, &objbox->object, "parser.object");
257 0 : if (objbox->isFunctionBox)
258 0 : static_cast<FunctionBox *>(objbox)->bindings.trace(trc);
259 0 : objbox = objbox->traceLink;
260 : }
261 :
262 21 : for (TreeContext *tc = this->tc; tc; tc = tc->parent)
263 6 : tc->trace(trc);
264 15 : }
265 :
266 : static bool
267 918 : GenerateBlockIdForStmtNode(ParseNode *pn, TreeContext *tc)
268 : {
269 918 : JS_ASSERT(tc->topStmt);
270 918 : JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt));
271 918 : JS_ASSERT(pn->isKind(PNK_STATEMENTLIST) || pn->isKind(PNK_LEXICALSCOPE));
272 918 : if (!GenerateBlockId(tc, tc->topStmt->blockid))
273 0 : return false;
274 918 : pn->pn_blockid = tc->topStmt->blockid;
275 918 : return true;
276 : }
277 :
278 : /*
279 : * Parse a top-level JS script.
280 : */
281 : ParseNode *
282 2367 : Parser::parse(JSObject *chain)
283 : {
284 : /*
285 : * Protect atoms from being collected by a GC activation, which might
286 : * - nest on this thread due to out of memory (the so-called "last ditch"
287 : * GC attempted within js_NewGCThing), or
288 : * - run for any reason on another thread if this thread is suspended on
289 : * an object lock before it finishes generating bytecode into a script
290 : * protected from the GC by a root or a stack frame reference.
291 : */
292 4734 : TreeContext globaltc(this);
293 2367 : if (!globaltc.init(context))
294 0 : return NULL;
295 2367 : globaltc.setScopeChain(chain);
296 2367 : if (!GenerateBlockId(&globaltc, globaltc.bodyid))
297 0 : return NULL;
298 :
299 2367 : ParseNode *pn = statements();
300 2367 : if (pn) {
301 2367 : if (!tokenStream.matchToken(TOK_EOF)) {
302 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
303 0 : pn = NULL;
304 2367 : } else if (foldConstants) {
305 0 : if (!FoldConstants(context, pn, &globaltc))
306 0 : pn = NULL;
307 : }
308 : }
309 2367 : return pn;
310 : }
311 :
312 : JS_STATIC_ASSERT(UpvarCookie::FREE_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
313 :
314 : /*
315 : * Insist on a final return before control flows out of pn. Try to be a bit
316 : * smart about loops: do {...; return e2;} while(0) at the end of a function
317 : * that contains an early return e1 will get a strict warning. Similarly for
318 : * iloops: while (true){...} is treated as though ... returns.
319 : */
320 : #define ENDS_IN_OTHER 0
321 : #define ENDS_IN_RETURN 1
322 : #define ENDS_IN_BREAK 2
323 :
324 : static int
325 18 : HasFinalReturn(ParseNode *pn)
326 : {
327 : ParseNode *pn2, *pn3;
328 : unsigned rv, rv2, hasDefault;
329 :
330 18 : switch (pn->getKind()) {
331 : case PNK_STATEMENTLIST:
332 9 : if (!pn->pn_head)
333 0 : return ENDS_IN_OTHER;
334 9 : return HasFinalReturn(pn->last());
335 :
336 : case PNK_IF:
337 9 : if (!pn->pn_kid3)
338 9 : return ENDS_IN_OTHER;
339 0 : return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
340 :
341 : case PNK_WHILE:
342 0 : pn2 = pn->pn_left;
343 0 : if (pn2->isKind(PNK_TRUE))
344 0 : return ENDS_IN_RETURN;
345 0 : if (pn2->isKind(PNK_NUMBER) && pn2->pn_dval)
346 0 : return ENDS_IN_RETURN;
347 0 : return ENDS_IN_OTHER;
348 :
349 : case PNK_DOWHILE:
350 0 : pn2 = pn->pn_right;
351 0 : if (pn2->isKind(PNK_FALSE))
352 0 : return HasFinalReturn(pn->pn_left);
353 0 : if (pn2->isKind(PNK_TRUE))
354 0 : return ENDS_IN_RETURN;
355 0 : if (pn2->isKind(PNK_NUMBER)) {
356 0 : if (pn2->pn_dval == 0)
357 0 : return HasFinalReturn(pn->pn_left);
358 0 : return ENDS_IN_RETURN;
359 : }
360 0 : return ENDS_IN_OTHER;
361 :
362 : case PNK_FOR:
363 0 : pn2 = pn->pn_left;
364 0 : if (pn2->isArity(PN_TERNARY) && !pn2->pn_kid2)
365 0 : return ENDS_IN_RETURN;
366 0 : return ENDS_IN_OTHER;
367 :
368 : case PNK_SWITCH:
369 0 : rv = ENDS_IN_RETURN;
370 0 : hasDefault = ENDS_IN_OTHER;
371 0 : pn2 = pn->pn_right;
372 0 : if (pn2->isKind(PNK_LEXICALSCOPE))
373 0 : pn2 = pn2->expr();
374 0 : for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
375 0 : if (pn2->isKind(PNK_DEFAULT))
376 0 : hasDefault = ENDS_IN_RETURN;
377 0 : pn3 = pn2->pn_right;
378 0 : JS_ASSERT(pn3->isKind(PNK_STATEMENTLIST));
379 0 : if (pn3->pn_head) {
380 0 : rv2 = HasFinalReturn(pn3->last());
381 0 : if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
382 : /* Falling through to next case or default. */;
383 : else
384 0 : rv &= rv2;
385 : }
386 : }
387 : /* If a final switch has no default case, we judge it harshly. */
388 0 : rv &= hasDefault;
389 0 : return rv;
390 :
391 : case PNK_BREAK:
392 0 : return ENDS_IN_BREAK;
393 :
394 : case PNK_WITH:
395 0 : return HasFinalReturn(pn->pn_right);
396 :
397 : case PNK_RETURN:
398 0 : return ENDS_IN_RETURN;
399 :
400 : case PNK_COLON:
401 : case PNK_LEXICALSCOPE:
402 0 : return HasFinalReturn(pn->expr());
403 :
404 : case PNK_THROW:
405 0 : return ENDS_IN_RETURN;
406 :
407 : case PNK_TRY:
408 : /* If we have a finally block that returns, we are done. */
409 0 : if (pn->pn_kid3) {
410 0 : rv = HasFinalReturn(pn->pn_kid3);
411 0 : if (rv == ENDS_IN_RETURN)
412 0 : return rv;
413 : }
414 :
415 : /* Else check the try block and any and all catch statements. */
416 0 : rv = HasFinalReturn(pn->pn_kid1);
417 0 : if (pn->pn_kid2) {
418 0 : JS_ASSERT(pn->pn_kid2->isArity(PN_LIST));
419 0 : for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
420 0 : rv &= HasFinalReturn(pn2);
421 : }
422 0 : return rv;
423 :
424 : case PNK_CATCH:
425 : /* Check this catch block's body. */
426 0 : return HasFinalReturn(pn->pn_kid3);
427 :
428 : case PNK_LET:
429 : /* Non-binary let statements are let declarations. */
430 0 : if (!pn->isArity(PN_BINARY))
431 0 : return ENDS_IN_OTHER;
432 0 : return HasFinalReturn(pn->pn_right);
433 :
434 : default:
435 0 : return ENDS_IN_OTHER;
436 : }
437 : }
438 :
439 : static JSBool
440 9 : ReportBadReturn(JSContext *cx, TreeContext *tc, ParseNode *pn, unsigned flags, unsigned errnum,
441 : unsigned anonerrnum)
442 : {
443 18 : JSAutoByteString name;
444 9 : if (tc->fun()->atom) {
445 9 : if (!js_AtomToPrintableString(cx, tc->fun()->atom, &name))
446 0 : return false;
447 : } else {
448 0 : errnum = anonerrnum;
449 : }
450 9 : return ReportCompileErrorNumber(cx, TS(tc->parser), pn, flags, errnum, name.ptr());
451 : }
452 :
453 : static JSBool
454 9 : CheckFinalReturn(JSContext *cx, TreeContext *tc, ParseNode *pn)
455 : {
456 9 : JS_ASSERT(tc->inFunction());
457 9 : return HasFinalReturn(pn) == ENDS_IN_RETURN ||
458 : ReportBadReturn(cx, tc, pn, JSREPORT_WARNING | JSREPORT_STRICT,
459 9 : JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
460 : }
461 :
462 : /*
463 : * Check that it is permitted to assign to lhs. Strict mode code may not
464 : * assign to 'eval' or 'arguments'.
465 : */
466 : static bool
467 402875 : CheckStrictAssignment(JSContext *cx, TreeContext *tc, ParseNode *lhs)
468 : {
469 402875 : if (tc->needStrictChecks() && lhs->isKind(PNK_NAME)) {
470 435 : JSAtom *atom = lhs->pn_atom;
471 435 : JSAtomState *atomState = &cx->runtime->atomState;
472 435 : if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
473 0 : JSAutoByteString name;
474 0 : if (!js_AtomToPrintableString(cx, atom, &name) ||
475 : !ReportStrictModeError(cx, TS(tc->parser), tc, lhs, JSMSG_DEPRECATED_ASSIGN,
476 0 : name.ptr())) {
477 0 : return false;
478 : }
479 : }
480 : }
481 402875 : return true;
482 : }
483 :
484 : /*
485 : * Check that it is permitted to introduce a binding for atom. Strict mode
486 : * forbids introducing new definitions for 'eval', 'arguments', or for any
487 : * strict mode reserved keyword. Use pn for reporting error locations, or use
488 : * tc's token stream if pn is NULL.
489 : */
490 : bool
491 587589 : CheckStrictBinding(JSContext *cx, TreeContext *tc, PropertyName *name, ParseNode *pn)
492 : {
493 587589 : if (!tc->needStrictChecks())
494 586441 : return true;
495 :
496 1148 : JSAtomState *atomState = &cx->runtime->atomState;
497 2296 : if (name == atomState->evalAtom ||
498 : name == atomState->argumentsAtom ||
499 1148 : FindKeyword(name->charsZ(), name->length()))
500 : {
501 0 : JSAutoByteString bytes;
502 0 : if (!js_AtomToPrintableString(cx, name, &bytes))
503 0 : return false;
504 0 : return ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_BAD_BINDING, bytes.ptr());
505 : }
506 :
507 1148 : return true;
508 : }
509 :
510 : static bool
511 0 : ReportBadParameter(JSContext *cx, TreeContext *tc, JSAtom *name, unsigned errorNumber)
512 : {
513 0 : Definition *dn = tc->decls.lookupFirst(name);
514 0 : JSAutoByteString bytes;
515 0 : return js_AtomToPrintableString(cx, name, &bytes) &&
516 0 : ReportStrictModeError(cx, TS(tc->parser), tc, dn, errorNumber, bytes.ptr());
517 : }
518 :
519 : /*
520 : * In strict mode code, all parameter names must be distinct, must not be
521 : * strict mode reserved keywords, and must not be 'eval' or 'arguments'. We
522 : * must perform these checks here, and not eagerly during parsing, because a
523 : * function's body may turn on strict mode for the function head.
524 : */
525 : bool
526 166865 : js::CheckStrictParameters(JSContext *cx, TreeContext *tc)
527 : {
528 166865 : JS_ASSERT(tc->inFunction());
529 :
530 166865 : if (!tc->needStrictChecks() || tc->bindings.countArgs() == 0)
531 166451 : return true;
532 :
533 414 : JSAtom *argumentsAtom = cx->runtime->atomState.argumentsAtom;
534 414 : JSAtom *evalAtom = cx->runtime->atomState.evalAtom;
535 :
536 : /* name => whether we've warned about the name already */
537 828 : HashMap<JSAtom *, bool> parameters(cx);
538 414 : if (!parameters.init(tc->bindings.countArgs()))
539 0 : return false;
540 :
541 : /* Start with lastVariable(), not lastArgument(), for destructuring. */
542 1017 : for (Shape::Range r = tc->bindings.lastVariable(); !r.empty(); r.popFront()) {
543 603 : jsid id = r.front().propid();
544 603 : if (!JSID_IS_ATOM(id))
545 0 : continue;
546 :
547 603 : JSAtom *name = JSID_TO_ATOM(id);
548 :
549 603 : if (name == argumentsAtom || name == evalAtom) {
550 0 : if (!ReportBadParameter(cx, tc, name, JSMSG_BAD_BINDING))
551 0 : return false;
552 : }
553 :
554 603 : if (tc->inStrictMode() && FindKeyword(name->charsZ(), name->length())) {
555 : /*
556 : * JSOPTION_STRICT is supposed to warn about future keywords, too,
557 : * but we took care of that in the scanner.
558 : */
559 0 : JS_ALWAYS_TRUE(!ReportBadParameter(cx, tc, name, JSMSG_RESERVED_ID));
560 0 : return false;
561 : }
562 :
563 : /*
564 : * Check for a duplicate parameter: warn or report an error exactly
565 : * once for each duplicated parameter.
566 : */
567 1206 : if (HashMap<JSAtom *, bool>::AddPtr p = parameters.lookupForAdd(name)) {
568 0 : if (!p->value && !ReportBadParameter(cx, tc, name, JSMSG_DUPLICATE_FORMAL))
569 0 : return false;
570 0 : p->value = true;
571 : } else {
572 603 : if (!parameters.add(p, name, false))
573 0 : return false;
574 : }
575 : }
576 :
577 414 : return true;
578 : }
579 :
580 : ParseNode *
581 167182 : Parser::functionBody(FunctionBodyType type)
582 : {
583 167182 : JS_ASSERT(tc->inFunction());
584 :
585 : StmtInfo stmtInfo;
586 167182 : PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
587 167182 : stmtInfo.flags = SIF_BODY_BLOCK;
588 :
589 167182 : unsigned oldflags = tc->flags;
590 167182 : tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
591 :
592 : ParseNode *pn;
593 167182 : if (type == StatementListBody) {
594 165418 : pn = statements();
595 : } else {
596 1764 : JS_ASSERT(type == ExpressionBody);
597 : JS_ASSERT(JS_HAS_EXPR_CLOSURES);
598 1764 : pn = UnaryNode::create(PNK_RETURN, tc);
599 1764 : if (pn) {
600 1764 : pn->pn_kid = assignExpr();
601 1764 : if (!pn->pn_kid) {
602 0 : pn = NULL;
603 : } else {
604 1764 : if (tc->flags & TCF_FUN_IS_GENERATOR) {
605 : ReportBadReturn(context, tc, pn, JSREPORT_ERROR,
606 : JSMSG_BAD_GENERATOR_RETURN,
607 0 : JSMSG_BAD_ANON_GENERATOR_RETURN);
608 0 : pn = NULL;
609 : } else {
610 1764 : pn->setOp(JSOP_RETURN);
611 1764 : pn->pn_pos.end = pn->pn_kid->pn_pos.end;
612 : }
613 : }
614 : }
615 : }
616 :
617 167182 : if (pn) {
618 166865 : JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
619 166865 : PopStatementTC(tc);
620 :
621 : /* Check for falling off the end of a function that returns a value. */
622 166874 : if (context->hasStrictOption() && (tc->flags & TCF_RETURN_EXPR) &&
623 9 : !CheckFinalReturn(context, tc, pn)) {
624 0 : pn = NULL;
625 : }
626 : }
627 :
628 167182 : tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
629 167182 : return pn;
630 : }
631 :
632 : /* Create a placeholder Definition node for |atom|. */
633 : static Definition *
634 286830 : MakePlaceholder(ParseNode *pn, TreeContext *tc)
635 : {
636 286830 : Definition *dn = (Definition *) NameNode::create(PNK_NAME, pn->pn_atom, tc);
637 286830 : if (!dn)
638 0 : return NULL;
639 :
640 286830 : dn->setOp(JSOP_NOP);
641 286830 : dn->setDefn(true);
642 286830 : dn->pn_dflags |= PND_PLACEHOLDER;
643 286830 : return dn;
644 : }
645 :
646 : static bool
647 804440 : Define(ParseNode *pn, JSAtom *atom, TreeContext *tc, bool let = false)
648 : {
649 804440 : JS_ASSERT(!pn->isUsed());
650 804440 : JS_ASSERT_IF(pn->isDefn(), pn->isPlaceholder());
651 :
652 804440 : bool foundLexdep = false;
653 804440 : Definition *dn = NULL;
654 :
655 804440 : if (let)
656 322985 : dn = tc->decls.lookupFirst(atom);
657 :
658 804440 : if (!dn) {
659 801497 : dn = tc->lexdeps.lookupDefn(atom);
660 801497 : foundLexdep = !!dn;
661 : }
662 :
663 804440 : if (dn && dn != pn) {
664 4392 : ParseNode **pnup = &dn->dn_uses;
665 : ParseNode *pnu;
666 4392 : unsigned start = let ? pn->pn_blockid : tc->bodyid;
667 :
668 10008 : while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
669 1224 : JS_ASSERT(pnu->isUsed());
670 1224 : pnu->pn_lexdef = (Definition *) pn;
671 1224 : pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
672 1224 : pnup = &pnu->pn_link;
673 : }
674 :
675 4392 : if (pnu != dn->dn_uses) {
676 1179 : *pnup = pn->dn_uses;
677 1179 : pn->dn_uses = dn->dn_uses;
678 1179 : dn->dn_uses = pnu;
679 :
680 1179 : if ((!pnu || pnu->pn_blockid < tc->bodyid) && foundLexdep)
681 1179 : tc->lexdeps->remove(atom);
682 : }
683 :
684 4392 : pn->pn_dflags |= dn->pn_dflags & PND_CLOSED;
685 : }
686 :
687 804440 : Definition *toAdd = (Definition *) pn;
688 804440 : bool ok = let ? tc->decls.addShadow(atom, toAdd) : tc->decls.addUnique(atom, toAdd);
689 804440 : if (!ok)
690 0 : return false;
691 804440 : pn->setDefn(true);
692 804440 : pn->pn_dflags &= ~PND_PLACEHOLDER;
693 804440 : if (!tc->parent)
694 355575 : pn->pn_dflags |= PND_TOPLEVEL;
695 804440 : return true;
696 : }
697 :
698 : static void
699 207 : ForgetUse(ParseNode *pn)
700 : {
701 207 : if (!pn->isUsed()) {
702 0 : JS_ASSERT(!pn->isDefn());
703 0 : return;
704 : }
705 :
706 207 : ParseNode **pnup = &pn->lexdef()->dn_uses;
707 : ParseNode *pnu;
708 414 : while ((pnu = *pnup) != pn)
709 0 : pnup = &pnu->pn_link;
710 207 : *pnup = pn->pn_link;
711 207 : pn->setUsed(false);
712 : }
713 :
714 : static ParseNode *
715 2610 : MakeAssignment(ParseNode *pn, ParseNode *rhs, TreeContext *tc)
716 : {
717 2610 : ParseNode *lhs = tc->parser->cloneNode(*pn);
718 2610 : if (!lhs)
719 0 : return NULL;
720 :
721 2610 : if (pn->isUsed()) {
722 2610 : Definition *dn = pn->pn_lexdef;
723 2610 : ParseNode **pnup = &dn->dn_uses;
724 :
725 5382 : while (*pnup != pn)
726 162 : pnup = &(*pnup)->pn_link;
727 2610 : *pnup = lhs;
728 2610 : lhs->pn_link = pn->pn_link;
729 2610 : pn->pn_link = NULL;
730 : }
731 :
732 2610 : pn->setKind(PNK_ASSIGN);
733 2610 : pn->setOp(JSOP_NOP);
734 2610 : pn->setArity(PN_BINARY);
735 2610 : pn->setInParens(false);
736 2610 : pn->setUsed(false);
737 2610 : pn->setDefn(false);
738 2610 : pn->pn_left = lhs;
739 2610 : pn->pn_right = rhs;
740 2610 : return lhs;
741 : }
742 :
743 : static ParseNode *
744 72 : MakeDefIntoUse(Definition *dn, ParseNode *pn, JSAtom *atom, TreeContext *tc)
745 : {
746 : /*
747 : * If dn is arg, or in [var, const, let] and has an initializer, then we
748 : * must rewrite it to be an assignment node, whose freshly allocated
749 : * left-hand side becomes a use of pn.
750 : */
751 72 : if (dn->isBindingForm()) {
752 27 : ParseNode *rhs = dn->expr();
753 27 : if (rhs) {
754 0 : ParseNode *lhs = MakeAssignment(dn, rhs, tc);
755 0 : if (!lhs)
756 0 : return NULL;
757 : //pn->dn_uses = lhs;
758 0 : dn = (Definition *) lhs;
759 : }
760 :
761 27 : dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME);
762 45 : } else if (dn->kind() == Definition::FUNCTION) {
763 27 : JS_ASSERT(dn->isOp(JSOP_NOP));
764 27 : tc->parser->prepareNodeForMutation(dn);
765 27 : dn->setKind(PNK_NAME);
766 27 : dn->setArity(PN_NAME);
767 27 : dn->pn_atom = atom;
768 : }
769 :
770 : /* Now make dn no longer a definition, rather a use of pn. */
771 72 : JS_ASSERT(dn->isKind(PNK_NAME));
772 72 : JS_ASSERT(dn->isArity(PN_NAME));
773 72 : JS_ASSERT(dn->pn_atom == atom);
774 :
775 117 : for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
776 45 : JS_ASSERT(pnu->isUsed());
777 45 : JS_ASSERT(!pnu->isDefn());
778 45 : pnu->pn_lexdef = (Definition *) pn;
779 45 : pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
780 : }
781 72 : pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
782 72 : pn->dn_uses = dn;
783 :
784 72 : dn->setDefn(false);
785 72 : dn->setUsed(true);
786 72 : dn->pn_lexdef = (Definition *) pn;
787 72 : dn->pn_cookie.makeFree();
788 72 : dn->pn_dflags &= ~PND_BOUND;
789 72 : return dn;
790 : }
791 :
792 : bool
793 226878 : js::DefineArg(ParseNode *pn, JSAtom *atom, unsigned i, TreeContext *tc)
794 : {
795 : /* Flag tc so we don't have to lookup arguments on every use. */
796 226878 : if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
797 0 : tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
798 :
799 : /*
800 : * Make an argument definition node, distinguished by being in tc->decls
801 : * but having PNK_NAME kind and JSOP_NOP op. Insert it in a PNK_ARGSBODY
802 : * list node returned via pn->pn_body.
803 : */
804 226878 : ParseNode *argpn = NameNode::create(PNK_NAME, atom, tc);
805 226878 : if (!argpn)
806 0 : return false;
807 226878 : JS_ASSERT(argpn->isKind(PNK_NAME) && argpn->isOp(JSOP_NOP));
808 :
809 : /* Arguments are initialized by definition. */
810 226878 : argpn->pn_dflags |= PND_INITIALIZED;
811 226878 : if (!Define(argpn, atom, tc))
812 0 : return false;
813 :
814 226878 : ParseNode *argsbody = pn->pn_body;
815 226878 : if (!argsbody) {
816 59974 : argsbody = ListNode::create(PNK_ARGSBODY, tc);
817 59974 : if (!argsbody)
818 0 : return false;
819 59974 : argsbody->setOp(JSOP_NOP);
820 59974 : argsbody->makeEmpty();
821 59974 : pn->pn_body = argsbody;
822 : }
823 226878 : argsbody->append(argpn);
824 :
825 226878 : argpn->setOp(JSOP_GETARG);
826 226878 : argpn->pn_cookie.set(tc->staticLevel, i);
827 226878 : argpn->pn_dflags |= PND_BOUND;
828 226878 : return true;
829 : }
830 :
831 : /*
832 : * Parameter block types for the several Binder functions. We use a common
833 : * helper function signature in order to share code among destructuring and
834 : * simple variable declaration parsers. In the destructuring case, the binder
835 : * function is called indirectly from the variable declaration parser by way
836 : * of CheckDestructuring and its friends.
837 : */
838 : typedef JSBool
839 : (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc);
840 :
841 : static JSBool
842 : BindLet(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc);
843 :
844 : static JSBool
845 : BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc);
846 :
847 : struct BindData {
848 251765 : BindData() : fresh(true) {}
849 :
850 : ParseNode *pn; /* name node for definition processing and
851 : error source coordinates */
852 : JSOp op; /* prolog bytecode or nop */
853 : Binder binder; /* binder, discriminates u */
854 : union {
855 : struct {
856 : VarContext varContext;
857 : StaticBlockObject *blockObj;
858 : unsigned overflow;
859 : } let;
860 : };
861 : bool fresh;
862 :
863 101332 : void initLet(VarContext varContext, StaticBlockObject &blockObj, unsigned overflow) {
864 101332 : this->pn = NULL;
865 101332 : this->op = JSOP_NOP;
866 101332 : this->binder = BindLet;
867 101332 : this->let.varContext = varContext;
868 101332 : this->let.blockObj = &blockObj;
869 101332 : this->let.overflow = overflow;
870 101332 : }
871 :
872 150352 : void initVarOrConst(JSOp op) {
873 150352 : this->op = op;
874 150352 : this->binder = BindVarOrConst;
875 150352 : }
876 : };
877 :
878 : static bool
879 121627 : BindLocalVariable(JSContext *cx, TreeContext *tc, ParseNode *pn, BindingKind kind)
880 : {
881 121627 : JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
882 :
883 : /* 'arguments' can be bound as a local only via a destructuring formal parameter. */
884 121627 : JS_ASSERT_IF(pn->pn_atom == cx->runtime->atomState.argumentsAtom, kind == VARIABLE);
885 :
886 121627 : unsigned index = tc->bindings.countVars();
887 121627 : if (!tc->bindings.add(cx, pn->pn_atom, kind))
888 0 : return false;
889 :
890 121627 : pn->pn_cookie.set(tc->staticLevel, index);
891 121627 : pn->pn_dflags |= PND_BOUND;
892 121627 : return true;
893 : }
894 :
895 : #if JS_HAS_DESTRUCTURING
896 : static JSBool
897 162 : BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc)
898 : {
899 : /* Flag tc so we don't have to lookup arguments on every use. */
900 162 : if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
901 0 : tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
902 :
903 162 : JS_ASSERT(tc->inFunction());
904 :
905 : /*
906 : * NB: Check tc->decls rather than tc->bindings, because destructuring
907 : * bindings aren't added to tc->bindings until after all arguments have
908 : * been parsed.
909 : */
910 162 : if (tc->decls.lookupFirst(atom)) {
911 : ReportCompileErrorNumber(cx, TS(tc->parser), NULL, JSREPORT_ERROR,
912 0 : JSMSG_DESTRUCT_DUP_ARG);
913 0 : return JS_FALSE;
914 : }
915 :
916 162 : ParseNode *pn = data->pn;
917 :
918 : /*
919 : * Distinguish destructured-to binding nodes as vars, not args, by setting
920 : * pn_op to JSOP_SETLOCAL. Parser::functionDef checks for this pn_op value
921 : * when processing the destructuring-assignment AST prelude induced by such
922 : * destructuring args in Parser::functionArguments.
923 : *
924 : * We must set the PND_BOUND flag too to prevent pn_op from being reset to
925 : * JSOP_SETNAME by BindDestructuringVar. The only field not initialized is
926 : * pn_cookie; it gets set in functionDef in the first "if (prelude)" block.
927 : * We have to wait to set the cookie until we can call JSFunction::addLocal
928 : * with kind = JSLOCAL_VAR, after all JSLOCAL_ARG locals have been added.
929 : *
930 : * Thus a destructuring formal parameter binds an ARG (as in arguments[i]
931 : * element) with a null atom name for the object or array passed in to be
932 : * destructured, and zero or more VARs (as in named local variables) for
933 : * the destructured-to identifiers in the property value positions within
934 : * the object or array destructuring pattern, and all ARGs for the formal
935 : * parameter list bound as locals before any VAR for a destructured name.
936 : */
937 162 : pn->setOp(JSOP_SETLOCAL);
938 162 : pn->pn_dflags |= PND_BOUND;
939 :
940 162 : return Define(pn, atom, tc);
941 : }
942 : #endif /* JS_HAS_DESTRUCTURING */
943 :
944 : JSFunction *
945 156778 : Parser::newFunction(TreeContext *tc, JSAtom *atom, FunctionSyntaxKind kind)
946 : {
947 156778 : JS_ASSERT_IF(kind == Statement, atom != NULL);
948 :
949 : /*
950 : * Find the global compilation context in order to pre-set the newborn
951 : * function's parent slot to tc->scopeChain. If the global context is a
952 : * compile-and-go one, we leave the pre-set parent intact; otherwise we
953 : * clear parent and proto.
954 : */
955 371000 : while (tc->parent)
956 57444 : tc = tc->parent;
957 :
958 313556 : RootedVarObject parent(context);
959 156778 : parent = tc->inFunction() ? NULL : tc->scopeChain();
960 :
961 : JSFunction *fun =
962 : js_NewFunction(context, NULL, NULL, 0,
963 : JSFUN_INTERPRETED | (kind == Expression ? JSFUN_LAMBDA : 0),
964 156778 : parent, atom);
965 156778 : if (fun && !tc->compileAndGo()) {
966 2837 : if (!fun->clearParent(context))
967 0 : return NULL;
968 2837 : if (!fun->clearType(context))
969 0 : return NULL;
970 2837 : fun->setEnvironment(NULL);
971 : }
972 156778 : return fun;
973 : }
974 :
975 : static JSBool
976 738575 : MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
977 : {
978 738575 : TokenKind tt = ts->peekTokenSameLine(TSF_OPERAND);
979 738575 : if (tt == TOK_ERROR)
980 0 : return JS_FALSE;
981 738575 : if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
982 : /* Advance the scanner for proper error location reporting. */
983 18 : ts->getToken(TSF_OPERAND);
984 18 : ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_SEMI_BEFORE_STMNT);
985 18 : return JS_FALSE;
986 : }
987 738557 : (void) ts->matchToken(TOK_SEMI);
988 738557 : return JS_TRUE;
989 : }
990 :
991 : static FunctionBox *
992 156778 : EnterFunction(ParseNode *fn, TreeContext *funtc, JSAtom *funAtom = NULL,
993 : FunctionSyntaxKind kind = Expression)
994 : {
995 156778 : TreeContext *tc = funtc->parent;
996 156778 : JSFunction *fun = tc->parser->newFunction(tc, funAtom, kind);
997 156778 : if (!fun)
998 0 : return NULL;
999 :
1000 : /* Create box for fun->object early to protect against last-ditch GC. */
1001 156778 : FunctionBox *funbox = tc->parser->newFunctionBox(fun, fn, tc);
1002 156778 : if (!funbox)
1003 0 : return NULL;
1004 :
1005 : /* Initialize non-default members of funtc. */
1006 156778 : funtc->flags |= funbox->tcflags;
1007 156778 : funtc->blockidGen = tc->blockidGen;
1008 156778 : if (!GenerateBlockId(funtc, funtc->bodyid))
1009 0 : return NULL;
1010 156778 : funtc->setFunction(fun);
1011 156778 : funtc->funbox = funbox;
1012 156778 : if (!SetStaticLevel(funtc, tc->staticLevel + 1))
1013 0 : return NULL;
1014 :
1015 156778 : return funbox;
1016 : }
1017 :
1018 : static bool
1019 7577 : DeoptimizeUsesWithin(Definition *dn, const TokenPos &pos)
1020 : {
1021 7577 : unsigned ndeoptimized = 0;
1022 :
1023 107900 : for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1024 100323 : JS_ASSERT(pnu->isUsed());
1025 100323 : JS_ASSERT(!pnu->isDefn());
1026 100323 : if (pnu->pn_pos.begin >= pos.begin && pnu->pn_pos.end <= pos.end) {
1027 99909 : pnu->pn_dflags |= PND_DEOPTIMIZED;
1028 99909 : ++ndeoptimized;
1029 : }
1030 : }
1031 :
1032 7577 : return ndeoptimized != 0;
1033 : }
1034 :
1035 : static bool
1036 156769 : LeaveFunction(ParseNode *fn, TreeContext *funtc, PropertyName *funName = NULL,
1037 : FunctionSyntaxKind kind = Expression)
1038 : {
1039 156769 : TreeContext *tc = funtc->parent;
1040 156769 : tc->blockidGen = funtc->blockidGen;
1041 :
1042 156769 : FunctionBox *funbox = fn->pn_funbox;
1043 156769 : funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO | TCF_RETURN_EXPR);
1044 :
1045 156769 : fn->pn_dflags |= PND_INITIALIZED;
1046 156769 : if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
1047 155815 : fn->pn_dflags |= PND_BLOCKCHILD;
1048 :
1049 : /*
1050 : * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
1051 : * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
1052 : * params and body. We do this only if there are lexical dependencies not
1053 : * satisfied by the function's declarations, to avoid penalizing functions
1054 : * that use only their arguments and other local bindings.
1055 : */
1056 156769 : if (funtc->lexdeps->count()) {
1057 61321 : int foundCallee = 0;
1058 :
1059 155794 : for (AtomDefnRange r = funtc->lexdeps->all(); !r.empty(); r.popFront()) {
1060 94473 : JSAtom *atom = r.front().key();
1061 94473 : Definition *dn = r.front().value();
1062 94473 : JS_ASSERT(dn->isPlaceholder());
1063 :
1064 94473 : if (atom == funName && kind == Expression) {
1065 450 : dn->setOp(JSOP_CALLEE);
1066 450 : dn->pn_cookie.set(funtc->staticLevel, UpvarCookie::CALLEE_SLOT);
1067 450 : dn->pn_dflags |= PND_BOUND;
1068 450 : foundCallee = 1;
1069 450 : continue;
1070 : }
1071 :
1072 94023 : Definition *outer_dn = tc->decls.lookupFirst(atom);
1073 :
1074 : /*
1075 : * Make sure to deoptimize lexical dependencies that are polluted
1076 : * by eval or with, to safely bind globals (see bug 561923).
1077 : */
1078 94086 : if (funtc->callsEval() ||
1079 : (outer_dn && tc->innermostWith &&
1080 63 : outer_dn->pn_pos < tc->innermostWith->pn_pos)) {
1081 6659 : DeoptimizeUsesWithin(dn, fn->pn_pos);
1082 : }
1083 :
1084 94023 : if (!outer_dn) {
1085 132552 : AtomDefnAddPtr p = tc->lexdeps->lookupForAdd(atom);
1086 66276 : if (p) {
1087 10837 : outer_dn = p.value();
1088 : } else {
1089 : /*
1090 : * Create a new placeholder for our outer lexdep. We could
1091 : * simply re-use the inner placeholder, but that introduces
1092 : * subtleties in the case where we find a later definition
1093 : * that captures an existing lexdep. For example:
1094 : *
1095 : * function f() { function g() { x; } let x; }
1096 : *
1097 : * Here, g's TOK_UPVARS node lists the placeholder for x,
1098 : * which must be captured by the 'let' declaration later,
1099 : * since 'let's are hoisted. Taking g's placeholder as our
1100 : * own would work fine. But consider:
1101 : *
1102 : * function f() { x; { function g() { x; } let x; } }
1103 : *
1104 : * Here, the 'let' must not capture all the uses of f's
1105 : * lexdep entry for x, but it must capture the x node
1106 : * referred to from g's TOK_UPVARS node. Always turning
1107 : * inherited lexdeps into uses of a new outer definition
1108 : * allows us to handle both these cases in a natural way.
1109 : */
1110 55439 : outer_dn = MakePlaceholder(dn, tc);
1111 55439 : if (!outer_dn || !tc->lexdeps->add(p, atom, outer_dn))
1112 0 : return false;
1113 : }
1114 : }
1115 :
1116 : /*
1117 : * Insert dn's uses list at the front of outer_dn's list.
1118 : *
1119 : * Without loss of generality or correctness, we allow a dn to
1120 : * be in inner and outer lexdeps, since the purpose of lexdeps
1121 : * is one-pass coordination of name use and definition across
1122 : * functions, and if different dn's are used we'll merge lists
1123 : * when leaving the inner function.
1124 : *
1125 : * The dn == outer_dn case arises with generator expressions
1126 : * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
1127 : * case), and nowhere else, currently.
1128 : */
1129 94023 : if (dn != outer_dn) {
1130 94023 : ParseNode **pnup = &dn->dn_uses;
1131 : ParseNode *pnu;
1132 :
1133 434053 : while ((pnu = *pnup) != NULL) {
1134 246007 : pnu->pn_lexdef = outer_dn;
1135 246007 : pnup = &pnu->pn_link;
1136 : }
1137 :
1138 : /*
1139 : * Make dn be a use that redirects to outer_dn, because we
1140 : * can't replace dn with outer_dn in all the pn_namesets in
1141 : * the AST where it may be. Instead we make it forward to
1142 : * outer_dn. See Definition::resolve.
1143 : */
1144 94023 : *pnup = outer_dn->dn_uses;
1145 94023 : outer_dn->dn_uses = dn;
1146 94023 : outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
1147 94023 : dn->setDefn(false);
1148 94023 : dn->setUsed(true);
1149 94023 : dn->pn_lexdef = outer_dn;
1150 : }
1151 :
1152 : /* Mark the outer dn as escaping. */
1153 94023 : outer_dn->pn_dflags |= PND_CLOSED;
1154 : }
1155 :
1156 61321 : if (funtc->lexdeps->count() - foundCallee != 0) {
1157 61222 : ParseNode *body = fn->pn_body;
1158 :
1159 61222 : fn->pn_body = NameSetNode::create(PNK_UPVARS, tc);
1160 61222 : if (!fn->pn_body)
1161 0 : return false;
1162 :
1163 61222 : fn->pn_body->pn_pos = body->pn_pos;
1164 61222 : if (foundCallee)
1165 351 : funtc->lexdeps->remove(funName);
1166 : /* Transfer ownership of the lexdep map to the parse node. */
1167 61222 : fn->pn_body->pn_names = funtc->lexdeps;
1168 61222 : funtc->lexdeps.clearMap();
1169 61222 : fn->pn_body->pn_tree = body;
1170 : } else {
1171 99 : funtc->lexdeps.releaseMap(funtc->parser->context);
1172 : }
1173 :
1174 : }
1175 :
1176 : /*
1177 : * Check whether any parameters have been assigned within this function.
1178 : * In strict mode parameters do not alias arguments[i], and to make the
1179 : * arguments object reflect initial parameter values prior to any mutation
1180 : * we create it eagerly whenever parameters are (or might, in the case of
1181 : * calls to eval) be assigned.
1182 : */
1183 156769 : if (funtc->inStrictMode() && funbox->object->toFunction()->nargs > 0) {
1184 315 : AtomDeclsIter iter(&funtc->decls);
1185 : Definition *dn;
1186 :
1187 315 : while ((dn = iter()) != NULL) {
1188 486 : if (dn->kind() == Definition::ARG && dn->isAssigned()) {
1189 54 : funbox->tcflags |= TCF_FUN_MUTATES_PARAMETER;
1190 54 : break;
1191 : }
1192 : }
1193 : }
1194 :
1195 156769 : funbox->bindings.transfer(funtc->parser->context, &funtc->bindings);
1196 :
1197 156769 : return true;
1198 : }
1199 :
1200 : static bool
1201 : DefineGlobal(ParseNode *pn, BytecodeEmitter *bce, PropertyName *name);
1202 :
1203 : /*
1204 : * FIXME? this Parser method was factored from Parser::functionDef with minimal
1205 : * change, hence the funtc ref param and funbox. It probably should match
1206 : * functionBody, etc., and use tc and tc->funbox instead of taking explicit
1207 : * parameters.
1208 : */
1209 : bool
1210 156571 : Parser::functionArguments(TreeContext &funtc, FunctionBox *funbox, ParseNode **listp)
1211 : {
1212 156571 : if (tokenStream.getToken() != TOK_LP) {
1213 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_BEFORE_FORMAL);
1214 0 : return false;
1215 : }
1216 :
1217 156571 : if (!tokenStream.matchToken(TOK_RP)) {
1218 : #if JS_HAS_DESTRUCTURING
1219 55088 : JSAtom *duplicatedArg = NULL;
1220 55088 : bool destructuringArg = false;
1221 55088 : ParseNode *list = NULL;
1222 : #endif
1223 :
1224 222046 : do {
1225 222046 : switch (TokenKind tt = tokenStream.getToken()) {
1226 : #if JS_HAS_DESTRUCTURING
1227 : case TOK_LB:
1228 : case TOK_LC:
1229 : {
1230 : /* See comment below in the TOK_NAME case. */
1231 81 : if (duplicatedArg)
1232 0 : goto report_dup_and_destructuring;
1233 81 : destructuringArg = true;
1234 :
1235 : /*
1236 : * A destructuring formal parameter turns into one or more
1237 : * local variables initialized from properties of a single
1238 : * anonymous positional parameter, so here we must tweak our
1239 : * binder and its data.
1240 : */
1241 81 : BindData data;
1242 81 : data.pn = NULL;
1243 81 : data.op = JSOP_DEFVAR;
1244 81 : data.binder = BindDestructuringArg;
1245 81 : ParseNode *lhs = destructuringExpr(&data, tt);
1246 81 : if (!lhs)
1247 0 : return false;
1248 :
1249 : /*
1250 : * Adjust fun->nargs to count the single anonymous positional
1251 : * parameter that is to be destructured.
1252 : */
1253 : uint16_t slot;
1254 81 : if (!funtc.bindings.addDestructuring(context, &slot))
1255 0 : return false;
1256 :
1257 : /*
1258 : * Synthesize a destructuring assignment from the single
1259 : * anonymous positional parameter into the destructuring
1260 : * left-hand-side expression and accumulate it in list.
1261 : */
1262 : ParseNode *rhs =
1263 81 : NameNode::create(PNK_NAME, context->runtime->atomState.emptyAtom, &funtc);
1264 81 : if (!rhs)
1265 0 : return false;
1266 81 : rhs->setOp(JSOP_GETARG);
1267 81 : rhs->pn_cookie.set(funtc.staticLevel, slot);
1268 81 : rhs->pn_dflags |= PND_BOUND;
1269 :
1270 81 : ParseNode *item = new_<BinaryNode>(PNK_ASSIGN, JSOP_NOP, lhs->pn_pos, lhs, rhs);
1271 81 : if (!item)
1272 0 : return false;
1273 81 : if (!list) {
1274 45 : list = ListNode::create(PNK_VAR, &funtc);
1275 45 : if (!list)
1276 0 : return false;
1277 45 : list->makeEmpty();
1278 45 : *listp = list;
1279 : }
1280 81 : list->append(item);
1281 81 : break;
1282 : }
1283 : #endif /* JS_HAS_DESTRUCTURING */
1284 :
1285 : case TOK_NAME:
1286 : {
1287 221965 : PropertyName *name = tokenStream.currentToken().name();
1288 :
1289 : #ifdef JS_HAS_DESTRUCTURING
1290 : /*
1291 : * ECMA-262 requires us to support duplicate parameter names,
1292 : * but if the parameter list includes destructuring, we
1293 : * consider the code to have "opted in" to higher standards and
1294 : * forbid duplicates. We may see a destructuring parameter
1295 : * later, so always note duplicates now.
1296 : *
1297 : * Duplicates are warned about (strict option) or cause errors
1298 : * (strict mode code), but we do those tests in one place
1299 : * below, after having parsed the body in case it begins with a
1300 : * "use strict"; directive.
1301 : *
1302 : * NB: Check funtc.decls rather than funtc.bindings, because
1303 : * destructuring bindings aren't added to funtc.bindings
1304 : * until after all arguments have been parsed.
1305 : */
1306 221965 : if (funtc.decls.lookupFirst(name)) {
1307 18 : funtc.bindings.noteDup();
1308 18 : duplicatedArg = name;
1309 18 : if (destructuringArg)
1310 0 : goto report_dup_and_destructuring;
1311 : }
1312 : #endif
1313 :
1314 : uint16_t slot;
1315 221965 : if (!funtc.bindings.addArgument(context, name, &slot))
1316 0 : return false;
1317 221965 : if (!DefineArg(funbox->node, name, slot, &funtc))
1318 0 : return false;
1319 221965 : break;
1320 : }
1321 :
1322 : default:
1323 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_MISSING_FORMAL);
1324 : /* FALL THROUGH */
1325 : case TOK_ERROR:
1326 0 : return false;
1327 :
1328 : #if JS_HAS_DESTRUCTURING
1329 : report_dup_and_destructuring:
1330 0 : Definition *dn = funtc.decls.lookupFirst(duplicatedArg);
1331 0 : reportErrorNumber(dn, JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
1332 0 : return false;
1333 : #endif
1334 : }
1335 222046 : } while (tokenStream.matchToken(TOK_COMMA));
1336 :
1337 55088 : if (tokenStream.getToken() != TOK_RP) {
1338 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_AFTER_FORMAL);
1339 0 : return false;
1340 : }
1341 : }
1342 :
1343 156571 : return true;
1344 : }
1345 :
1346 : ParseNode *
1347 156571 : Parser::functionDef(PropertyName *funName, FunctionType type, FunctionSyntaxKind kind)
1348 : {
1349 156571 : JS_ASSERT_IF(kind == Statement, funName);
1350 :
1351 : /* Make a TOK_FUNCTION node. */
1352 156571 : ParseNode *pn = FunctionNode::create(PNK_FUNCTION, tc);
1353 156571 : if (!pn)
1354 0 : return NULL;
1355 156571 : pn->pn_body = NULL;
1356 156571 : pn->pn_cookie.makeFree();
1357 156571 : pn->pn_dflags = 0;
1358 :
1359 : /*
1360 : * Record names for function statements in tc->decls so we know when to
1361 : * avoid optimizing variable references that might name a function.
1362 : */
1363 156571 : bool bodyLevel = tc->atBodyLevel();
1364 156571 : if (kind == Statement) {
1365 28621 : if (Definition *dn = tc->decls.lookupFirst(funName)) {
1366 81 : Definition::Kind dn_kind = dn->kind();
1367 :
1368 81 : JS_ASSERT(!dn->isUsed());
1369 81 : JS_ASSERT(dn->isDefn());
1370 :
1371 81 : if (context->hasStrictOption() || dn_kind == Definition::CONST) {
1372 0 : JSAutoByteString name;
1373 0 : if (!js_AtomToPrintableString(context, funName, &name) ||
1374 : !reportErrorNumber(NULL,
1375 : (dn_kind != Definition::CONST)
1376 : ? JSREPORT_WARNING | JSREPORT_STRICT
1377 : : JSREPORT_ERROR,
1378 : JSMSG_REDECLARED_VAR,
1379 : Definition::kindString(dn_kind),
1380 0 : name.ptr())) {
1381 0 : return NULL;
1382 : }
1383 : }
1384 :
1385 81 : if (bodyLevel) {
1386 72 : tc->decls.updateFirst(funName, (Definition *) pn);
1387 72 : pn->setDefn(true);
1388 72 : pn->dn_uses = dn; /* dn->dn_uses is now pn_link */
1389 :
1390 72 : if (!MakeDefIntoUse(dn, pn, funName, tc))
1391 0 : return NULL;
1392 : }
1393 28540 : } else if (bodyLevel) {
1394 : /*
1395 : * If this function was used before it was defined, claim the
1396 : * pre-created definition node for this function that primaryExpr
1397 : * put in tc->lexdeps on first forward reference, and recycle pn.
1398 : */
1399 :
1400 28198 : if (Definition *fn = tc->lexdeps.lookupDefn(funName)) {
1401 1683 : JS_ASSERT(fn->isDefn());
1402 1683 : fn->setKind(PNK_FUNCTION);
1403 1683 : fn->setArity(PN_FUNC);
1404 1683 : fn->pn_pos.begin = pn->pn_pos.begin;
1405 :
1406 : /*
1407 : * Set fn->pn_pos.end too, in case of error before we parse the
1408 : * closing brace. See bug 640075.
1409 : */
1410 1683 : fn->pn_pos.end = pn->pn_pos.end;
1411 :
1412 1683 : fn->pn_body = NULL;
1413 1683 : fn->pn_cookie.makeFree();
1414 :
1415 1683 : tc->lexdeps->remove(funName);
1416 1683 : freeTree(pn);
1417 1683 : pn = fn;
1418 : }
1419 :
1420 28198 : if (!Define(pn, funName, tc))
1421 0 : return NULL;
1422 : }
1423 :
1424 : /*
1425 : * A function directly inside another's body needs only a local
1426 : * variable to bind its name to its value, and not an activation object
1427 : * property (it might also need the activation property, if the outer
1428 : * function contains with statements, e.g., but the stack slot wins
1429 : * when BytecodeEmitter.cpp's BindNameToSlot can optimize a JSOP_NAME
1430 : * into a JSOP_GETLOCAL bytecode).
1431 : */
1432 28621 : if (bodyLevel && tc->inFunction()) {
1433 : /*
1434 : * Define a local in the outer function so that BindNameToSlot
1435 : * can properly optimize accesses. Note that we need a local
1436 : * variable, not an argument, for the function statement. Thus
1437 : * we add a variable even if a parameter with the given name
1438 : * already exists.
1439 : */
1440 : unsigned index;
1441 3771 : switch (tc->bindings.lookup(context, funName, &index)) {
1442 : case NONE:
1443 : case ARGUMENT:
1444 3771 : index = tc->bindings.countVars();
1445 3771 : if (!tc->bindings.addVariable(context, funName))
1446 0 : return NULL;
1447 : /* FALL THROUGH */
1448 :
1449 : case VARIABLE:
1450 3771 : pn->pn_cookie.set(tc->staticLevel, index);
1451 3771 : pn->pn_dflags |= PND_BOUND;
1452 3771 : break;
1453 :
1454 : default:;
1455 : }
1456 : }
1457 : }
1458 :
1459 156571 : TreeContext *outertc = tc;
1460 :
1461 : /* Initialize early for possible flags mutation via destructuringExpr. */
1462 313142 : TreeContext funtc(tc->parser);
1463 156571 : if (!funtc.init(context))
1464 0 : return NULL;
1465 :
1466 156571 : FunctionBox *funbox = EnterFunction(pn, &funtc, funName, kind);
1467 156571 : if (!funbox)
1468 0 : return NULL;
1469 :
1470 156571 : JSFunction *fun = funbox->function();
1471 :
1472 : /* Now parse formal argument list and compute fun->nargs. */
1473 156571 : ParseNode *prelude = NULL;
1474 156571 : if (!functionArguments(funtc, funbox, &prelude))
1475 0 : return NULL;
1476 :
1477 156571 : fun->setArgCount(funtc.bindings.countArgs());
1478 :
1479 : #if JS_HAS_DESTRUCTURING
1480 : /*
1481 : * If there were destructuring formal parameters, bind the destructured-to
1482 : * local variables now that we've parsed all the regular and destructuring
1483 : * formal parameters. Because js::Bindings::add must be called first for
1484 : * all ARGUMENTs, then all VARIABLEs and CONSTANTs, and finally all UPVARs,
1485 : * we can't bind vars induced by formal parameter destructuring until after
1486 : * Parser::functionArguments has returned.
1487 : */
1488 156571 : if (prelude) {
1489 45 : AtomDeclsIter iter(&funtc.decls);
1490 :
1491 423 : while (Definition *apn = iter()) {
1492 : /* Filter based on pn_op -- see BindDestructuringArg, above. */
1493 189 : if (!apn->isOp(JSOP_SETLOCAL))
1494 27 : continue;
1495 :
1496 162 : if (!BindLocalVariable(context, &funtc, apn, VARIABLE))
1497 0 : return NULL;
1498 : }
1499 : }
1500 : #endif
1501 :
1502 156571 : if (type == Getter && fun->nargs > 0) {
1503 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ACCESSOR_WRONG_ARGS,
1504 0 : "getter", "no", "s");
1505 0 : return NULL;
1506 : }
1507 156571 : if (type == Setter && fun->nargs != 1) {
1508 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ACCESSOR_WRONG_ARGS,
1509 0 : "setter", "one", "");
1510 0 : return NULL;
1511 : }
1512 :
1513 156571 : FunctionBodyType bodyType = StatementListBody;
1514 : #if JS_HAS_EXPR_CLOSURES
1515 156571 : if (tokenStream.getToken(TSF_OPERAND) != TOK_LC) {
1516 1764 : tokenStream.ungetToken();
1517 1764 : fun->flags |= JSFUN_EXPR_CLOSURE;
1518 1764 : bodyType = ExpressionBody;
1519 : }
1520 : #else
1521 : MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
1522 : #endif
1523 :
1524 156571 : ParseNode *body = functionBody(bodyType);
1525 156571 : if (!body)
1526 9 : return NULL;
1527 :
1528 156562 : if (funName && !CheckStrictBinding(context, &funtc, funName, pn))
1529 0 : return NULL;
1530 :
1531 156562 : if (!CheckStrictParameters(context, &funtc))
1532 0 : return NULL;
1533 :
1534 : #if JS_HAS_EXPR_CLOSURES
1535 156562 : if (bodyType == StatementListBody)
1536 154798 : MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1537 1764 : else if (kind == Statement && !MatchOrInsertSemicolon(context, &tokenStream))
1538 0 : return NULL;
1539 : #else
1540 : MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1541 : #endif
1542 156562 : pn->pn_pos.end = tokenStream.currentToken().pos.end;
1543 :
1544 : /*
1545 : * Fruit of the poisonous tree: if a closure calls eval, we consider the
1546 : * parent to call eval. We need this for two reasons: (1) the Jaegermonkey
1547 : * optimizations really need to know if eval is called transitively, and
1548 : * (2) in strict mode, eval called transitively requires eager argument
1549 : * creation in strict mode parent functions.
1550 : *
1551 : * For the latter, we really only need to propagate callsEval if both
1552 : * functions are strict mode, but we don't lose much by always propagating.
1553 : * The only optimization we lose this way is in the case where a function
1554 : * is strict, does not mutate arguments, does not call eval directly, but
1555 : * calls eval transitively.
1556 : */
1557 156562 : if (funtc.callsEval())
1558 3535 : outertc->noteCallsEval();
1559 :
1560 : #if JS_HAS_DESTRUCTURING
1561 : /*
1562 : * If there were destructuring formal parameters, prepend the initializing
1563 : * comma expression that we synthesized to body. If the body is a return
1564 : * node, we must make a special PNK_SEQ node, to prepend the destructuring
1565 : * code without bracing the decompilation of the function body.
1566 : */
1567 156562 : if (prelude) {
1568 45 : if (!body->isArity(PN_LIST)) {
1569 : ParseNode *block;
1570 :
1571 0 : block = ListNode::create(PNK_SEQ, outertc);
1572 0 : if (!block)
1573 0 : return NULL;
1574 0 : block->pn_pos = body->pn_pos;
1575 0 : block->initList(body);
1576 :
1577 0 : body = block;
1578 : }
1579 :
1580 45 : ParseNode *item = UnaryNode::create(PNK_SEMI, outertc);
1581 45 : if (!item)
1582 0 : return NULL;
1583 :
1584 45 : item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
1585 45 : item->pn_kid = prelude;
1586 45 : item->pn_next = body->pn_head;
1587 45 : body->pn_head = item;
1588 45 : if (body->pn_tail == &body->pn_head)
1589 27 : body->pn_tail = &item->pn_next;
1590 45 : ++body->pn_count;
1591 45 : body->pn_xflags |= PNX_DESTRUCT;
1592 : }
1593 : #endif
1594 :
1595 : /*
1596 : * If we collected flags that indicate nested heavyweight functions, or
1597 : * this function contains heavyweight-making statements (with statement,
1598 : * visible eval call, or assignment to 'arguments'), flag the function as
1599 : * heavyweight (requiring a call object per invocation).
1600 : */
1601 156562 : if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
1602 5623 : fun->flags |= JSFUN_HEAVYWEIGHT;
1603 5623 : outertc->flags |= TCF_FUN_HEAVYWEIGHT;
1604 : }
1605 :
1606 156562 : JSOp op = JSOP_NOP;
1607 156562 : if (kind == Expression) {
1608 127941 : op = JSOP_LAMBDA;
1609 : } else {
1610 28621 : if (!bodyLevel) {
1611 : /*
1612 : * Extension: in non-strict mode code, a function statement not at
1613 : * the top level of a function body or whole program, e.g., in a
1614 : * compound statement such as the "then" part of an "if" statement,
1615 : * binds a closure only if control reaches that sub-statement.
1616 : */
1617 351 : JS_ASSERT(!outertc->inStrictMode());
1618 351 : op = JSOP_DEFFUN;
1619 351 : outertc->noteMightAliasLocals();
1620 351 : outertc->noteHasExtensibleScope();
1621 351 : outertc->flags |= TCF_FUN_HEAVYWEIGHT;
1622 351 : if (fun->atom == context->runtime->atomState.argumentsAtom)
1623 9 : outertc->noteLocalOverwritesArguments();
1624 : }
1625 : }
1626 :
1627 156562 : funbox->kids = funtc.functionList;
1628 :
1629 156562 : pn->pn_funbox = funbox;
1630 156562 : pn->setOp(op);
1631 156562 : if (pn->pn_body) {
1632 55070 : pn->pn_body->append(body);
1633 55070 : pn->pn_body->pn_pos = body->pn_pos;
1634 : } else {
1635 101492 : pn->pn_body = body;
1636 : }
1637 :
1638 156562 : if (!outertc->inFunction() && bodyLevel && kind == Statement && outertc->compiling()) {
1639 24499 : JS_ASSERT(pn->pn_cookie.isFree());
1640 24499 : if (!DefineGlobal(pn, outertc->asBytecodeEmitter(), funName))
1641 0 : return NULL;
1642 : }
1643 :
1644 156562 : pn->pn_blockid = outertc->blockid();
1645 :
1646 156562 : if (!LeaveFunction(pn, &funtc, funName, kind))
1647 0 : return NULL;
1648 :
1649 : /* If the surrounding function is not strict code, reset the lexer. */
1650 156562 : if (!outertc->inStrictMode())
1651 156256 : tokenStream.setStrictMode(false);
1652 :
1653 156562 : return pn;
1654 : }
1655 :
1656 : ParseNode *
1657 28630 : Parser::functionStmt()
1658 : {
1659 28630 : PropertyName *name = NULL;
1660 28630 : if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME) {
1661 28621 : name = tokenStream.currentToken().name();
1662 : } else {
1663 : /* Unnamed function expressions are forbidden in statement context. */
1664 9 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_UNNAMED_FUNCTION_STMT);
1665 9 : return NULL;
1666 : }
1667 :
1668 : /* We forbid function statements in strict mode code. */
1669 28972 : if (!tc->atBodyLevel() && tc->inStrictMode()) {
1670 0 : reportErrorNumber(NULL, JSREPORT_STRICT_MODE_ERROR, JSMSG_STRICT_FUNCTION_STATEMENT);
1671 0 : return NULL;
1672 : }
1673 :
1674 28621 : return functionDef(name, Normal, Statement);
1675 : }
1676 :
1677 : ParseNode *
1678 127653 : Parser::functionExpr()
1679 : {
1680 127653 : PropertyName *name = NULL;
1681 127653 : if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME)
1682 6112 : name = tokenStream.currentToken().name();
1683 : else
1684 121541 : tokenStream.ungetToken();
1685 127653 : return functionDef(name, Normal, Expression);
1686 : }
1687 :
1688 : /*
1689 : * Recognize Directive Prologue members and directives. Assuming |pn| is a
1690 : * candidate for membership in a directive prologue, recognize directives and
1691 : * set |tc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
1692 : * |pn_prologue| flag.
1693 : *
1694 : * Note that the following is a strict mode function:
1695 : *
1696 : * function foo() {
1697 : * "blah" // inserted semi colon
1698 : * "blurgh"
1699 : * "use\x20loose"
1700 : * "use strict"
1701 : * }
1702 : *
1703 : * That is, even though "use\x20loose" can never be a directive, now or in the
1704 : * future (because of the hex escape), the Directive Prologue extends through it
1705 : * to the "use strict" statement, which is indeed a directive.
1706 : */
1707 : bool
1708 183879 : Parser::recognizeDirectivePrologue(ParseNode *pn, bool *isDirectivePrologueMember)
1709 : {
1710 183879 : *isDirectivePrologueMember = pn->isStringExprStatement();
1711 183879 : if (!*isDirectivePrologueMember)
1712 183043 : return true;
1713 :
1714 836 : ParseNode *kid = pn->pn_kid;
1715 836 : if (kid->isEscapeFreeStringLiteral()) {
1716 : /*
1717 : * Mark this statement as being a possibly legitimate part of a
1718 : * directive prologue, so the byte code emitter won't warn about it
1719 : * being useless code. (We mustn't just omit the statement entirely yet,
1720 : * as it could be producing the value of an eval or JSScript execution.)
1721 : *
1722 : * Note that even if the string isn't one we recognize as a directive,
1723 : * the emitter still shouldn't flag it as useless, as it could become a
1724 : * directive in the future. We don't want to interfere with people
1725 : * taking advantage of directive-prologue-enabled features that appear
1726 : * in other browsers first.
1727 : */
1728 836 : pn->pn_prologue = true;
1729 :
1730 836 : JSAtom *directive = kid->pn_atom;
1731 836 : if (directive == context->runtime->atomState.useStrictAtom) {
1732 : /*
1733 : * Unfortunately, Directive Prologue members in general may contain
1734 : * escapes, even while "use strict" directives may not. Therefore
1735 : * we must check whether an octal character escape has been seen in
1736 : * any previous directives whenever we encounter a "use strict"
1737 : * directive, so that the octal escape is properly treated as a
1738 : * syntax error. An example of this case:
1739 : *
1740 : * function error()
1741 : * {
1742 : * "\145"; // octal escape
1743 : * "use strict"; // retroactively makes "\145" a syntax error
1744 : * }
1745 : */
1746 687 : if (tokenStream.hasOctalCharacterEscape()) {
1747 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_DEPRECATED_OCTAL);
1748 0 : return false;
1749 : }
1750 :
1751 687 : tc->flags |= TCF_STRICT_MODE_CODE;
1752 687 : tokenStream.setStrictMode();
1753 : }
1754 : }
1755 836 : return true;
1756 : }
1757 :
1758 : /*
1759 : * Parse the statements in a block, creating a TOK_LC node that lists the
1760 : * statements' trees. If called from block-parsing code, the caller must
1761 : * match { before and } after.
1762 : */
1763 : ParseNode *
1764 443902 : Parser::statements()
1765 : {
1766 443902 : JS_CHECK_RECURSION(context, return NULL);
1767 :
1768 443902 : ParseNode *pn = ListNode::create(PNK_STATEMENTLIST, tc);
1769 443902 : if (!pn)
1770 0 : return NULL;
1771 443902 : pn->makeEmpty();
1772 443902 : pn->pn_blockid = tc->blockid();
1773 443902 : ParseNode *saveBlock = tc->blockNode;
1774 443902 : tc->blockNode = pn;
1775 :
1776 443902 : bool inDirectivePrologue = tc->atBodyLevel();
1777 443902 : tokenStream.setOctalCharacterEscape(false);
1778 489628 : for (;;) {
1779 933530 : TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
1780 933530 : if (tt <= TOK_EOF || tt == TOK_RC) {
1781 443531 : if (tt == TOK_ERROR) {
1782 0 : if (tokenStream.isEOF())
1783 0 : tokenStream.setUnexpectedEOF();
1784 0 : return NULL;
1785 : }
1786 : break;
1787 : }
1788 489999 : ParseNode *next = statement();
1789 489999 : if (!next) {
1790 371 : if (tokenStream.isEOF())
1791 11 : tokenStream.setUnexpectedEOF();
1792 371 : return NULL;
1793 : }
1794 :
1795 489628 : if (inDirectivePrologue && !recognizeDirectivePrologue(next, &inDirectivePrologue))
1796 0 : return NULL;
1797 :
1798 489628 : if (next->isKind(PNK_FUNCTION)) {
1799 : /*
1800 : * PNX_FUNCDEFS notifies the emitter that the block contains body-
1801 : * level function definitions that should be processed before the
1802 : * rest of nodes.
1803 : *
1804 : * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
1805 : * is relevant only for function definitions not at body-level,
1806 : * which we call function statements.
1807 : */
1808 4086 : if (tc->atBodyLevel()) {
1809 3771 : pn->pn_xflags |= PNX_FUNCDEFS;
1810 : } else {
1811 : /*
1812 : * General deoptimization was done in functionDef, here we just
1813 : * need to tell TOK_LC in Parser::statement to add braces.
1814 : */
1815 315 : JS_ASSERT(tc->hasExtensibleScope());
1816 315 : tc->flags |= TCF_HAS_FUNCTION_STMT;
1817 : }
1818 : }
1819 489628 : pn->append(next);
1820 : }
1821 :
1822 : /*
1823 : * Handle the case where there was a let declaration under this block. If
1824 : * it replaced tc->blockNode with a new block node then we must refresh pn
1825 : * and then restore tc->blockNode.
1826 : */
1827 443531 : if (tc->blockNode != pn)
1828 1755 : pn = tc->blockNode;
1829 443531 : tc->blockNode = saveBlock;
1830 :
1831 443531 : pn->pn_pos.end = tokenStream.currentToken().pos.end;
1832 443531 : JS_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
1833 443531 : return pn;
1834 : }
1835 :
1836 : ParseNode *
1837 95696 : Parser::condition()
1838 : {
1839 95696 : MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1840 95696 : ParseNode *pn = parenExpr();
1841 95696 : if (!pn)
1842 0 : return NULL;
1843 95696 : MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1844 :
1845 : /* Check for (a = b) and warn about possible (a == b) mistype. */
1846 95696 : JS_ASSERT_IF(pn->isKind(PNK_ASSIGN), pn->isOp(JSOP_NOP));
1847 95894 : if (pn->isKind(PNK_ASSIGN) &&
1848 99 : !pn->isInParens() &&
1849 99 : !reportErrorNumber(NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_EQUAL_AS_ASSIGN))
1850 : {
1851 0 : return NULL;
1852 : }
1853 95696 : return pn;
1854 : }
1855 :
1856 : static bool
1857 1881 : MatchLabel(JSContext *cx, TokenStream *ts, PropertyName **label)
1858 : {
1859 1881 : TokenKind tt = ts->peekTokenSameLine(TSF_OPERAND);
1860 1881 : if (tt == TOK_ERROR)
1861 0 : return false;
1862 1881 : if (tt == TOK_NAME) {
1863 144 : (void) ts->getToken();
1864 144 : *label = ts->currentToken().name();
1865 : } else {
1866 1737 : *label = NULL;
1867 : }
1868 1881 : return true;
1869 : }
1870 :
1871 : static bool
1872 189 : ReportRedeclaration(JSContext *cx, TreeContext *tc, ParseNode *pn, bool isConst, JSAtom *atom)
1873 : {
1874 378 : JSAutoByteString name;
1875 189 : if (js_AtomToPrintableString(cx, atom, &name)) {
1876 : ReportCompileErrorNumber(cx, TS(tc->parser), pn,
1877 : JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
1878 : isConst ? "const" : "variable",
1879 189 : name.ptr());
1880 : }
1881 189 : return false;
1882 : }
1883 :
1884 : /*
1885 : * Define a let-variable in a block, let-expression, or comprehension scope. tc
1886 : * must already be in such a scope.
1887 : *
1888 : * Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a
1889 : * property for the new variable on the block object, tc->blockChain;
1890 : * populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to
1891 : * data->pn in a slot of the block object.
1892 : */
1893 : static JSBool
1894 323507 : BindLet(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc)
1895 : {
1896 323507 : ParseNode *pn = data->pn;
1897 323507 : if (!CheckStrictBinding(cx, tc, atom->asPropertyName(), pn))
1898 0 : return false;
1899 :
1900 323507 : StaticBlockObject &blockObj = *data->let.blockObj;
1901 323507 : unsigned blockCount = blockObj.slotCount();
1902 323507 : if (blockCount == JS_BIT(16)) {
1903 : ReportCompileErrorNumber(cx, TS(tc->parser), pn,
1904 0 : JSREPORT_ERROR, data->let.overflow);
1905 0 : return false;
1906 : }
1907 :
1908 : /*
1909 : * For bindings that are hoisted to the beginning of the block/function,
1910 : * Define() right now. For the rest, delay Define() until PushLetScope.
1911 : */
1912 323507 : if (data->let.varContext == HoistVars) {
1913 163827 : JS_ASSERT(!tc->atBodyLevel());
1914 163827 : Definition *dn = tc->decls.lookupFirst(atom);
1915 163827 : if (dn && dn->pn_blockid == tc->blockid())
1916 18 : return ReportRedeclaration(cx, tc, pn, dn->isConst(), atom);
1917 163809 : if (!Define(pn, atom, tc, true))
1918 0 : return false;
1919 : }
1920 :
1921 : /*
1922 : * Assign block-local index to pn->pn_cookie right away, encoding it as an
1923 : * upvar cookie whose skip tells the current static level. The emitter will
1924 : * adjust the node's slot based on its stack depth model -- and, for global
1925 : * and eval code, js::frontend::CompileScript will adjust the slot
1926 : * again to include script->nfixed.
1927 : */
1928 323489 : pn->setOp(JSOP_GETLOCAL);
1929 323489 : pn->pn_cookie.set(tc->staticLevel, uint16_t(blockCount));
1930 323489 : pn->pn_dflags |= PND_LET | PND_BOUND;
1931 :
1932 : /*
1933 : * Define the let binding's property before storing pn in the the binding's
1934 : * slot indexed by blockCount off the class-reserved slot base.
1935 : */
1936 : bool redeclared;
1937 323489 : jsid id = ATOM_TO_JSID(atom);
1938 323489 : const Shape *shape = blockObj.addVar(cx, id, blockCount, &redeclared);
1939 323489 : if (!shape) {
1940 171 : if (redeclared)
1941 171 : ReportRedeclaration(cx, tc, pn, false, atom);
1942 171 : return false;
1943 : }
1944 :
1945 : /* Store pn in the static block object. */
1946 323318 : blockObj.setDefinitionParseNode(blockCount, reinterpret_cast<Definition *>(pn));
1947 323318 : return true;
1948 : }
1949 :
1950 : template <class Op>
1951 : static inline bool
1952 111836 : ForEachLetDef(TreeContext *tc, StaticBlockObject &blockObj, Op op)
1953 : {
1954 602430 : for (Shape::Range r = blockObj.lastProperty()->all(); !r.empty(); r.popFront()) {
1955 490594 : const Shape &shape = r.front();
1956 :
1957 : /* Beware the destructuring dummy slots. */
1958 490594 : if (JSID_IS_INT(shape.propid()))
1959 8469 : continue;
1960 :
1961 482125 : if (!op(tc, blockObj, shape, JSID_TO_ATOM(shape.propid())))
1962 0 : return false;
1963 : }
1964 111836 : return true;
1965 : }
1966 :
1967 : struct RemoveDecl {
1968 322949 : bool operator()(TreeContext *tc, StaticBlockObject &, const Shape &, JSAtom *atom) {
1969 322949 : tc->decls.remove(atom);
1970 322949 : return true;
1971 : }
1972 : };
1973 :
1974 : static void
1975 412876 : PopStatement(TreeContext *tc)
1976 : {
1977 412876 : if (tc->topStmt->flags & SIF_SCOPE) {
1978 100675 : StaticBlockObject &blockObj = *tc->topStmt->blockObj;
1979 100675 : JS_ASSERT(!blockObj.inDictionaryMode());
1980 100675 : ForEachLetDef(tc, blockObj, RemoveDecl());
1981 : }
1982 412876 : PopStatementTC(tc);
1983 412876 : }
1984 :
1985 : static inline bool
1986 9 : OuterLet(TreeContext *tc, StmtInfo *stmt, JSAtom *atom)
1987 : {
1988 18 : while (stmt->downScope) {
1989 0 : stmt = LexicalLookup(tc, atom, NULL, stmt->downScope);
1990 0 : if (!stmt)
1991 0 : return false;
1992 0 : if (stmt->type == STMT_BLOCK)
1993 0 : return true;
1994 : }
1995 9 : return false;
1996 : }
1997 :
1998 : /*
1999 : * If we are generating global or eval-called-from-global code, bind a "gvar"
2000 : * here, as soon as possible. The JSOP_GETGVAR, etc., ops speed up interpreted
2001 : * global variable access by memoizing name-to-slot mappings during execution
2002 : * of the script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
2003 : * can't be done due to a pre-existing property of the same name as the var or
2004 : * const but incompatible attributes/getter/setter/etc, these ops devolve to
2005 : * JSOP_NAME, etc.
2006 : *
2007 : * For now, don't try to lookup eval frame variables at compile time. This is
2008 : * sub-optimal: we could handle eval-called-from-global-code gvars since eval
2009 : * gets its own script and frame. The eval-from-function-code case is harder,
2010 : * since functions do not atomize gvars and then reserve their atom indexes as
2011 : * stack frame slots.
2012 : */
2013 : static bool
2014 72533 : DefineGlobal(ParseNode *pn, BytecodeEmitter *bce, PropertyName *name)
2015 : {
2016 72533 : GlobalScope *globalScope = bce->globalScope;
2017 72533 : JSObject *globalObj = globalScope->globalObj;
2018 :
2019 72533 : if (!bce->compileAndGo() || !globalObj || bce->compilingForEval())
2020 3668 : return true;
2021 :
2022 137730 : AtomIndexAddPtr p = globalScope->names.lookupForAdd(name);
2023 68865 : if (!p) {
2024 68811 : JSContext *cx = bce->parser->context;
2025 :
2026 : JSObject *holder;
2027 : JSProperty *prop;
2028 68811 : if (!globalObj->lookupProperty(cx, name, &holder, &prop))
2029 0 : return false;
2030 :
2031 68811 : FunctionBox *funbox = pn->isKind(PNK_FUNCTION) ? pn->pn_funbox : NULL;
2032 :
2033 68811 : GlobalScope::GlobalDef def;
2034 68811 : if (prop) {
2035 : /*
2036 : * A few cases where we don't bother aggressively caching:
2037 : * 1) Function value changes.
2038 : * 2) Configurable properties.
2039 : * 3) Properties without slots, or with getters/setters.
2040 : */
2041 222 : const Shape *shape = (const Shape *)prop;
2042 459 : if (funbox ||
2043 : globalObj != holder ||
2044 120 : shape->configurable() ||
2045 39 : !shape->hasSlot() ||
2046 39 : !shape->hasDefaultGetter() ||
2047 39 : !shape->hasDefaultSetter()) {
2048 183 : return true;
2049 : }
2050 :
2051 39 : def = GlobalScope::GlobalDef(shape->slot());
2052 : } else {
2053 68589 : def = GlobalScope::GlobalDef(name, funbox);
2054 : }
2055 :
2056 68628 : if (!globalScope->defs.append(def))
2057 0 : return false;
2058 :
2059 68628 : jsatomid index = globalScope->names.count();
2060 68628 : if (!globalScope->names.add(p, name, index))
2061 0 : return false;
2062 :
2063 68628 : JS_ASSERT(index == globalScope->defs.length() - 1);
2064 : } else {
2065 : /*
2066 : * Functions can be redeclared, and the last one takes effect. Check
2067 : * for this and make sure to rewrite the definition.
2068 : *
2069 : * Note: This could overwrite an existing variable declaration, for
2070 : * example:
2071 : * var c = []
2072 : * function c() { }
2073 : *
2074 : * This rewrite is allowed because the function will be statically
2075 : * hoisted to the top of the script, and the |c = []| will just
2076 : * overwrite it at runtime.
2077 : */
2078 54 : if (pn->isKind(PNK_FUNCTION)) {
2079 54 : JS_ASSERT(pn->isArity(PN_FUNC));
2080 54 : jsatomid index = p.value();
2081 54 : globalScope->defs[index].funbox = pn->pn_funbox;
2082 : }
2083 : }
2084 :
2085 68682 : pn->pn_dflags |= PND_GVAR;
2086 :
2087 68682 : return true;
2088 : }
2089 :
2090 : static bool
2091 104725 : BindTopLevelVar(JSContext *cx, BindData *data, ParseNode *pn, TreeContext *tc)
2092 : {
2093 104725 : JS_ASSERT(pn->isOp(JSOP_NAME));
2094 104725 : JS_ASSERT(!tc->inFunction());
2095 :
2096 : /* There's no need to optimize bindings if we're not compiling code. */
2097 104725 : if (!tc->compiling())
2098 9 : return true;
2099 :
2100 : /*
2101 : * Bindings at top level in eval code aren't like bindings at top level in
2102 : * regular code, and we must handle them specially.
2103 : */
2104 104716 : if (tc->parser->callerFrame) {
2105 : /*
2106 : * If the eval code is not strict mode code, such bindings are created
2107 : * from scratch in the the caller's environment (if the eval is direct)
2108 : * or in the global environment (if the eval is indirect) -- and they
2109 : * can be deleted. Therefore we can't bind early.
2110 : */
2111 576 : if (!tc->inStrictMode())
2112 432 : return true;
2113 :
2114 : /*
2115 : * But if the eval code is strict mode code, bindings are added to a
2116 : * new environment specifically for that eval code's compilation, and
2117 : * they can't be deleted. Thus strict mode eval code does not affect
2118 : * the caller's environment, and we can bind such names early. (But
2119 : * note: strict mode eval code can still affect the global environment
2120 : * by performing an indirect eval of non-strict mode code.)
2121 : *
2122 : * However, optimizing such bindings requires either precarious
2123 : * type-punning or, ideally, a new kind of Call object specifically for
2124 : * strict mode eval frames. Further, strict mode eval is not (yet)
2125 : * common. So for now (until we rewrite the scope chain to not use
2126 : * objects?) just disable optimizations for top-level names in eval
2127 : * code.
2128 : */
2129 144 : return true;
2130 : }
2131 :
2132 104140 : if (pn->pn_dflags & PND_CONST)
2133 56106 : return true;
2134 :
2135 : /*
2136 : * If this is a global variable, we're compile-and-go, and a global object
2137 : * is present, try to bake in either an already available slot or a
2138 : * predicted slot that will be defined after compiling is completed.
2139 : */
2140 48034 : return DefineGlobal(pn, tc->asBytecodeEmitter(), pn->pn_atom->asPropertyName());
2141 : }
2142 :
2143 : static bool
2144 121492 : BindFunctionLocal(JSContext *cx, BindData *data, MultiDeclRange &mdl, TreeContext *tc)
2145 : {
2146 121492 : JS_ASSERT(tc->inFunction());
2147 :
2148 121492 : ParseNode *pn = data->pn;
2149 121492 : JSAtom *name = pn->pn_atom;
2150 :
2151 : /*
2152 : * Don't create a local variable with the name 'arguments', per ECMA-262.
2153 : * Instead, 'var arguments' always restates that predefined binding of the
2154 : * lexical environment for function activations. Assignments to arguments
2155 : * must be handled specially -- see NoteLValue.
2156 : */
2157 121492 : if (name == cx->runtime->atomState.argumentsAtom) {
2158 27 : pn->setOp(JSOP_ARGUMENTS);
2159 27 : pn->pn_dflags |= PND_BOUND;
2160 27 : return true;
2161 : }
2162 :
2163 121465 : BindingKind kind = tc->bindings.lookup(cx, name, NULL);
2164 121465 : if (kind == NONE) {
2165 : /*
2166 : * Property not found in current variable scope: we have not seen this
2167 : * variable before, so bind a new local variable for it. Any locals
2168 : * declared in a with statement body are handled at runtime, by script
2169 : * prolog JSOP_DEFVAR opcodes generated for global and heavyweight-
2170 : * function-local vars.
2171 : */
2172 121465 : kind = (data->op == JSOP_DEFCONST) ? CONSTANT : VARIABLE;
2173 :
2174 121465 : if (!BindLocalVariable(cx, tc, pn, kind))
2175 0 : return false;
2176 121465 : pn->setOp(JSOP_GETLOCAL);
2177 121465 : return true;
2178 : }
2179 :
2180 0 : if (kind == ARGUMENT) {
2181 0 : JS_ASSERT(tc->inFunction());
2182 0 : JS_ASSERT(!mdl.empty() && mdl.front()->kind() == Definition::ARG);
2183 : } else {
2184 0 : JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
2185 : }
2186 :
2187 0 : return true;
2188 : }
2189 :
2190 : static JSBool
2191 229349 : BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc)
2192 : {
2193 229349 : ParseNode *pn = data->pn;
2194 :
2195 : /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
2196 229349 : pn->setOp(JSOP_NAME);
2197 :
2198 229349 : if (!CheckStrictBinding(cx, tc, atom->asPropertyName(), pn))
2199 0 : return false;
2200 :
2201 229349 : StmtInfo *stmt = LexicalLookup(tc, atom, NULL);
2202 :
2203 229349 : if (stmt && stmt->type == STMT_WITH) {
2204 81 : data->fresh = false;
2205 81 : pn->pn_dflags |= PND_DEOPTIMIZED;
2206 81 : tc->noteMightAliasLocals();
2207 81 : return true;
2208 : }
2209 :
2210 229268 : MultiDeclRange mdl = tc->decls.lookupMulti(atom);
2211 229268 : JSOp op = data->op;
2212 :
2213 229268 : if (stmt || !mdl.empty()) {
2214 3051 : Definition *dn = mdl.empty() ? NULL : mdl.front();
2215 3051 : Definition::Kind dn_kind = dn ? dn->kind() : Definition::VAR;
2216 :
2217 3051 : if (dn_kind == Definition::ARG) {
2218 900 : JSAutoByteString name;
2219 450 : if (!js_AtomToPrintableString(cx, atom, &name))
2220 0 : return JS_FALSE;
2221 :
2222 450 : if (op == JSOP_DEFCONST) {
2223 : ReportCompileErrorNumber(cx, TS(tc->parser), pn,
2224 : JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
2225 0 : name.ptr());
2226 0 : return JS_FALSE;
2227 : }
2228 450 : if (!ReportCompileErrorNumber(cx, TS(tc->parser), pn,
2229 : JSREPORT_WARNING | JSREPORT_STRICT,
2230 450 : JSMSG_VAR_HIDES_ARG, name.ptr())) {
2231 0 : return JS_FALSE;
2232 : }
2233 : } else {
2234 : bool error = (op == JSOP_DEFCONST ||
2235 : dn_kind == Definition::CONST ||
2236 : (dn_kind == Definition::LET &&
2237 2601 : (stmt->type != STMT_CATCH || OuterLet(tc, stmt, atom))));
2238 :
2239 2601 : if (cx->hasStrictOption()
2240 : ? op != JSOP_DEFVAR || dn_kind != Definition::VAR
2241 : : error) {
2242 0 : JSAutoByteString name;
2243 0 : if (!js_AtomToPrintableString(cx, atom, &name) ||
2244 : !ReportCompileErrorNumber(cx, TS(tc->parser), pn,
2245 0 : !error
2246 : ? JSREPORT_WARNING | JSREPORT_STRICT
2247 : : JSREPORT_ERROR,
2248 : JSMSG_REDECLARED_VAR,
2249 : Definition::kindString(dn_kind),
2250 0 : name.ptr())) {
2251 0 : return JS_FALSE;
2252 : }
2253 : }
2254 : }
2255 : }
2256 :
2257 229268 : if (mdl.empty()) {
2258 226217 : if (!Define(pn, atom, tc))
2259 0 : return JS_FALSE;
2260 : } else {
2261 : /*
2262 : * A var declaration never recreates an existing binding, it restates
2263 : * it and possibly reinitializes its value. Beware that if pn becomes a
2264 : * use of |mdl.defn()|, and if we have an initializer for this var or
2265 : * const (typically a const would ;-), then pn must be rewritten into a
2266 : * PNK_ASSIGN node. See js::Parser::variables, further below.
2267 : *
2268 : * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
2269 : * There the x definition is hoisted but the x = 2 assignment mutates
2270 : * the block-local binding of x.
2271 : */
2272 3051 : Definition *dn = mdl.front();
2273 :
2274 3051 : data->fresh = false;
2275 :
2276 3051 : if (!pn->isUsed()) {
2277 : /* Make pnu be a fresh name node that uses dn. */
2278 3051 : ParseNode *pnu = pn;
2279 :
2280 3051 : if (pn->isDefn()) {
2281 0 : pnu = NameNode::create(PNK_NAME, atom, tc);
2282 0 : if (!pnu)
2283 0 : return JS_FALSE;
2284 : }
2285 :
2286 3051 : LinkUseToDef(pnu, dn, tc);
2287 3051 : pnu->setOp(JSOP_NAME);
2288 : }
2289 :
2290 : /* Find the first non-let binding of this atom. */
2291 6102 : while (dn->kind() == Definition::LET) {
2292 9 : mdl.popFront();
2293 9 : if (mdl.empty())
2294 9 : break;
2295 0 : dn = mdl.front();
2296 : }
2297 :
2298 3051 : if (dn) {
2299 0 : JS_ASSERT_IF(data->op == JSOP_DEFCONST,
2300 3051 : dn->kind() == Definition::CONST);
2301 3051 : return JS_TRUE;
2302 : }
2303 :
2304 : /*
2305 : * A var or const that is shadowed by one or more let bindings of the
2306 : * same name, but that has not been declared until this point, must be
2307 : * hoisted above the let bindings.
2308 : */
2309 0 : if (!pn->isDefn()) {
2310 0 : if (tc->lexdeps->lookup(atom)) {
2311 0 : tc->lexdeps->remove(atom);
2312 : } else {
2313 0 : ParseNode *pn2 = NameNode::create(PNK_NAME, atom, tc);
2314 0 : if (!pn2)
2315 0 : return JS_FALSE;
2316 :
2317 : /* The token stream may be past the location for pn. */
2318 0 : pn2->pn_pos = pn->pn_pos;
2319 0 : pn = pn2;
2320 : }
2321 0 : pn->setOp(JSOP_NAME);
2322 : }
2323 :
2324 0 : if (!tc->decls.addHoist(atom, (Definition *) pn))
2325 0 : return JS_FALSE;
2326 0 : pn->setDefn(true);
2327 0 : pn->pn_dflags &= ~PND_PLACEHOLDER;
2328 : }
2329 :
2330 226217 : if (data->op == JSOP_DEFCONST)
2331 56178 : pn->pn_dflags |= PND_CONST;
2332 :
2333 226217 : if (tc->inFunction())
2334 121492 : return BindFunctionLocal(cx, data, mdl, tc);
2335 :
2336 104725 : return BindTopLevelVar(cx, data, pn, tc);
2337 : }
2338 :
2339 : static bool
2340 36 : MakeSetCall(JSContext *cx, ParseNode *pn, TreeContext *tc, unsigned msg)
2341 : {
2342 36 : JS_ASSERT(pn->isArity(PN_LIST));
2343 36 : JS_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_EVAL) ||
2344 36 : pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY));
2345 36 : if (!ReportStrictModeError(cx, TS(tc->parser), tc, pn, msg))
2346 0 : return false;
2347 :
2348 36 : ParseNode *pn2 = pn->pn_head;
2349 36 : if (pn2->isKind(PNK_FUNCTION) && (pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA)) {
2350 0 : ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR, msg);
2351 0 : return false;
2352 : }
2353 36 : pn->pn_xflags |= PNX_SETCALL;
2354 36 : return true;
2355 : }
2356 :
2357 : static void
2358 561131 : NoteLValue(JSContext *cx, ParseNode *pn, TreeContext *tc, unsigned dflag = PND_ASSIGNED)
2359 : {
2360 561131 : if (pn->isUsed()) {
2361 409068 : Definition *dn = pn->pn_lexdef;
2362 :
2363 : /*
2364 : * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
2365 : * occur as direct kids of the same block with no forward refs to x.
2366 : */
2367 439776 : if (!(dn->pn_dflags & (PND_INITIALIZED | PND_CONST | PND_PLACEHOLDER)) &&
2368 15782 : dn->isBlockChild() &&
2369 12865 : pn->isBlockChild() &&
2370 : dn->pn_blockid == pn->pn_blockid &&
2371 2061 : dn->pn_pos.end <= pn->pn_pos.begin &&
2372 : dn->dn_uses == pn) {
2373 2043 : dflag = PND_INITIALIZED;
2374 : }
2375 :
2376 409068 : dn->pn_dflags |= dflag;
2377 : }
2378 :
2379 561131 : pn->pn_dflags |= dflag;
2380 :
2381 : /*
2382 : * Both arguments and the enclosing function's name are immutable bindings
2383 : * in ES5, so assignments to them must do nothing or throw a TypeError
2384 : * depending on code strictness. Assignment to arguments is a syntax error
2385 : * in strict mode and thus cannot happen. Outside strict mode, we optimize
2386 : * away assignment to the function name. For assignment to function name
2387 : * to fail in strict mode, we must have a binding for it in the scope
2388 : * chain; we ensure this happens by making such functions heavyweight.
2389 : */
2390 561131 : JSAtom *lname = pn->pn_atom;
2391 561131 : if (lname == cx->runtime->atomState.argumentsAtom) {
2392 63 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
2393 63 : tc->noteLocalOverwritesArguments();
2394 63 : tc->countArgumentsUse(pn);
2395 561068 : } else if (tc->inFunction() && lname == tc->fun()->atom) {
2396 99 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
2397 : }
2398 561131 : }
2399 :
2400 : #if JS_HAS_DESTRUCTURING
2401 :
2402 : static JSBool
2403 4392 : BindDestructuringVar(JSContext *cx, BindData *data, ParseNode *pn, TreeContext *tc)
2404 : {
2405 : JSAtom *atom;
2406 :
2407 : /*
2408 : * Destructuring is a form of assignment, so just as for an initialized
2409 : * simple variable, we must check for assignment to 'arguments' and flag
2410 : * the enclosing function (if any) as heavyweight.
2411 : */
2412 4392 : JS_ASSERT(pn->isKind(PNK_NAME));
2413 4392 : atom = pn->pn_atom;
2414 4392 : if (atom == cx->runtime->atomState.argumentsAtom) {
2415 0 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
2416 0 : tc->noteLocalOverwritesArguments();
2417 : }
2418 :
2419 4392 : data->pn = pn;
2420 4392 : if (!data->binder(cx, data, atom, tc))
2421 135 : return JS_FALSE;
2422 :
2423 : /*
2424 : * Select the appropriate name-setting opcode, respecting eager selection
2425 : * done by the data->binder function.
2426 : */
2427 4257 : if (pn->pn_dflags & PND_BOUND) {
2428 3942 : JS_ASSERT(!(pn->pn_dflags & PND_GVAR));
2429 3942 : pn->setOp(pn->isOp(JSOP_ARGUMENTS) ? JSOP_SETNAME : JSOP_SETLOCAL);
2430 : } else {
2431 315 : pn->setOp((data->op == JSOP_DEFCONST) ? JSOP_SETCONST : JSOP_SETNAME);
2432 : }
2433 :
2434 4257 : if (data->op == JSOP_DEFCONST)
2435 9 : pn->pn_dflags |= PND_CONST;
2436 :
2437 4257 : NoteLValue(cx, pn, tc, PND_INITIALIZED);
2438 4257 : return JS_TRUE;
2439 : }
2440 :
2441 : /*
2442 : * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
2443 : * LHS expression except a destructuring initialiser, and R is on the stack.
2444 : * Because R is already evaluated, the usual LHS-specialized bytecodes won't
2445 : * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
2446 : * then push its property name QN. At this point the stack looks like
2447 : *
2448 : * [... R, R[P], QB, QN]
2449 : *
2450 : * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
2451 : * its operands with left-hand side above right-hand side:
2452 : *
2453 : * [rval, lval, xval]
2454 : *
2455 : * and pops all three values, setting lval[xval] = rval. But we cannot select
2456 : * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
2457 : * which can be optimized further. So we select JSOP_SETNAME.
2458 : */
2459 : static JSBool
2460 162 : BindDestructuringLHS(JSContext *cx, ParseNode *pn, TreeContext *tc)
2461 : {
2462 162 : switch (pn->getKind()) {
2463 : case PNK_NAME:
2464 144 : NoteLValue(cx, pn, tc);
2465 : /* FALL THROUGH */
2466 :
2467 : case PNK_DOT:
2468 : case PNK_LB:
2469 : /*
2470 : * We may be called on a name node that has already been specialized,
2471 : * in the very weird and ECMA-262-required "for (var [x] = i in o) ..."
2472 : * case. See bug 558633.
2473 : */
2474 162 : if (!(js_CodeSpec[pn->getOp()].format & JOF_SET))
2475 162 : pn->setOp(JSOP_SETNAME);
2476 162 : break;
2477 :
2478 : case PNK_LP:
2479 0 : if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
2480 0 : return JS_FALSE;
2481 0 : break;
2482 :
2483 : #if JS_HAS_XML_SUPPORT
2484 : case PNK_XMLUNARY:
2485 0 : JS_ASSERT(pn->isOp(JSOP_XMLNAME));
2486 0 : pn->setOp(JSOP_BINDXMLNAME);
2487 0 : break;
2488 : #endif
2489 :
2490 : default:
2491 : ReportCompileErrorNumber(cx, TS(tc->parser), pn,
2492 0 : JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
2493 0 : return JS_FALSE;
2494 : }
2495 :
2496 162 : return JS_TRUE;
2497 : }
2498 :
2499 : /*
2500 : * Destructuring patterns can appear in two kinds of contexts:
2501 : *
2502 : * - assignment-like: assignment expressions and |for| loop heads. In
2503 : * these cases, the patterns' property value positions can be
2504 : * arbitrary lvalue expressions; the destructuring is just a fancy
2505 : * assignment.
2506 : *
2507 : * - declaration-like: |var| and |let| declarations, functions' formal
2508 : * parameter lists, |catch| clauses, and comprehension tails. In
2509 : * these cases, the patterns' property value positions must be
2510 : * simple names; the destructuring defines them as new variables.
2511 : *
2512 : * In both cases, other code parses the pattern as an arbitrary
2513 : * primaryExpr, and then, here in CheckDestructuring, verify that the
2514 : * tree is a valid destructuring expression.
2515 : *
2516 : * In assignment-like contexts, we parse the pattern with the
2517 : * TCF_DECL_DESTRUCTURING flag clear, so the lvalue expressions in the
2518 : * pattern are parsed normally. primaryExpr links variable references
2519 : * into the appropriate use chains; creates placeholder definitions;
2520 : * and so on. CheckDestructuring is called with |data| NULL (since we
2521 : * won't be binding any new names), and we specialize lvalues as
2522 : * appropriate.
2523 : *
2524 : * In declaration-like contexts, the normal variable reference
2525 : * processing would just be an obstruction, because we're going to
2526 : * define the names that appear in the property value positions as new
2527 : * variables anyway. In this case, we parse the pattern with
2528 : * TCF_DECL_DESTRUCTURING set, which directs primaryExpr to leave
2529 : * whatever name nodes it creates unconnected. Then, here in
2530 : * CheckDestructuring, we require the pattern's property value
2531 : * positions to be simple names, and define them as appropriate to the
2532 : * context. For these calls, |data| points to the right sort of
2533 : * BindData.
2534 : *
2535 : * See also UndominateInitializers, immediately below. If you change
2536 : * either of these functions, you might have to change the other to
2537 : * match.
2538 : *
2539 : * The 'toplevel' is a private detail of the recursive strategy used by
2540 : * CheckDestructuring and callers should use the default value.
2541 : */
2542 : static bool
2543 12429 : CheckDestructuring(JSContext *cx, BindData *data, ParseNode *left, TreeContext *tc,
2544 : bool toplevel = true)
2545 : {
2546 : bool ok;
2547 :
2548 12429 : if (left->isKind(PNK_ARRAYCOMP)) {
2549 : ReportCompileErrorNumber(cx, TS(tc->parser), left, JSREPORT_ERROR,
2550 0 : JSMSG_ARRAY_COMP_LEFTSIDE);
2551 0 : return false;
2552 : }
2553 :
2554 12429 : StaticBlockObject *blockObj = data && data->binder == BindLet ? data->let.blockObj : NULL;
2555 12429 : uint32_t blockCountBefore = blockObj ? blockObj->slotCount() : 0;
2556 :
2557 12429 : if (left->isKind(PNK_RB)) {
2558 19152 : for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) {
2559 : /* Nullary comma is an elision; binary comma is an expression.*/
2560 8442 : if (!pn->isArrayHole()) {
2561 7407 : if (pn->isKind(PNK_RB) || pn->isKind(PNK_RC)) {
2562 3852 : ok = CheckDestructuring(cx, data, pn, tc, false);
2563 : } else {
2564 3555 : if (data) {
2565 3447 : if (!pn->isKind(PNK_NAME)) {
2566 : ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR,
2567 0 : JSMSG_NO_VARIABLE_NAME);
2568 0 : return false;
2569 : }
2570 3447 : ok = BindDestructuringVar(cx, data, pn, tc);
2571 : } else {
2572 108 : ok = BindDestructuringLHS(cx, pn, tc);
2573 : }
2574 : }
2575 7407 : if (!ok)
2576 189 : return false;
2577 : }
2578 : }
2579 : } else {
2580 1530 : JS_ASSERT(left->isKind(PNK_RC));
2581 3429 : for (ParseNode *pair = left->pn_head; pair; pair = pair->pn_next) {
2582 1962 : JS_ASSERT(pair->isKind(PNK_COLON));
2583 1962 : ParseNode *pn = pair->pn_right;
2584 :
2585 1962 : if (pn->isKind(PNK_RB) || pn->isKind(PNK_RC)) {
2586 936 : ok = CheckDestructuring(cx, data, pn, tc, false);
2587 1026 : } else if (data) {
2588 972 : if (!pn->isKind(PNK_NAME)) {
2589 : ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR,
2590 27 : JSMSG_NO_VARIABLE_NAME);
2591 27 : return false;
2592 : }
2593 945 : ok = BindDestructuringVar(cx, data, pn, tc);
2594 : } else {
2595 54 : ok = BindDestructuringLHS(cx, pn, tc);
2596 : }
2597 1935 : if (!ok)
2598 36 : return false;
2599 : }
2600 : }
2601 :
2602 : /*
2603 : * The catch/finally handler implementation in the interpreter assumes
2604 : * that any operation that introduces a new scope (like a "let" or "with"
2605 : * block) increases the stack depth. This way, it is possible to restore
2606 : * the scope chain based on stack depth of the handler alone. "let" with
2607 : * an empty destructuring pattern like in
2608 : *
2609 : * let [] = 1;
2610 : *
2611 : * would violate this assumption as the there would be no let locals to
2612 : * store on the stack.
2613 : *
2614 : * Furthermore, the decompiler needs an abstract stack location to store
2615 : * the decompilation of each let block/expr initializer. E.g., given:
2616 : *
2617 : * let (x = 1, [[]] = b, y = 3, {a:[]} = c) { ... }
2618 : *
2619 : * four slots are needed.
2620 : *
2621 : * To satisfy both constraints, we push a dummy slot (and add a
2622 : * corresponding dummy property to the block object) for each initializer
2623 : * that doesn't introduce at least one binding.
2624 : */
2625 12177 : if (toplevel && blockObj && blockCountBefore == blockObj->slotCount()) {
2626 : bool redeclared;
2627 4428 : if (!blockObj->addVar(cx, INT_TO_JSID(blockCountBefore), blockCountBefore, &redeclared))
2628 0 : return false;
2629 4428 : JS_ASSERT(!redeclared);
2630 4428 : JS_ASSERT(blockObj->slotCount() == blockCountBefore + 1);
2631 : }
2632 :
2633 12177 : return true;
2634 : }
2635 :
2636 : /*
2637 : * Extend the pn_pos.end source coordinate of each name in a destructuring
2638 : * binding such as
2639 : *
2640 : * var [x, y] = [function () y, 42];
2641 : *
2642 : * to cover the entire initializer, so that the initialized bindings do not
2643 : * appear to dominate any closures in the initializer. See bug 496134.
2644 : *
2645 : * The quick-and-dirty dominance computation in Parser::setFunctionKinds is not
2646 : * very precise. With one-pass SSA construction from structured source code
2647 : * (see "Single-Pass Generation of Static Single Assignment Form for Structured
2648 : * Languages", Brandis and Mössenböck), we could do much better.
2649 : *
2650 : * See CheckDestructuring, immediately above. If you change either of these
2651 : * functions, you might have to change the other to match.
2652 : */
2653 : static void
2654 11475 : UndominateInitializers(ParseNode *left, const TokenPtr &end, TreeContext *tc)
2655 : {
2656 11475 : if (left->isKind(PNK_RB)) {
2657 17631 : for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) {
2658 : /* Nullary comma is an elision; binary comma is an expression.*/
2659 7461 : if (!pn->isKind(PNK_COMMA) || !pn->isArity(PN_NULLARY)) {
2660 6435 : if (pn->isKind(PNK_RB) || pn->isKind(PNK_RC))
2661 3591 : UndominateInitializers(pn, end, tc);
2662 : else
2663 2844 : pn->pn_pos.end = end;
2664 : }
2665 : }
2666 : } else {
2667 1305 : JS_ASSERT(left->isKind(PNK_RC));
2668 :
2669 2961 : for (ParseNode *pair = left->pn_head; pair; pair = pair->pn_next) {
2670 1656 : JS_ASSERT(pair->isKind(PNK_COLON));
2671 1656 : ParseNode *pn = pair->pn_right;
2672 1656 : if (pn->isKind(PNK_RB) || pn->isKind(PNK_RC))
2673 918 : UndominateInitializers(pn, end, tc);
2674 : else
2675 738 : pn->pn_pos.end = end;
2676 : }
2677 : }
2678 11475 : }
2679 :
2680 : ParseNode *
2681 81 : Parser::destructuringExpr(BindData *data, TokenKind tt)
2682 : {
2683 81 : JS_ASSERT(tokenStream.isCurrentTokenType(tt));
2684 :
2685 81 : tc->flags |= TCF_DECL_DESTRUCTURING;
2686 81 : ParseNode *pn = primaryExpr(tt, JS_FALSE);
2687 81 : tc->flags &= ~TCF_DECL_DESTRUCTURING;
2688 81 : if (!pn)
2689 0 : return NULL;
2690 81 : if (!CheckDestructuring(context, data, pn, tc))
2691 0 : return NULL;
2692 81 : return pn;
2693 : }
2694 :
2695 : #endif /* JS_HAS_DESTRUCTURING */
2696 :
2697 : ParseNode *
2698 39114 : Parser::returnOrYield(bool useAssignExpr)
2699 : {
2700 39114 : TokenKind tt = tokenStream.currentToken().type;
2701 39114 : if (!tc->inFunction()) {
2702 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD,
2703 0 : (tt == TOK_RETURN) ? js_return_str : js_yield_str);
2704 0 : return NULL;
2705 : }
2706 :
2707 39114 : ParseNode *pn = UnaryNode::create((tt == TOK_RETURN) ? PNK_RETURN : PNK_YIELD, tc);
2708 39114 : if (!pn)
2709 0 : return NULL;
2710 :
2711 : #if JS_HAS_GENERATORS
2712 39114 : if (tt == TOK_YIELD) {
2713 : /*
2714 : * If we're within parens, we won't know if this is a generator expression until we see
2715 : * a |for| token, so we have to delay flagging the current function.
2716 : */
2717 558 : if (tc->parenDepth == 0) {
2718 558 : tc->flags |= TCF_FUN_IS_GENERATOR;
2719 : } else {
2720 0 : tc->yieldCount++;
2721 0 : tc->yieldNode = pn;
2722 : }
2723 : }
2724 : #endif
2725 :
2726 : /* This is ugly, but we don't want to require a semicolon. */
2727 39114 : TokenKind tt2 = tokenStream.peekTokenSameLine(TSF_OPERAND);
2728 39114 : if (tt2 == TOK_ERROR)
2729 0 : return NULL;
2730 :
2731 39114 : if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
2732 : #if JS_HAS_GENERATORS
2733 : && (tt != TOK_YIELD ||
2734 : (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
2735 : tt2 != TOK_COLON && tt2 != TOK_COMMA))
2736 : #endif
2737 : )
2738 : {
2739 37602 : ParseNode *pn2 = useAssignExpr ? assignExpr() : expr();
2740 37602 : if (!pn2)
2741 0 : return NULL;
2742 : #if JS_HAS_GENERATORS
2743 37602 : if (tt == TOK_RETURN)
2744 : #endif
2745 37197 : tc->flags |= TCF_RETURN_EXPR;
2746 37602 : pn->pn_pos.end = pn2->pn_pos.end;
2747 37602 : pn->pn_kid = pn2;
2748 : } else {
2749 : #if JS_HAS_GENERATORS
2750 1512 : if (tt == TOK_RETURN)
2751 : #endif
2752 1359 : tc->flags |= TCF_RETURN_VOID;
2753 : }
2754 :
2755 39114 : if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
2756 : /* As in Python (see PEP-255), disallow return v; in generators. */
2757 : ReportBadReturn(context, tc, pn, JSREPORT_ERROR,
2758 : JSMSG_BAD_GENERATOR_RETURN,
2759 0 : JSMSG_BAD_ANON_GENERATOR_RETURN);
2760 0 : return NULL;
2761 : }
2762 :
2763 39114 : if (context->hasStrictOption() &&
2764 : (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
2765 : !ReportBadReturn(context, tc, pn, JSREPORT_WARNING | JSREPORT_STRICT,
2766 : JSMSG_NO_RETURN_VALUE,
2767 0 : JSMSG_ANON_NO_RETURN_VALUE)) {
2768 0 : return NULL;
2769 : }
2770 :
2771 39114 : return pn;
2772 : }
2773 :
2774 : static ParseNode *
2775 98938 : PushLexicalScope(JSContext *cx, TreeContext *tc, StaticBlockObject &obj, StmtInfo *stmt)
2776 : {
2777 98938 : ParseNode *pn = LexicalScopeNode::create(PNK_LEXICALSCOPE, tc);
2778 98938 : if (!pn)
2779 0 : return NULL;
2780 :
2781 98938 : ObjectBox *blockbox = tc->parser->newObjectBox(&obj);
2782 98938 : if (!blockbox)
2783 0 : return NULL;
2784 :
2785 98938 : PushBlockScope(tc, stmt, obj, -1);
2786 98938 : pn->setOp(JSOP_LEAVEBLOCK);
2787 98938 : pn->pn_objbox = blockbox;
2788 98938 : pn->pn_cookie.makeFree();
2789 98938 : pn->pn_dflags = 0;
2790 98938 : if (!GenerateBlockId(tc, stmt->blockid))
2791 0 : return NULL;
2792 98938 : pn->pn_blockid = stmt->blockid;
2793 98938 : return pn;
2794 : }
2795 :
2796 : static ParseNode *
2797 87777 : PushLexicalScope(JSContext *cx, TreeContext *tc, StmtInfo *stmt)
2798 : {
2799 87777 : StaticBlockObject *blockObj = StaticBlockObject::create(cx);
2800 87777 : if (!blockObj)
2801 0 : return NULL;
2802 :
2803 87777 : return PushLexicalScope(cx, tc, *blockObj, stmt);
2804 : }
2805 :
2806 : #if JS_HAS_BLOCK_SCOPE
2807 :
2808 : struct AddDecl
2809 : {
2810 : uint32_t blockid;
2811 :
2812 11161 : AddDecl(uint32_t blockid) : blockid(blockid) {}
2813 :
2814 159176 : bool operator()(TreeContext *tc, StaticBlockObject &blockObj, const Shape &shape, JSAtom *atom)
2815 : {
2816 159176 : ParseNode *def = (ParseNode *) blockObj.getSlot(shape.slot()).toPrivate();
2817 159176 : def->pn_blockid = blockid;
2818 159176 : return Define(def, atom, tc, true);
2819 : }
2820 : };
2821 :
2822 : static ParseNode *
2823 11161 : PushLetScope(JSContext *cx, TreeContext *tc, StaticBlockObject &blockObj, StmtInfo *stmt)
2824 : {
2825 11161 : ParseNode *pn = PushLexicalScope(cx, tc, blockObj, stmt);
2826 11161 : if (!pn)
2827 0 : return NULL;
2828 :
2829 : /* Tell codegen to emit JSOP_ENTERLETx (not JSOP_ENTERBLOCK). */
2830 11161 : pn->pn_dflags |= PND_LET;
2831 :
2832 : /* Populate the new scope with decls found in the head with updated blockid. */
2833 11161 : if (!ForEachLetDef(tc, blockObj, AddDecl(stmt->blockid)))
2834 0 : return NULL;
2835 :
2836 11161 : return pn;
2837 : }
2838 :
2839 : /*
2840 : * Parse a let block statement or let expression (determined by 'letContext').
2841 : * In both cases, bindings are not hoisted to the top of the enclosing block
2842 : * and thus must be carefully injected between variables() and the let body.
2843 : */
2844 : ParseNode *
2845 4231 : Parser::letBlock(LetContext letContext)
2846 : {
2847 4231 : JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
2848 :
2849 4231 : ParseNode *pnlet = BinaryNode::create(PNK_LET, tc);
2850 4231 : if (!pnlet)
2851 0 : return NULL;
2852 :
2853 4231 : StaticBlockObject *blockObj = StaticBlockObject::create(context);
2854 4231 : if (!blockObj)
2855 0 : return NULL;
2856 :
2857 4231 : MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
2858 :
2859 4231 : ParseNode *vars = variables(PNK_LET, blockObj, DontHoistVars);
2860 4231 : if (!vars)
2861 108 : return NULL;
2862 :
2863 4123 : MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
2864 :
2865 : StmtInfo stmtInfo;
2866 4105 : ParseNode *block = PushLetScope(context, tc, *blockObj, &stmtInfo);
2867 4105 : if (!block)
2868 0 : return NULL;
2869 :
2870 4105 : pnlet->pn_left = vars;
2871 4105 : pnlet->pn_right = block;
2872 :
2873 : ParseNode *ret;
2874 4105 : if (letContext == LetStatement && !tokenStream.matchToken(TOK_LC, TSF_OPERAND)) {
2875 : /*
2876 : * Strict mode eliminates a grammar ambiguity with unparenthesized
2877 : * LetExpressions in an ExpressionStatement. If followed immediately
2878 : * by an arguments list, it's ambiguous whether the let expression
2879 : * is the callee or the call is inside the let expression body.
2880 : *
2881 : * See bug 569464.
2882 : */
2883 234 : if (!ReportStrictModeError(context, &tokenStream, tc, pnlet,
2884 234 : JSMSG_STRICT_CODE_LET_EXPR_STMT)) {
2885 0 : return NULL;
2886 : }
2887 :
2888 : /*
2889 : * If this is really an expression in let statement guise, then we
2890 : * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
2891 : * the return value of the expression.
2892 : */
2893 234 : ParseNode *semi = UnaryNode::create(PNK_SEMI, tc);
2894 234 : if (!semi)
2895 0 : return NULL;
2896 :
2897 234 : semi->pn_kid = pnlet;
2898 :
2899 234 : letContext = LetExpresion;
2900 234 : ret = semi;
2901 : } else {
2902 3871 : ret = pnlet;
2903 : }
2904 :
2905 4105 : if (letContext == LetStatement) {
2906 2025 : JS_ASSERT(block->getOp() == JSOP_LEAVEBLOCK);
2907 2025 : block->pn_expr = statements();
2908 2025 : if (!block->pn_expr)
2909 18 : return NULL;
2910 2007 : MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
2911 : } else {
2912 2080 : JS_ASSERT(letContext == LetExpresion);
2913 2080 : block->setOp(JSOP_LEAVEBLOCKEXPR);
2914 2080 : block->pn_expr = assignExpr();
2915 2080 : if (!block->pn_expr)
2916 0 : return NULL;
2917 : }
2918 :
2919 4087 : PopStatement(tc);
2920 4087 : return ret;
2921 : }
2922 :
2923 : #endif /* JS_HAS_BLOCK_SCOPE */
2924 :
2925 : static bool
2926 186972 : PushBlocklikeStatement(StmtInfo *stmt, StmtType type, TreeContext *tc)
2927 : {
2928 186972 : PushStatement(tc, stmt, type, -1);
2929 186972 : return GenerateBlockId(tc, stmt->blockid);
2930 : }
2931 :
2932 : static ParseNode *
2933 548707 : NewBindingNode(JSAtom *atom, TreeContext *tc, StaticBlockObject *blockObj = NULL,
2934 : VarContext varContext = HoistVars)
2935 : {
2936 : /*
2937 : * If this name is being injected into an existing block/function, see if
2938 : * it has already been declared or if it resolves an outstanding lexdep.
2939 : * Otherwise, this is a let block/expr that introduces a new scope and thus
2940 : * shadows existing decls and doesn't resolve existing lexdeps. Duplicate
2941 : * names are caught by BindLet.
2942 : */
2943 548707 : if (!blockObj || varContext == HoistVars) {
2944 391628 : ParseNode *pn = tc->decls.lookupFirst(atom);
2945 391628 : AtomDefnPtr removal;
2946 391628 : if (pn) {
2947 3312 : JS_ASSERT(!pn->isPlaceholder());
2948 : } else {
2949 388316 : removal = tc->lexdeps->lookup(atom);
2950 388316 : pn = removal ? removal.value() : NULL;
2951 388316 : JS_ASSERT_IF(pn, pn->isPlaceholder());
2952 : }
2953 :
2954 391628 : if (pn) {
2955 5571 : JS_ASSERT(pn->isDefn());
2956 :
2957 : /*
2958 : * A let binding at top level becomes a var before we get here, so if
2959 : * pn and tc have the same blockid then that id must not be the bodyid.
2960 : * If pn is a forward placeholder definition from the same or a higher
2961 : * block then we claim it.
2962 : */
2963 441 : JS_ASSERT_IF(blockObj && pn->pn_blockid == tc->blockid(),
2964 6012 : pn->pn_blockid != tc->bodyid);
2965 :
2966 5571 : if (pn->isPlaceholder() && pn->pn_blockid >= tc->blockid()) {
2967 981 : pn->pn_blockid = tc->blockid();
2968 981 : tc->lexdeps->remove(removal);
2969 981 : return pn;
2970 : }
2971 : }
2972 : }
2973 :
2974 : /* Make a new node for this declarator name (or destructuring pattern). */
2975 547726 : JS_ASSERT(tc->parser->tokenStream.currentToken().type == TOK_NAME);
2976 547726 : ParseNode *pn = NameNode::create(PNK_NAME, atom, tc);
2977 547726 : if (!pn)
2978 0 : return NULL;
2979 :
2980 547726 : if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
2981 36 : tc->countArgumentsUse(pn);
2982 :
2983 547726 : return pn;
2984 : }
2985 :
2986 : ParseNode *
2987 918 : Parser::switchStatement()
2988 : {
2989 918 : JS_ASSERT(tc->parser->tokenStream.currentToken().type == TOK_SWITCH);
2990 918 : ParseNode *pn = BinaryNode::create(PNK_SWITCH, tc);
2991 918 : if (!pn)
2992 0 : return NULL;
2993 918 : MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
2994 :
2995 : /* pn1 points to the switch's discriminant. */
2996 918 : ParseNode *pn1 = parenExpr();
2997 918 : if (!pn1)
2998 0 : return NULL;
2999 :
3000 918 : MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
3001 918 : MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
3002 :
3003 : /*
3004 : * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
3005 : * because that function states tc->topStmt->blockid.
3006 : */
3007 : StmtInfo stmtInfo;
3008 918 : PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
3009 :
3010 : /* pn2 is a list of case nodes. The default case has pn_left == NULL */
3011 918 : ParseNode *pn2 = ListNode::create(PNK_STATEMENTLIST, tc);
3012 918 : if (!pn2)
3013 0 : return NULL;
3014 918 : pn2->makeEmpty();
3015 918 : if (!GenerateBlockIdForStmtNode(pn2, tc))
3016 0 : return NULL;
3017 918 : ParseNode *saveBlock = tc->blockNode;
3018 918 : tc->blockNode = pn2;
3019 :
3020 918 : bool seenDefault = false;
3021 : TokenKind tt;
3022 5499 : while ((tt = tokenStream.getToken()) != TOK_RC) {
3023 : ParseNode *pn3;
3024 3672 : switch (tt) {
3025 : case TOK_DEFAULT:
3026 558 : if (seenDefault) {
3027 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_DEFAULTS);
3028 0 : return NULL;
3029 : }
3030 558 : seenDefault = true;
3031 558 : pn3 = BinaryNode::create(PNK_DEFAULT, tc);
3032 558 : if (!pn3)
3033 0 : return NULL;
3034 558 : break;
3035 :
3036 : case TOK_CASE:
3037 : {
3038 3114 : pn3 = BinaryNode::create(PNK_CASE, tc);
3039 3114 : if (!pn3)
3040 0 : return NULL;
3041 3114 : pn3->pn_left = expr();
3042 3114 : if (!pn3->pn_left)
3043 0 : return NULL;
3044 3114 : break;
3045 : }
3046 :
3047 : case TOK_ERROR:
3048 0 : return NULL;
3049 :
3050 : default:
3051 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_SWITCH);
3052 0 : return NULL;
3053 : }
3054 :
3055 3672 : pn2->append(pn3);
3056 3672 : if (pn2->pn_count == JS_BIT(16)) {
3057 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_CASES);
3058 0 : return NULL;
3059 : }
3060 :
3061 3672 : MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
3062 :
3063 3672 : ParseNode *pn4 = ListNode::create(PNK_STATEMENTLIST, tc);
3064 3672 : if (!pn4)
3065 0 : return NULL;
3066 3672 : pn4->makeEmpty();
3067 162459 : while ((tt = tokenStream.peekToken(TSF_OPERAND)) != TOK_RC &&
3068 : tt != TOK_CASE && tt != TOK_DEFAULT) {
3069 155124 : if (tt == TOK_ERROR)
3070 0 : return NULL;
3071 155124 : ParseNode *pn5 = statement();
3072 155124 : if (!pn5)
3073 9 : return NULL;
3074 155115 : pn4->pn_pos.end = pn5->pn_pos.end;
3075 155115 : pn4->append(pn5);
3076 : }
3077 :
3078 : /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
3079 3663 : if (pn4->pn_head)
3080 2988 : pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
3081 3663 : pn3->pn_pos.end = pn4->pn_pos.end;
3082 3663 : pn3->pn_right = pn4;
3083 : }
3084 :
3085 : /*
3086 : * Handle the case where there was a let declaration in any case in
3087 : * the switch body, but not within an inner block. If it replaced
3088 : * tc->blockNode with a new block node then we must refresh pn2 and
3089 : * then restore tc->blockNode.
3090 : */
3091 909 : if (tc->blockNode != pn2)
3092 144 : pn2 = tc->blockNode;
3093 909 : tc->blockNode = saveBlock;
3094 909 : PopStatement(tc);
3095 :
3096 909 : pn->pn_pos.end = pn2->pn_pos.end = tokenStream.currentToken().pos.end;
3097 909 : pn->pn_left = pn1;
3098 909 : pn->pn_right = pn2;
3099 909 : return pn;
3100 : }
3101 :
3102 : bool
3103 30630 : Parser::matchInOrOf(bool *isForOfp)
3104 : {
3105 30630 : if (tokenStream.matchToken(TOK_IN)) {
3106 4321 : *isForOfp = false;
3107 4321 : return true;
3108 : }
3109 26309 : if (tokenStream.matchToken(TOK_NAME)) {
3110 1035 : if (tokenStream.currentToken().name() == context->runtime->atomState.ofAtom) {
3111 1035 : *isForOfp = true;
3112 1035 : return true;
3113 : }
3114 0 : tokenStream.ungetToken();
3115 : }
3116 25274 : return false;
3117 : }
3118 :
3119 : ParseNode *
3120 29893 : Parser::forStatement()
3121 : {
3122 29893 : JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
3123 :
3124 : /* A FOR node is binary, left is loop control and right is the body. */
3125 29893 : ParseNode *pn = BinaryNode::create(PNK_FOR, tc);
3126 29893 : if (!pn)
3127 0 : return NULL;
3128 :
3129 : StmtInfo forStmt;
3130 29893 : PushStatement(tc, &forStmt, STMT_FOR_LOOP, -1);
3131 :
3132 29893 : pn->setOp(JSOP_ITER);
3133 29893 : pn->pn_iflags = 0;
3134 29893 : if (tokenStream.matchToken(TOK_NAME)) {
3135 1152 : if (tokenStream.currentToken().name() == context->runtime->atomState.eachAtom)
3136 1152 : pn->pn_iflags = JSITER_FOREACH;
3137 : else
3138 0 : tokenStream.ungetToken();
3139 : }
3140 :
3141 29893 : MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
3142 :
3143 : /*
3144 : * True if we have 'for (var/let/const ...)', except in the oddball case
3145 : * where 'let' begins a let-expression in 'for (let (...) ...)'.
3146 : */
3147 29893 : bool forDecl = false;
3148 :
3149 : /* Non-null when forDecl is true for a 'for (let ...)' statement. */
3150 29893 : StaticBlockObject *blockObj = NULL;
3151 :
3152 : /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
3153 : ParseNode *pn1;
3154 :
3155 : {
3156 29893 : TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
3157 29893 : if (tt == TOK_SEMI) {
3158 307 : if (pn->pn_iflags & JSITER_FOREACH) {
3159 0 : reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP);
3160 0 : return NULL;
3161 : }
3162 :
3163 307 : pn1 = NULL;
3164 : } else {
3165 : /*
3166 : * Set pn1 to a var list or an initializing expression.
3167 : *
3168 : * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
3169 : * of the for statement. This flag will be used by the RelExpr
3170 : * production; if it is set, then the 'in' keyword will not be
3171 : * recognized as an operator, leaving it available to be parsed as
3172 : * part of a for/in loop.
3173 : *
3174 : * A side effect of this restriction is that (unparenthesized)
3175 : * expressions involving an 'in' operator are illegal in the init
3176 : * clause of an ordinary for loop.
3177 : */
3178 29586 : tc->flags |= TCF_IN_FOR_INIT;
3179 29586 : if (tt == TOK_VAR || tt == TOK_CONST) {
3180 19056 : forDecl = true;
3181 19056 : tokenStream.consumeKnownToken(tt);
3182 19056 : pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST);
3183 : }
3184 : #if JS_HAS_BLOCK_SCOPE
3185 10530 : else if (tt == TOK_LET) {
3186 7227 : (void) tokenStream.getToken();
3187 7227 : if (tokenStream.peekToken() == TOK_LP) {
3188 81 : pn1 = letBlock(LetExpresion);
3189 : } else {
3190 7146 : forDecl = true;
3191 7146 : blockObj = StaticBlockObject::create(context);
3192 7146 : if (!blockObj)
3193 0 : return NULL;
3194 7146 : pn1 = variables(PNK_LET, blockObj, DontHoistVars);
3195 : }
3196 : }
3197 : #endif
3198 : else {
3199 3303 : pn1 = expr();
3200 : }
3201 29586 : tc->flags &= ~TCF_IN_FOR_INIT;
3202 29586 : if (!pn1)
3203 90 : return NULL;
3204 : }
3205 : }
3206 :
3207 29803 : JS_ASSERT_IF(forDecl, pn1->isArity(PN_LIST));
3208 29803 : JS_ASSERT(!!blockObj == (forDecl && pn1->isOp(JSOP_NOP)));
3209 :
3210 29803 : const TokenPos pos = tokenStream.currentToken().pos;
3211 :
3212 : /* If non-null, the parent that should be returned instead of forHead. */
3213 29803 : ParseNode *forParent = NULL;
3214 :
3215 : /*
3216 : * We can be sure that it's a for/in loop if there's still an 'in'
3217 : * keyword here, even if JavaScript recognizes 'in' as an operator,
3218 : * as we've excluded 'in' from being parsed in RelExpr by setting
3219 : * the TCF_IN_FOR_INIT flag in our TreeContext.
3220 : */
3221 : ParseNode *forHead; /* initialized by both branches. */
3222 : StmtInfo letStmt; /* used if blockObj != NULL. */
3223 : ParseNode *pn2, *pn3; /* forHead->pn_kid1 and pn_kid2. */
3224 : bool forOf;
3225 29803 : if (pn1 && matchInOrOf(&forOf)) {
3226 : /*
3227 : * Parse the rest of the for/in or for/of head.
3228 : *
3229 : * Here pn1 is everything to the left of 'in' or 'of'. At the end of
3230 : * this block, pn1 is a decl or NULL, pn2 is the assignment target that
3231 : * receives the enumeration value each iteration, and pn3 is the rhs of
3232 : * 'in'.
3233 : */
3234 4582 : forStmt.type = STMT_FOR_IN_LOOP;
3235 :
3236 : /* Set pn_iflags and rule out invalid combinations. */
3237 4582 : if (forOf && pn->pn_iflags != 0) {
3238 27 : JS_ASSERT(pn->pn_iflags == JSITER_FOREACH);
3239 27 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP);
3240 27 : return NULL;
3241 : }
3242 4555 : pn->pn_iflags |= (forOf ? JSITER_FOR_OF : JSITER_ENUMERATE);
3243 :
3244 : /* Check that the left side of the 'in' or 'of' is valid. */
3245 12603 : if (forDecl
3246 3052 : ? (pn1->pn_count > 1 || pn1->isOp(JSOP_DEFCONST)
3247 : #if JS_HAS_DESTRUCTURING
3248 3052 : || (versionNumber() == JSVERSION_1_7 &&
3249 0 : pn->isOp(JSOP_ITER) &&
3250 0 : !(pn->pn_iflags & JSITER_FOREACH) &&
3251 0 : (pn1->pn_head->isKind(PNK_RC) ||
3252 0 : (pn1->pn_head->isKind(PNK_RB) &&
3253 : pn1->pn_head->pn_count != 2) ||
3254 0 : (pn1->pn_head->isKind(PNK_ASSIGN) &&
3255 0 : (!pn1->pn_head->pn_left->isKind(PNK_RB) ||
3256 : pn1->pn_head->pn_left->pn_count != 2))))
3257 : #endif
3258 : )
3259 1503 : : (!pn1->isKind(PNK_NAME) &&
3260 117 : !pn1->isKind(PNK_DOT) &&
3261 : #if JS_HAS_DESTRUCTURING
3262 72 : ((versionNumber() == JSVERSION_1_7 &&
3263 0 : pn->isOp(JSOP_ITER) &&
3264 0 : !(pn->pn_iflags & JSITER_FOREACH))
3265 0 : ? (!pn1->isKind(PNK_RB) || pn1->pn_count != 2)
3266 117 : : (!pn1->isKind(PNK_RB) && !pn1->isKind(PNK_RC))) &&
3267 : #endif
3268 45 : !pn1->isKind(PNK_LP) &&
3269 : #if JS_HAS_XML_SUPPORT
3270 45 : !pn1->isKind(PNK_XMLUNARY) &&
3271 : #endif
3272 45 : !pn1->isKind(PNK_LB)))
3273 : {
3274 27 : reportErrorNumber(pn1, JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE);
3275 27 : return NULL;
3276 : }
3277 :
3278 : /*
3279 : * After the following if-else, pn2 will point to the name or
3280 : * destructuring pattern on in's left. pn1 will point to the decl, if
3281 : * any, else NULL. Note that the "declaration with initializer" case
3282 : * rewrites the loop-head, moving the decl and setting pn1 to NULL.
3283 : */
3284 4528 : pn2 = NULL;
3285 4528 : unsigned dflag = PND_ASSIGNED;
3286 4528 : if (forDecl) {
3287 : /* Tell EmitVariables that pn1 is part of a for/in. */
3288 3052 : pn1->pn_xflags |= PNX_FORINVAR;
3289 :
3290 3052 : pn2 = pn1->pn_head;
3291 6095 : if ((pn2->isKind(PNK_NAME) && pn2->maybeExpr())
3292 : #if JS_HAS_DESTRUCTURING
3293 3043 : || pn2->isKind(PNK_ASSIGN)
3294 : #endif
3295 : )
3296 : {
3297 : /*
3298 : * Declaration with initializer.
3299 : *
3300 : * Rewrite 'for (<decl> x = i in o)' where <decl> is 'var' or
3301 : * 'const' to hoist the initializer or the entire decl out of
3302 : * the loop head.
3303 : */
3304 : #if JS_HAS_BLOCK_SCOPE
3305 36 : if (blockObj) {
3306 0 : reportErrorNumber(pn2, JSREPORT_ERROR, JSMSG_INVALID_FOR_IN_INIT);
3307 0 : return NULL;
3308 : }
3309 : #endif /* JS_HAS_BLOCK_SCOPE */
3310 :
3311 36 : ParseNode *pnseq = ListNode::create(PNK_SEQ, tc);
3312 36 : if (!pnseq)
3313 0 : return NULL;
3314 :
3315 36 : dflag = PND_INITIALIZED;
3316 :
3317 : /*
3318 : * All of 'var x = i' is hoisted above 'for (x in o)',
3319 : * so clear PNX_FORINVAR.
3320 : *
3321 : * Request JSOP_POP here since the var is for a simple
3322 : * name (it is not a destructuring binding's left-hand
3323 : * side) and it has an initializer.
3324 : */
3325 36 : pn1->pn_xflags &= ~PNX_FORINVAR;
3326 36 : pn1->pn_xflags |= PNX_POPVAR;
3327 36 : pnseq->initList(pn1);
3328 36 : pn1 = NULL;
3329 :
3330 : #if JS_HAS_DESTRUCTURING
3331 36 : if (pn2->isKind(PNK_ASSIGN)) {
3332 27 : pn2 = pn2->pn_left;
3333 27 : JS_ASSERT(pn2->isKind(PNK_RB) || pn2->isKind(PNK_RC) ||
3334 27 : pn2->isKind(PNK_NAME));
3335 : }
3336 : #endif
3337 36 : pnseq->append(pn);
3338 36 : forParent = pnseq;
3339 : }
3340 : } else {
3341 : /* Not a declaration. */
3342 1476 : JS_ASSERT(!blockObj);
3343 1476 : pn2 = pn1;
3344 1476 : pn1 = NULL;
3345 :
3346 1476 : if (!setAssignmentLhsOps(pn2, JSOP_NOP))
3347 0 : return NULL;
3348 : }
3349 :
3350 4528 : pn3 = expr();
3351 4528 : if (!pn3)
3352 9 : return NULL;
3353 :
3354 4519 : if (blockObj) {
3355 : /*
3356 : * Now that the pn3 has been parsed, push the let scope. To hold
3357 : * the blockObj for the emitter, wrap the TOK_LEXICALSCOPE node
3358 : * created by PushLetScope around the for's initializer. This also
3359 : * serves to indicate the let-decl to the emitter.
3360 : */
3361 1368 : ParseNode *block = PushLetScope(context, tc, *blockObj, &letStmt);
3362 1368 : if (!block)
3363 0 : return NULL;
3364 1368 : letStmt.flags |= SIF_FOR_BLOCK;
3365 1368 : block->pn_expr = pn1;
3366 1368 : pn1 = block;
3367 : }
3368 :
3369 4519 : if (forDecl) {
3370 : /*
3371 : * pn2 is part of a declaration. Make a copy that can be passed to
3372 : * EmitAssignment. Take care to do this after PushLetScope has
3373 : * Define's the new binding since this pn2->isDefn() which tells
3374 : * CloneLeftHandSide to make the new pn2 a use.
3375 : */
3376 3052 : pn2 = CloneLeftHandSide(pn2, tc);
3377 3052 : if (!pn2)
3378 0 : return NULL;
3379 : }
3380 :
3381 4519 : switch (pn2->getKind()) {
3382 : case PNK_NAME:
3383 : /* Beware 'for (arguments in ...)' with or without a 'var'. */
3384 4258 : NoteLValue(context, pn2, tc, dflag);
3385 4258 : break;
3386 :
3387 : #if JS_HAS_DESTRUCTURING
3388 : case PNK_ASSIGN:
3389 0 : JS_NOT_REACHED("forStatement TOK_ASSIGN");
3390 : break;
3391 :
3392 : case PNK_RB:
3393 : case PNK_RC:
3394 198 : if (versionNumber() == JSVERSION_1_7) {
3395 : /*
3396 : * Destructuring for-in requires [key, value] enumeration
3397 : * in JS1.7.
3398 : */
3399 0 : JS_ASSERT(pn->isOp(JSOP_ITER));
3400 0 : if (!(pn->pn_iflags & JSITER_FOREACH))
3401 0 : pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
3402 : }
3403 198 : break;
3404 : #endif
3405 :
3406 : default:;
3407 : }
3408 :
3409 4519 : forHead = TernaryNode::create(PNK_FORIN, tc);
3410 4519 : if (!forHead)
3411 0 : return NULL;
3412 : } else {
3413 25221 : if (blockObj) {
3414 : /*
3415 : * Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }'
3416 : * to induce the correct scoping for A.
3417 : */
3418 5688 : ParseNode *block = PushLetScope(context, tc, *blockObj, &letStmt);
3419 5688 : if (!block)
3420 0 : return NULL;
3421 5688 : letStmt.flags |= SIF_FOR_BLOCK;
3422 :
3423 5688 : ParseNode *let = new_<BinaryNode>(PNK_LET, JSOP_NOP, pos, pn1, block);
3424 5688 : if (!let)
3425 0 : return NULL;
3426 :
3427 5688 : pn1 = NULL;
3428 5688 : block->pn_expr = pn;
3429 5688 : forParent = let;
3430 : }
3431 :
3432 25221 : if (pn->pn_iflags & JSITER_FOREACH) {
3433 0 : reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP);
3434 0 : return NULL;
3435 : }
3436 25221 : pn->setOp(JSOP_NOP);
3437 :
3438 : /* Parse the loop condition or null into pn2. */
3439 25221 : MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
3440 25221 : if (tokenStream.peekToken(TSF_OPERAND) == TOK_SEMI) {
3441 514 : pn2 = NULL;
3442 : } else {
3443 24707 : pn2 = expr();
3444 24707 : if (!pn2)
3445 0 : return NULL;
3446 : }
3447 :
3448 : /* Parse the update expression or null into pn3. */
3449 25221 : MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
3450 25221 : if (tokenStream.peekToken(TSF_OPERAND) == TOK_RP) {
3451 631 : pn3 = NULL;
3452 : } else {
3453 24590 : pn3 = expr();
3454 24590 : if (!pn3)
3455 0 : return NULL;
3456 : }
3457 :
3458 25221 : forHead = TernaryNode::create(PNK_FORHEAD, tc);
3459 25221 : if (!forHead)
3460 0 : return NULL;
3461 : }
3462 :
3463 29740 : forHead->pn_pos = pos;
3464 29740 : forHead->setOp(JSOP_NOP);
3465 29740 : forHead->pn_kid1 = pn1;
3466 29740 : forHead->pn_kid2 = pn2;
3467 29740 : forHead->pn_kid3 = pn3;
3468 29740 : pn->pn_left = forHead;
3469 :
3470 29740 : MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
3471 :
3472 : /* Parse the loop body. */
3473 29686 : ParseNode *body = statement();
3474 29686 : if (!body)
3475 9 : return NULL;
3476 :
3477 : /* Record the absolute line number for source note emission. */
3478 29677 : pn->pn_pos.end = body->pn_pos.end;
3479 29677 : pn->pn_right = body;
3480 :
3481 29677 : if (forParent) {
3482 5724 : forParent->pn_pos.begin = pn->pn_pos.begin;
3483 5724 : forParent->pn_pos.end = pn->pn_pos.end;
3484 : }
3485 :
3486 : #if JS_HAS_BLOCK_SCOPE
3487 29677 : if (blockObj)
3488 7047 : PopStatement(tc);
3489 : #endif
3490 29677 : PopStatement(tc);
3491 29677 : return forParent ? forParent : pn;
3492 : }
3493 :
3494 : ParseNode *
3495 87255 : Parser::tryStatement()
3496 : {
3497 87255 : JS_ASSERT(tokenStream.isCurrentTokenType(TOK_TRY));
3498 :
3499 : /*
3500 : * try nodes are ternary.
3501 : * kid1 is the try statement
3502 : * kid2 is the catch node list or null
3503 : * kid3 is the finally statement
3504 : *
3505 : * catch nodes are ternary.
3506 : * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
3507 : * kid2 is the catch guard or null if no guard
3508 : * kid3 is the catch block
3509 : *
3510 : * catch lvalue nodes are either:
3511 : * TOK_NAME for a single identifier
3512 : * TOK_RB or TOK_RC for a destructuring left-hand side
3513 : *
3514 : * finally nodes are TOK_LC statement lists.
3515 : */
3516 87255 : ParseNode *pn = TernaryNode::create(PNK_TRY, tc);
3517 87255 : if (!pn)
3518 0 : return NULL;
3519 87255 : pn->setOp(JSOP_NOP);
3520 :
3521 87255 : MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
3522 : StmtInfo stmtInfo;
3523 87255 : if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, tc))
3524 0 : return NULL;
3525 87255 : pn->pn_kid1 = statements();
3526 87255 : if (!pn->pn_kid1)
3527 0 : return NULL;
3528 87255 : MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
3529 87255 : PopStatement(tc);
3530 :
3531 : ParseNode *lastCatch;
3532 87255 : ParseNode *catchList = NULL;
3533 87255 : TokenKind tt = tokenStream.getToken();
3534 87255 : if (tt == TOK_CATCH) {
3535 87120 : catchList = ListNode::create(PNK_CATCHLIST, tc);
3536 87120 : if (!catchList)
3537 0 : return NULL;
3538 87120 : catchList->makeEmpty();
3539 87120 : lastCatch = NULL;
3540 :
3541 87120 : do {
3542 : ParseNode *pnblock;
3543 87120 : BindData data;
3544 :
3545 : /* Check for another catch after unconditional catch. */
3546 87120 : if (lastCatch && !lastCatch->pn_kid2) {
3547 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_AFTER_GENERAL);
3548 0 : return NULL;
3549 : }
3550 :
3551 : /*
3552 : * Create a lexical scope node around the whole catch clause,
3553 : * including the head.
3554 : */
3555 87120 : pnblock = PushLexicalScope(context, tc, &stmtInfo);
3556 87120 : if (!pnblock)
3557 0 : return NULL;
3558 87120 : stmtInfo.type = STMT_CATCH;
3559 :
3560 : /*
3561 : * Legal catch forms are:
3562 : * catch (lhs)
3563 : * catch (lhs if <boolean_expression>)
3564 : * where lhs is a name or a destructuring left-hand side.
3565 : * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
3566 : */
3567 87120 : ParseNode *pn2 = TernaryNode::create(PNK_CATCH, tc);
3568 87120 : if (!pn2)
3569 0 : return NULL;
3570 87120 : pnblock->pn_expr = pn2;
3571 87120 : MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
3572 :
3573 : /*
3574 : * Contrary to ECMA Ed. 3, the catch variable is lexically
3575 : * scoped, not a property of a new Object instance. This is
3576 : * an intentional change that anticipates ECMA Ed. 4.
3577 : */
3578 87120 : data.initLet(HoistVars, *tc->blockChain, JSMSG_TOO_MANY_CATCH_VARS);
3579 87120 : JS_ASSERT(data.let.blockObj && data.let.blockObj == pnblock->pn_objbox->object);
3580 :
3581 87120 : tt = tokenStream.getToken();
3582 : ParseNode *pn3;
3583 87120 : switch (tt) {
3584 : #if JS_HAS_DESTRUCTURING
3585 : case TOK_LB:
3586 : case TOK_LC:
3587 0 : pn3 = destructuringExpr(&data, tt);
3588 0 : if (!pn3)
3589 0 : return NULL;
3590 0 : break;
3591 : #endif
3592 :
3593 : case TOK_NAME:
3594 : {
3595 87120 : JSAtom *label = tokenStream.currentToken().name();
3596 87120 : pn3 = NewBindingNode(label, tc);
3597 87120 : if (!pn3)
3598 0 : return NULL;
3599 87120 : data.pn = pn3;
3600 87120 : if (!data.binder(context, &data, label, tc))
3601 0 : return NULL;
3602 87120 : break;
3603 : }
3604 :
3605 : default:
3606 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_IDENTIFIER);
3607 0 : return NULL;
3608 : }
3609 :
3610 87120 : pn2->pn_kid1 = pn3;
3611 : #if JS_HAS_CATCH_GUARD
3612 : /*
3613 : * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
3614 : * to avoid conflicting with the JS2/ECMAv4 type annotation
3615 : * catchguard syntax.
3616 : */
3617 87120 : if (tokenStream.matchToken(TOK_IF)) {
3618 45 : pn2->pn_kid2 = expr();
3619 45 : if (!pn2->pn_kid2)
3620 0 : return NULL;
3621 : }
3622 : #endif
3623 87120 : MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
3624 :
3625 87120 : MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
3626 87120 : pn2->pn_kid3 = statements();
3627 87120 : if (!pn2->pn_kid3)
3628 0 : return NULL;
3629 87120 : MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
3630 87120 : PopStatement(tc);
3631 :
3632 87120 : catchList->append(pnblock);
3633 87120 : lastCatch = pn2;
3634 87120 : tt = tokenStream.getToken(TSF_OPERAND);
3635 : } while (tt == TOK_CATCH);
3636 : }
3637 87255 : pn->pn_kid2 = catchList;
3638 :
3639 87255 : if (tt == TOK_FINALLY) {
3640 180 : MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
3641 180 : if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, tc))
3642 0 : return NULL;
3643 180 : pn->pn_kid3 = statements();
3644 180 : if (!pn->pn_kid3)
3645 0 : return NULL;
3646 180 : MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
3647 180 : PopStatement(tc);
3648 : } else {
3649 87075 : tokenStream.ungetToken();
3650 : }
3651 87255 : if (!catchList && !pn->pn_kid3) {
3652 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_OR_FINALLY);
3653 0 : return NULL;
3654 : }
3655 87255 : return pn;
3656 : }
3657 :
3658 : ParseNode *
3659 648 : Parser::withStatement()
3660 : {
3661 648 : JS_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
3662 :
3663 : /*
3664 : * In most cases, we want the constructs forbidden in strict mode
3665 : * code to be a subset of those that JSOPTION_STRICT warns about, and
3666 : * we should use ReportStrictModeError. However, 'with' is the sole
3667 : * instance of a construct that is forbidden in strict mode code, but
3668 : * doesn't even merit a warning under JSOPTION_STRICT. See
3669 : * https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
3670 : */
3671 648 : if (tc->flags & TCF_STRICT_MODE_CODE) {
3672 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_STRICT_CODE_WITH);
3673 0 : return NULL;
3674 : }
3675 :
3676 648 : ParseNode *pn = BinaryNode::create(PNK_WITH, tc);
3677 648 : if (!pn)
3678 0 : return NULL;
3679 648 : MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
3680 648 : ParseNode *pn2 = parenExpr();
3681 648 : if (!pn2)
3682 0 : return NULL;
3683 648 : MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
3684 648 : pn->pn_left = pn2;
3685 :
3686 648 : ParseNode *oldWith = tc->innermostWith;
3687 648 : tc->innermostWith = pn;
3688 :
3689 : StmtInfo stmtInfo;
3690 648 : PushStatement(tc, &stmtInfo, STMT_WITH, -1);
3691 648 : pn2 = statement();
3692 648 : if (!pn2)
3693 0 : return NULL;
3694 648 : PopStatement(tc);
3695 :
3696 648 : pn->pn_pos.end = pn2->pn_pos.end;
3697 648 : pn->pn_right = pn2;
3698 648 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
3699 648 : tc->innermostWith = oldWith;
3700 :
3701 : /*
3702 : * Make sure to deoptimize lexical dependencies inside the |with|
3703 : * to safely optimize binding globals (see bug 561923).
3704 : */
3705 1566 : for (AtomDefnRange r = tc->lexdeps->all(); !r.empty(); r.popFront()) {
3706 918 : Definition *defn = r.front().value();
3707 918 : Definition *lexdep = defn->resolve();
3708 918 : DeoptimizeUsesWithin(lexdep, pn->pn_pos);
3709 : }
3710 :
3711 648 : return pn;
3712 : }
3713 :
3714 : #if JS_HAS_BLOCK_SCOPE
3715 : ParseNode *
3716 4716 : Parser::letStatement()
3717 : {
3718 : ParseNode *pn;
3719 : do {
3720 : /* Check for a let statement or let expression. */
3721 4716 : if (tokenStream.peekToken() == TOK_LP) {
3722 2385 : pn = letBlock(LetStatement);
3723 2385 : if (!pn)
3724 144 : return NULL;
3725 :
3726 2241 : JS_ASSERT(pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI));
3727 2241 : if (pn->isKind(PNK_LET) && pn->pn_expr->getOp() == JSOP_LEAVEBLOCK)
3728 2007 : return pn;
3729 :
3730 : /* Let expressions require automatic semicolon insertion. */
3731 234 : JS_ASSERT(pn->isKind(PNK_SEMI) || pn->isOp(JSOP_NOP));
3732 234 : break;
3733 : }
3734 :
3735 : /*
3736 : * This is a let declaration. We must be directly under a block per the
3737 : * proposed ES4 specs, but not an implicit block created due to
3738 : * 'for (let ...)'. If we pass this error test, make the enclosing
3739 : * StmtInfo be our scope. Further let declarations in this block will
3740 : * find this scope statement and use the same block object.
3741 : *
3742 : * If we are the first let declaration in this block (i.e., when the
3743 : * enclosing maybe-scope StmtInfo isn't yet a scope statement) then
3744 : * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
3745 : */
3746 2331 : StmtInfo *stmt = tc->topStmt;
3747 6921 : if (stmt &&
3748 4590 : (!STMT_MAYBE_SCOPE(stmt) || (stmt->flags & SIF_FOR_BLOCK))) {
3749 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LET_DECL_NOT_IN_BLOCK);
3750 0 : return NULL;
3751 : }
3752 :
3753 2331 : if (stmt && (stmt->flags & SIF_SCOPE)) {
3754 270 : JS_ASSERT(tc->blockChain == stmt->blockObj);
3755 : } else {
3756 2061 : if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) {
3757 : /*
3758 : * ES4 specifies that let at top level and at body-block scope
3759 : * does not shadow var, so convert back to var.
3760 : */
3761 153 : pn = variables(PNK_VAR);
3762 153 : if (!pn)
3763 0 : return NULL;
3764 153 : pn->pn_xflags |= PNX_POPVAR;
3765 153 : break;
3766 : }
3767 :
3768 : /*
3769 : * Some obvious assertions here, but they may help clarify the
3770 : * situation. This stmt is not yet a scope, so it must not be a
3771 : * catch block (catch is a lexical scope by definition).
3772 : */
3773 1908 : JS_ASSERT(!(stmt->flags & SIF_SCOPE));
3774 1908 : JS_ASSERT(stmt != tc->topScopeStmt);
3775 0 : JS_ASSERT(stmt->type == STMT_BLOCK ||
3776 : stmt->type == STMT_SWITCH ||
3777 : stmt->type == STMT_TRY ||
3778 1908 : stmt->type == STMT_FINALLY);
3779 1908 : JS_ASSERT(!stmt->downScope);
3780 :
3781 : /* Convert the block statement into a scope statement. */
3782 1908 : StaticBlockObject *blockObj = StaticBlockObject::create(tc->parser->context);
3783 1908 : if (!blockObj)
3784 0 : return NULL;
3785 :
3786 1908 : ObjectBox *blockbox = tc->parser->newObjectBox(blockObj);
3787 1908 : if (!blockbox)
3788 0 : return NULL;
3789 :
3790 : /*
3791 : * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
3792 : * list stack, if it isn't already there. If it is there, but it
3793 : * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
3794 : * block.
3795 : */
3796 1908 : stmt->flags |= SIF_SCOPE;
3797 1908 : stmt->downScope = tc->topScopeStmt;
3798 1908 : tc->topScopeStmt = stmt;
3799 :
3800 1908 : blockObj->setEnclosingBlock(tc->blockChain);
3801 1908 : tc->blockChain = blockObj;
3802 1908 : stmt->blockObj = blockObj;
3803 :
3804 : #ifdef DEBUG
3805 1908 : ParseNode *tmp = tc->blockNode;
3806 1908 : JS_ASSERT(!tmp || !tmp->isKind(PNK_LEXICALSCOPE));
3807 : #endif
3808 :
3809 : /* Create a new lexical scope node for these statements. */
3810 1908 : ParseNode *pn1 = LexicalScopeNode::create(PNK_LEXICALSCOPE, tc);
3811 1908 : if (!pn1)
3812 0 : return NULL;
3813 :
3814 1908 : pn1->setOp(JSOP_LEAVEBLOCK);
3815 1908 : pn1->pn_pos = tc->blockNode->pn_pos;
3816 1908 : pn1->pn_objbox = blockbox;
3817 1908 : pn1->pn_expr = tc->blockNode;
3818 1908 : pn1->pn_blockid = tc->blockNode->pn_blockid;
3819 1908 : tc->blockNode = pn1;
3820 : }
3821 :
3822 2178 : pn = variables(PNK_LET, tc->blockChain, HoistVars);
3823 2178 : if (!pn)
3824 27 : return NULL;
3825 2151 : pn->pn_xflags = PNX_POPVAR;
3826 : } while (0);
3827 :
3828 : /* Check termination of this primitive statement. */
3829 2538 : return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
3830 : }
3831 : #endif
3832 :
3833 : ParseNode *
3834 557492 : Parser::expressionStatement()
3835 : {
3836 557492 : tokenStream.ungetToken();
3837 557492 : ParseNode *pn2 = expr();
3838 557492 : if (!pn2)
3839 193 : return NULL;
3840 :
3841 557299 : if (tokenStream.peekToken() == TOK_COLON) {
3842 207 : if (!pn2->isKind(PNK_NAME)) {
3843 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_LABEL);
3844 0 : return NULL;
3845 : }
3846 207 : JSAtom *label = pn2->pn_atom;
3847 684 : for (StmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
3848 477 : if (stmt->type == STMT_LABEL && stmt->label == label) {
3849 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_DUPLICATE_LABEL);
3850 0 : return NULL;
3851 : }
3852 : }
3853 207 : ForgetUse(pn2);
3854 :
3855 207 : (void) tokenStream.getToken();
3856 :
3857 : /* Push a label struct and parse the statement. */
3858 : StmtInfo stmtInfo;
3859 207 : PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
3860 207 : stmtInfo.label = label;
3861 207 : ParseNode *pn = statement();
3862 207 : if (!pn)
3863 0 : return NULL;
3864 :
3865 : /* Normalize empty statement to empty block for the decompiler. */
3866 207 : if (pn->isKind(PNK_SEMI) && !pn->pn_kid) {
3867 0 : pn->setKind(PNK_STATEMENTLIST);
3868 0 : pn->setArity(PN_LIST);
3869 0 : pn->makeEmpty();
3870 : }
3871 :
3872 : /* Pop the label, set pn_expr, and return early. */
3873 207 : PopStatement(tc);
3874 207 : pn2->setKind(PNK_COLON);
3875 207 : pn2->pn_pos.end = pn->pn_pos.end;
3876 207 : pn2->pn_expr = pn;
3877 207 : return pn2;
3878 : }
3879 :
3880 557092 : ParseNode *pn = UnaryNode::create(PNK_SEMI, tc);
3881 557092 : if (!pn)
3882 0 : return NULL;
3883 557092 : pn->pn_pos = pn2->pn_pos;
3884 557092 : pn->pn_kid = pn2;
3885 :
3886 557092 : if (pn2->getKind() == PNK_ASSIGN) {
3887 : /*
3888 : * Keep track of all apparent methods created by assignments such
3889 : * as this.foo = function (...) {...} in a function that could end
3890 : * up a constructor function. See Parser::setFunctionKinds.
3891 : */
3892 368649 : JS_ASSERT(pn2->isOp(JSOP_NOP));
3893 415505 : if (tc->funbox &&
3894 35075 : pn2->pn_left->isOp(JSOP_SETPROP) &&
3895 8235 : pn2->pn_left->pn_expr->isKind(PNK_THIS) &&
3896 3546 : pn2->pn_right->isOp(JSOP_LAMBDA))
3897 : {
3898 189 : JS_ASSERT(!pn2->isDefn());
3899 189 : JS_ASSERT(!pn2->isUsed());
3900 189 : pn2->pn_right->pn_link = tc->funbox->methods;
3901 189 : tc->funbox->methods = pn2->pn_right;
3902 : }
3903 : }
3904 :
3905 : /* Check termination of this primitive statement. */
3906 557092 : return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
3907 : }
3908 :
3909 : ParseNode *
3910 1085242 : Parser::statement()
3911 : {
3912 : ParseNode *pn;
3913 :
3914 1085242 : JS_CHECK_RECURSION(context, return NULL);
3915 :
3916 1085242 : switch (tokenStream.getToken(TSF_OPERAND)) {
3917 : case TOK_FUNCTION:
3918 : {
3919 : #if JS_HAS_XML_SUPPORT
3920 28648 : if (!tc->inStrictMode()) {
3921 28468 : TokenKind tt = tokenStream.peekToken(TSF_KEYWORD_IS_NAME);
3922 28468 : if (tt == TOK_DBLCOLON)
3923 18 : return expressionStatement();
3924 : }
3925 : #endif
3926 28630 : return functionStmt();
3927 : }
3928 :
3929 : case TOK_IF:
3930 : {
3931 : /* An IF node has three kids: condition, then, and optional else. */
3932 93371 : pn = TernaryNode::create(PNK_IF, tc);
3933 93371 : if (!pn)
3934 0 : return NULL;
3935 93371 : ParseNode *pn1 = condition();
3936 93371 : if (!pn1)
3937 0 : return NULL;
3938 :
3939 : StmtInfo stmtInfo;
3940 93371 : PushStatement(tc, &stmtInfo, STMT_IF, -1);
3941 93371 : ParseNode *pn2 = statement();
3942 93371 : if (!pn2)
3943 36 : return NULL;
3944 :
3945 99619 : if (pn2->isKind(PNK_SEMI) &&
3946 6284 : !pn2->pn_kid &&
3947 0 : !reportErrorNumber(NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_EMPTY_CONSEQUENT))
3948 : {
3949 0 : return NULL;
3950 : }
3951 :
3952 : ParseNode *pn3;
3953 93335 : if (tokenStream.matchToken(TOK_ELSE, TSF_OPERAND)) {
3954 3304 : stmtInfo.type = STMT_ELSE;
3955 3304 : pn3 = statement();
3956 3304 : if (!pn3)
3957 0 : return NULL;
3958 3304 : pn->pn_pos.end = pn3->pn_pos.end;
3959 : } else {
3960 90031 : pn3 = NULL;
3961 90031 : pn->pn_pos.end = pn2->pn_pos.end;
3962 : }
3963 93335 : PopStatement(tc);
3964 93335 : pn->pn_kid1 = pn1;
3965 93335 : pn->pn_kid2 = pn2;
3966 93335 : pn->pn_kid3 = pn3;
3967 93335 : return pn;
3968 : }
3969 :
3970 : case TOK_SWITCH:
3971 918 : return switchStatement();
3972 :
3973 : case TOK_WHILE:
3974 : {
3975 2127 : pn = BinaryNode::create(PNK_WHILE, tc);
3976 2127 : if (!pn)
3977 0 : return NULL;
3978 : StmtInfo stmtInfo;
3979 2127 : PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
3980 2127 : ParseNode *pn2 = condition();
3981 2127 : if (!pn2)
3982 0 : return NULL;
3983 2127 : pn->pn_left = pn2;
3984 2127 : ParseNode *pn3 = statement();
3985 2127 : if (!pn3)
3986 0 : return NULL;
3987 2127 : PopStatement(tc);
3988 2127 : pn->pn_pos.end = pn3->pn_pos.end;
3989 2127 : pn->pn_right = pn3;
3990 2127 : return pn;
3991 : }
3992 :
3993 : case TOK_DO:
3994 : {
3995 189 : pn = BinaryNode::create(PNK_DOWHILE, tc);
3996 189 : if (!pn)
3997 0 : return NULL;
3998 : StmtInfo stmtInfo;
3999 189 : PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
4000 189 : ParseNode *pn2 = statement();
4001 189 : if (!pn2)
4002 0 : return NULL;
4003 189 : pn->pn_left = pn2;
4004 189 : MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
4005 189 : ParseNode *pn3 = condition();
4006 189 : if (!pn3)
4007 0 : return NULL;
4008 189 : PopStatement(tc);
4009 189 : pn->pn_pos.end = pn3->pn_pos.end;
4010 189 : pn->pn_right = pn3;
4011 189 : if (versionNumber() != JSVERSION_ECMA_3) {
4012 : /*
4013 : * All legacy and extended versions must do automatic semicolon
4014 : * insertion after do-while. See the testcase and discussion in
4015 : * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4016 : */
4017 189 : (void) tokenStream.matchToken(TOK_SEMI);
4018 189 : return pn;
4019 : }
4020 0 : break;
4021 : }
4022 :
4023 : case TOK_FOR:
4024 29893 : return forStatement();
4025 :
4026 : case TOK_TRY:
4027 87255 : return tryStatement();
4028 :
4029 : case TOK_THROW:
4030 : {
4031 2887 : pn = UnaryNode::create(PNK_THROW, tc);
4032 2887 : if (!pn)
4033 0 : return NULL;
4034 :
4035 : /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
4036 2887 : TokenKind tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
4037 2887 : if (tt == TOK_ERROR)
4038 0 : return NULL;
4039 2887 : if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
4040 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
4041 0 : return NULL;
4042 : }
4043 :
4044 2887 : ParseNode *pn2 = expr();
4045 2887 : if (!pn2)
4046 0 : return NULL;
4047 2887 : pn->pn_pos.end = pn2->pn_pos.end;
4048 2887 : pn->setOp(JSOP_THROW);
4049 2887 : pn->pn_kid = pn2;
4050 2887 : break;
4051 : }
4052 :
4053 : /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
4054 : case TOK_CATCH:
4055 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_WITHOUT_TRY);
4056 0 : return NULL;
4057 :
4058 : case TOK_FINALLY:
4059 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_FINALLY_WITHOUT_TRY);
4060 0 : return NULL;
4061 :
4062 : case TOK_BREAK:
4063 : {
4064 1728 : TokenPtr begin = tokenStream.currentToken().pos.begin;
4065 : PropertyName *label;
4066 1728 : if (!MatchLabel(context, &tokenStream, &label))
4067 0 : return NULL;
4068 1728 : TokenPtr end = tokenStream.currentToken().pos.end;
4069 1728 : pn = new_<BreakStatement>(label, begin, end);
4070 1728 : if (!pn)
4071 0 : return NULL;
4072 1728 : StmtInfo *stmt = tc->topStmt;
4073 1728 : if (label) {
4074 522 : for (; ; stmt = stmt->down) {
4075 630 : if (!stmt) {
4076 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND);
4077 0 : return NULL;
4078 : }
4079 630 : if (stmt->type == STMT_LABEL && stmt->label == label)
4080 : break;
4081 : }
4082 : } else {
4083 1287 : for (; ; stmt = stmt->down) {
4084 2907 : if (!stmt) {
4085 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOUGH_BREAK);
4086 0 : return NULL;
4087 : }
4088 2907 : if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
4089 1620 : break;
4090 : }
4091 : }
4092 1728 : break;
4093 : }
4094 :
4095 : case TOK_CONTINUE:
4096 : {
4097 153 : TokenPtr begin = tokenStream.currentToken().pos.begin;
4098 : PropertyName *label;
4099 153 : if (!MatchLabel(context, &tokenStream, &label))
4100 0 : return NULL;
4101 153 : TokenPtr end = tokenStream.currentToken().pos.begin;
4102 153 : pn = new_<ContinueStatement>(label, begin, end);
4103 153 : if (!pn)
4104 0 : return NULL;
4105 153 : StmtInfo *stmt = tc->topStmt;
4106 153 : if (label) {
4107 189 : for (StmtInfo *stmt2 = NULL; ; stmt = stmt->down) {
4108 189 : if (!stmt) {
4109 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND);
4110 0 : return NULL;
4111 : }
4112 189 : if (stmt->type == STMT_LABEL) {
4113 36 : if (stmt->label == label) {
4114 36 : if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
4115 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_CONTINUE);
4116 0 : return NULL;
4117 : }
4118 : break;
4119 : }
4120 : } else {
4121 153 : stmt2 = stmt;
4122 : }
4123 : }
4124 : } else {
4125 252 : for (; ; stmt = stmt->down) {
4126 369 : if (!stmt) {
4127 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_CONTINUE);
4128 0 : return NULL;
4129 : }
4130 369 : if (STMT_IS_LOOP(stmt))
4131 117 : break;
4132 : }
4133 : }
4134 153 : break;
4135 : }
4136 :
4137 : case TOK_WITH:
4138 648 : return withStatement();
4139 :
4140 : case TOK_VAR:
4141 74956 : pn = variables(PNK_VAR);
4142 74956 : if (!pn)
4143 9 : return NULL;
4144 :
4145 : /* Tell js_EmitTree to generate a final POP. */
4146 74947 : pn->pn_xflags |= PNX_POPVAR;
4147 74947 : break;
4148 :
4149 : case TOK_CONST:
4150 56187 : pn = variables(PNK_CONST);
4151 56187 : if (!pn)
4152 0 : return NULL;
4153 :
4154 : /* Tell js_EmitTree to generate a final POP. */
4155 56187 : pn->pn_xflags |= PNX_POPVAR;
4156 56187 : break;
4157 :
4158 : #if JS_HAS_BLOCK_SCOPE
4159 : case TOK_LET:
4160 4716 : return letStatement();
4161 : #endif /* JS_HAS_BLOCK_SCOPE */
4162 :
4163 : case TOK_RETURN:
4164 38556 : pn = returnOrYield(false);
4165 38556 : if (!pn)
4166 0 : return NULL;
4167 38556 : break;
4168 :
4169 : case TOK_LC:
4170 : {
4171 : unsigned oldflags;
4172 :
4173 99537 : oldflags = tc->flags;
4174 99537 : tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
4175 : StmtInfo stmtInfo;
4176 99537 : if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, tc))
4177 0 : return NULL;
4178 99537 : pn = statements();
4179 99537 : if (!pn)
4180 36 : return NULL;
4181 :
4182 99501 : MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
4183 99501 : PopStatement(tc);
4184 :
4185 : /*
4186 : * If we contain a function statement and our container is top-level
4187 : * or another block, flag pn to preserve braces when decompiling.
4188 : */
4189 99753 : if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
4190 252 : (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
4191 99 : pn->pn_xflags |= PNX_NEEDBRACES;
4192 : }
4193 99501 : tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
4194 99501 : return pn;
4195 : }
4196 :
4197 : case TOK_SEMI:
4198 1656 : pn = UnaryNode::create(PNK_SEMI, tc);
4199 1656 : if (!pn)
4200 0 : return NULL;
4201 1656 : return pn;
4202 :
4203 : case TOK_DEBUGGER:
4204 4334 : pn = tc->parser->new_<DebuggerStatement>(tokenStream.currentToken().pos);
4205 4334 : if (!pn)
4206 0 : return NULL;
4207 4334 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
4208 4334 : break;
4209 :
4210 : #if JS_HAS_XML_SUPPORT
4211 : case TOK_DEFAULT:
4212 : {
4213 9 : if (tc->inStrictMode())
4214 0 : return expressionStatement();
4215 :
4216 9 : pn = UnaryNode::create(PNK_DEFXMLNS, tc);
4217 9 : if (!pn)
4218 0 : return NULL;
4219 45 : if (!tokenStream.matchToken(TOK_NAME) ||
4220 9 : tokenStream.currentToken().name() != context->runtime->atomState.xmlAtom ||
4221 9 : !tokenStream.matchToken(TOK_NAME) ||
4222 9 : tokenStream.currentToken().name() != context->runtime->atomState.namespaceAtom ||
4223 9 : !tokenStream.matchToken(TOK_ASSIGN))
4224 : {
4225 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_DEFAULT_XML_NAMESPACE);
4226 0 : return NULL;
4227 : }
4228 :
4229 9 : JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NOP);
4230 :
4231 : /* Is this an E4X dagger I see before me? */
4232 9 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
4233 9 : ParseNode *pn2 = expr();
4234 9 : if (!pn2)
4235 0 : return NULL;
4236 9 : pn->setOp(JSOP_DEFXMLNS);
4237 9 : pn->pn_pos.end = pn2->pn_pos.end;
4238 9 : pn->pn_kid = pn2;
4239 9 : break;
4240 : }
4241 : #endif
4242 :
4243 : case TOK_ERROR:
4244 0 : return NULL;
4245 :
4246 : default:
4247 557474 : return expressionStatement();
4248 : }
4249 :
4250 : /* Check termination of this primitive statement. */
4251 178801 : return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
4252 : }
4253 :
4254 : /*
4255 : * The 'blockObj' parameter is non-null when parsing the 'vars' in a let
4256 : * expression, block statement, non-top-level let declaration in statement
4257 : * context, and the let-initializer of a for-statement.
4258 : */
4259 : ParseNode *
4260 163907 : Parser::variables(ParseNodeKind kind, StaticBlockObject *blockObj, VarContext varContext)
4261 : {
4262 : /*
4263 : * The four options here are:
4264 : * - PNK_VAR: We're parsing var declarations.
4265 : * - PNK_CONST: We're parsing const declarations.
4266 : * - PNK_LET: We are parsing a let declaration.
4267 : * - PNK_LP: We are parsing the head of a let block.
4268 : */
4269 163907 : JS_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_LP);
4270 :
4271 163907 : ParseNode *pn = ListNode::create(kind, tc);
4272 163907 : if (!pn)
4273 0 : return NULL;
4274 :
4275 163907 : pn->setOp(blockObj ? JSOP_NOP : kind == PNK_VAR ? JSOP_DEFVAR : JSOP_DEFCONST);
4276 163907 : pn->makeEmpty();
4277 :
4278 : /*
4279 : * SpiderMonkey const is really "write once per initialization evaluation"
4280 : * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
4281 : * this code will change soon.
4282 : */
4283 163907 : BindData data;
4284 163907 : if (blockObj)
4285 13555 : data.initLet(varContext, *blockObj, JSMSG_TOO_MANY_LOCALS);
4286 : else
4287 150352 : data.initVarOrConst(pn->getOp());
4288 :
4289 : ParseNode *pn2;
4290 468013 : do {
4291 468247 : TokenKind tt = tokenStream.getToken();
4292 : #if JS_HAS_DESTRUCTURING
4293 468247 : if (tt == TOK_LB || tt == TOK_LC) {
4294 7272 : tc->flags |= TCF_DECL_DESTRUCTURING;
4295 7272 : pn2 = primaryExpr(tt, JS_FALSE);
4296 7272 : tc->flags &= ~TCF_DECL_DESTRUCTURING;
4297 7272 : if (!pn2)
4298 0 : return NULL;
4299 :
4300 7272 : if (!CheckDestructuring(context, &data, pn2, tc))
4301 162 : return NULL;
4302 : bool ignored;
4303 7110 : if ((tc->flags & TCF_IN_FOR_INIT) && matchInOrOf(&ignored)) {
4304 144 : tokenStream.ungetToken();
4305 144 : pn->append(pn2);
4306 144 : continue;
4307 : }
4308 :
4309 6966 : MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
4310 6966 : JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NOP);
4311 :
4312 6966 : ParseNode *init = assignExpr();
4313 6966 : if (!init)
4314 0 : return NULL;
4315 6966 : UndominateInitializers(pn2, init->pn_pos.end, tc);
4316 :
4317 6966 : pn2 = ParseNode::newBinaryOrAppend(PNK_ASSIGN, JSOP_NOP, pn2, init, tc);
4318 6966 : if (!pn2)
4319 0 : return NULL;
4320 6966 : pn->append(pn2);
4321 6966 : continue;
4322 : }
4323 : #endif /* JS_HAS_DESTRUCTURING */
4324 :
4325 460975 : if (tt != TOK_NAME) {
4326 9 : if (tt != TOK_ERROR)
4327 9 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME);
4328 9 : return NULL;
4329 : }
4330 :
4331 460966 : PropertyName *name = tokenStream.currentToken().name();
4332 460966 : pn2 = NewBindingNode(name, tc, blockObj, varContext);
4333 460966 : if (!pn2)
4334 0 : return NULL;
4335 460966 : if (data.op == JSOP_DEFCONST)
4336 56178 : pn2->pn_dflags |= PND_CONST;
4337 460966 : data.pn = pn2;
4338 460966 : if (!data.binder(context, &data, name, tc))
4339 54 : return NULL;
4340 460912 : pn->append(pn2);
4341 :
4342 460912 : if (tokenStream.matchToken(TOK_ASSIGN)) {
4343 150515 : JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NOP);
4344 :
4345 150515 : ParseNode *init = assignExpr();
4346 150515 : if (!init)
4347 9 : return NULL;
4348 :
4349 150506 : if (pn2->isUsed()) {
4350 2610 : pn2 = MakeAssignment(pn2, init, tc);
4351 2610 : if (!pn2)
4352 0 : return NULL;
4353 : } else {
4354 147896 : pn2->pn_expr = init;
4355 : }
4356 :
4357 150506 : JS_ASSERT_IF(pn2->pn_dflags & PND_GVAR, !(pn2->pn_dflags & PND_BOUND));
4358 :
4359 150506 : pn2->setOp(pn2->isOp(JSOP_ARGUMENTS)
4360 : ? JSOP_SETNAME
4361 : : (pn2->pn_dflags & PND_BOUND)
4362 : ? JSOP_SETLOCAL
4363 : : (data.op == JSOP_DEFCONST)
4364 : ? JSOP_SETCONST
4365 150506 : : JSOP_SETNAME);
4366 :
4367 150506 : NoteLValue(context, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
4368 :
4369 : /* The declarator's position must include the initializer. */
4370 150506 : pn2->pn_pos.end = init->pn_pos.end;
4371 :
4372 150506 : if (tc->inFunction() && name == context->runtime->atomState.argumentsAtom) {
4373 27 : tc->noteArgumentsNameUse(pn2);
4374 27 : if (!blockObj) {
4375 27 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
4376 27 : tc->noteLocalOverwritesArguments();
4377 : }
4378 : }
4379 : }
4380 468013 : } while (tokenStream.matchToken(TOK_COMMA));
4381 :
4382 163673 : pn->pn_pos.end = pn->last()->pn_pos.end;
4383 163673 : return pn;
4384 : }
4385 :
4386 : ParseNode *
4387 939085 : Parser::expr()
4388 : {
4389 939085 : ParseNode *pn = assignExpr();
4390 939085 : if (pn && tokenStream.matchToken(TOK_COMMA)) {
4391 1260 : ParseNode *pn2 = ListNode::create(PNK_COMMA, tc);
4392 1260 : if (!pn2)
4393 0 : return NULL;
4394 1260 : pn2->pn_pos.begin = pn->pn_pos.begin;
4395 1260 : pn2->initList(pn);
4396 1260 : pn = pn2;
4397 2034 : do {
4398 : #if JS_HAS_GENERATORS
4399 2034 : pn2 = pn->last();
4400 2034 : if (pn2->isKind(PNK_YIELD) && !pn2->isInParens()) {
4401 0 : reportErrorNumber(pn2, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
4402 0 : return NULL;
4403 : }
4404 : #endif
4405 2034 : pn2 = assignExpr();
4406 2034 : if (!pn2)
4407 0 : return NULL;
4408 2034 : pn->append(pn2);
4409 2034 : } while (tokenStream.matchToken(TOK_COMMA));
4410 1260 : pn->pn_pos.end = pn->last()->pn_pos.end;
4411 : }
4412 939085 : return pn;
4413 : }
4414 :
4415 : /*
4416 : * For a number of the expression parsers we define an always-inlined version
4417 : * and a never-inlined version (which just calls the always-inlined version).
4418 : * Using the always-inlined version in the hot call-sites givs a ~5% parsing
4419 : * speedup. These macros help avoid some boilerplate code.
4420 : */
4421 : #define BEGIN_EXPR_PARSER(name) \
4422 : JS_ALWAYS_INLINE ParseNode * \
4423 : Parser::name##i()
4424 :
4425 : #define END_EXPR_PARSER(name) \
4426 : JS_NEVER_INLINE ParseNode * \
4427 : Parser::name##n() { \
4428 : return name##i(); \
4429 : }
4430 :
4431 3604805 : BEGIN_EXPR_PARSER(mulExpr1)
4432 : {
4433 3604805 : ParseNode *pn = unaryExpr();
4434 :
4435 : /*
4436 : * Note: unlike addExpr1() et al, we use getToken() here instead of
4437 : * isCurrentTokenType() because unaryExpr() doesn't leave the TokenStream
4438 : * state one past the end of the unary expression.
4439 : */
4440 : TokenKind tt;
4441 7219034 : while (pn && ((tt = tokenStream.getToken()) == TOK_STAR || tt == TOK_DIV || tt == TOK_MOD)) {
4442 : ParseNodeKind kind = (tt == TOK_STAR)
4443 : ? PNK_STAR
4444 : : (tt == TOK_DIV)
4445 : ? PNK_DIV
4446 9424 : : PNK_MOD;
4447 9424 : JSOp op = tokenStream.currentToken().t_op;
4448 9424 : pn = ParseNode::newBinaryOrAppend(kind, op, pn, unaryExpr(), tc);
4449 : }
4450 3604805 : return pn;
4451 : }
4452 1533574 : END_EXPR_PARSER(mulExpr1)
4453 :
4454 2071231 : BEGIN_EXPR_PARSER(addExpr1)
4455 : {
4456 2071231 : ParseNode *pn = mulExpr1i();
4457 5676036 : while (pn && tokenStream.isCurrentTokenType(TOK_PLUS, TOK_MINUS)) {
4458 1533574 : TokenKind tt = tokenStream.currentToken().type;
4459 1533574 : JSOp op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
4460 1533574 : ParseNodeKind kind = (tt == TOK_PLUS) ? PNK_ADD : PNK_SUB;
4461 1533574 : pn = ParseNode::newBinaryOrAppend(kind, op, pn, mulExpr1n(), tc);
4462 : }
4463 2071231 : return pn;
4464 : }
4465 5644 : END_EXPR_PARSER(addExpr1)
4466 :
4467 : inline ParseNodeKind
4468 5644 : ShiftTokenToParseNodeKind(const Token &token)
4469 : {
4470 5644 : switch (token.type) {
4471 : case TOK_LSH:
4472 2043 : return PNK_LSH;
4473 : case TOK_RSH:
4474 2215 : return PNK_RSH;
4475 : default:
4476 1386 : JS_ASSERT(token.type == TOK_URSH);
4477 1386 : return PNK_URSH;
4478 : }
4479 : }
4480 :
4481 2065587 : BEGIN_EXPR_PARSER(shiftExpr1)
4482 : {
4483 2065587 : ParseNode *left = addExpr1i();
4484 4136818 : while (left && tokenStream.isCurrentTokenShift()) {
4485 5644 : ParseNodeKind kind = ShiftTokenToParseNodeKind(tokenStream.currentToken());
4486 5644 : JSOp op = tokenStream.currentToken().t_op;
4487 5644 : ParseNode *right = addExpr1n();
4488 5644 : if (!right)
4489 0 : return NULL;
4490 5644 : left = tc->parser->new_<BinaryNode>(kind, op, left, right);
4491 : }
4492 2065587 : return left;
4493 : }
4494 110677 : END_EXPR_PARSER(shiftExpr1)
4495 :
4496 : inline ParseNodeKind
4497 110677 : RelationalTokenToParseNodeKind(const Token &token)
4498 : {
4499 110677 : switch (token.type) {
4500 : case TOK_IN:
4501 75330 : return PNK_IN;
4502 : case TOK_INSTANCEOF:
4503 1901 : return PNK_INSTANCEOF;
4504 : case TOK_LT:
4505 26426 : return PNK_LT;
4506 : case TOK_LE:
4507 1683 : return PNK_LE;
4508 : case TOK_GT:
4509 3087 : return PNK_GT;
4510 : default:
4511 2250 : JS_ASSERT(token.type == TOK_GE);
4512 2250 : return PNK_GE;
4513 : }
4514 : }
4515 :
4516 1954910 : BEGIN_EXPR_PARSER(relExpr1)
4517 : {
4518 1954910 : unsigned inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
4519 :
4520 : /*
4521 : * Uses of the in operator in shiftExprs are always unambiguous,
4522 : * so unset the flag that prohibits recognizing it.
4523 : */
4524 1954910 : tc->flags &= ~TCF_IN_FOR_INIT;
4525 :
4526 1954910 : ParseNode *pn = shiftExpr1i();
4527 10044788 : while (pn &&
4528 2065348 : (tokenStream.isCurrentTokenRelational() ||
4529 : /*
4530 : * Recognize the 'in' token as an operator only if we're not
4531 : * currently in the init expr of a for loop.
4532 : */
4533 2002371 : (inForInitFlag == 0 && tokenStream.isCurrentTokenType(TOK_IN)) ||
4534 1956572 : tokenStream.isCurrentTokenType(TOK_INSTANCEOF))) {
4535 110677 : ParseNodeKind kind = RelationalTokenToParseNodeKind(tokenStream.currentToken());
4536 110677 : JSOp op = tokenStream.currentToken().t_op;
4537 110677 : pn = ParseNode::newBinaryOrAppend(kind, op, pn, shiftExpr1n(), tc);
4538 : }
4539 : /* Restore previous state of inForInit flag. */
4540 1954910 : tc->flags |= inForInitFlag;
4541 :
4542 1954910 : return pn;
4543 : }
4544 22444 : END_EXPR_PARSER(relExpr1)
4545 :
4546 : inline ParseNodeKind
4547 22444 : EqualityTokenToParseNodeKind(const Token &token)
4548 : {
4549 22444 : switch (token.type) {
4550 : case TOK_STRICTEQ:
4551 9533 : return PNK_STRICTEQ;
4552 : case TOK_EQ:
4553 4744 : return PNK_EQ;
4554 : case TOK_STRICTNE:
4555 4834 : return PNK_STRICTNE;
4556 : default:
4557 3333 : JS_ASSERT(token.type == TOK_NE);
4558 3333 : return PNK_NE;
4559 : }
4560 : }
4561 :
4562 1932466 : BEGIN_EXPR_PARSER(eqExpr1)
4563 : {
4564 1932466 : ParseNode *left = relExpr1i();
4565 3887376 : while (left && tokenStream.isCurrentTokenEquality()) {
4566 22444 : ParseNodeKind kind = EqualityTokenToParseNodeKind(tokenStream.currentToken());
4567 22444 : JSOp op = tokenStream.currentToken().t_op;
4568 22444 : ParseNode *right = relExpr1n();
4569 22444 : if (!right)
4570 0 : return NULL;
4571 22444 : left = tc->parser->new_<BinaryNode>(kind, op, left, right);
4572 : }
4573 1932466 : return left;
4574 : }
4575 2871 : END_EXPR_PARSER(eqExpr1)
4576 :
4577 1929595 : BEGIN_EXPR_PARSER(bitAndExpr1)
4578 : {
4579 1929595 : ParseNode *pn = eqExpr1i();
4580 3862061 : while (pn && tokenStream.isCurrentTokenType(TOK_BITAND))
4581 2871 : pn = ParseNode::newBinaryOrAppend(PNK_BITAND, JSOP_BITAND, pn, eqExpr1n(), tc);
4582 1929595 : return pn;
4583 : }
4584 1629 : END_EXPR_PARSER(bitAndExpr1)
4585 :
4586 1927966 : BEGIN_EXPR_PARSER(bitXorExpr1)
4587 : {
4588 1927966 : ParseNode *pn = bitAndExpr1i();
4589 3857561 : while (pn && tokenStream.isCurrentTokenType(TOK_BITXOR))
4590 1629 : pn = ParseNode::newBinaryOrAppend(PNK_BITXOR, JSOP_BITXOR, pn, bitAndExpr1n(), tc);
4591 1927966 : return pn;
4592 : }
4593 1629 : END_EXPR_PARSER(bitXorExpr1)
4594 :
4595 1926337 : BEGIN_EXPR_PARSER(bitOrExpr1)
4596 : {
4597 1926337 : ParseNode *pn = bitXorExpr1i();
4598 3854303 : while (pn && tokenStream.isCurrentTokenType(TOK_BITOR))
4599 1629 : pn = ParseNode::newBinaryOrAppend(PNK_BITOR, JSOP_BITOR, pn, bitXorExpr1n(), tc);
4600 1926337 : return pn;
4601 : }
4602 3870 : END_EXPR_PARSER(bitOrExpr1)
4603 :
4604 1922467 : BEGIN_EXPR_PARSER(andExpr1)
4605 : {
4606 1922467 : ParseNode *pn = bitOrExpr1i();
4607 3848804 : while (pn && tokenStream.isCurrentTokenType(TOK_AND))
4608 3870 : pn = ParseNode::newBinaryOrAppend(PNK_AND, JSOP_AND, pn, bitOrExpr1n(), tc);
4609 1922467 : return pn;
4610 : }
4611 2907 : END_EXPR_PARSER(andExpr1)
4612 :
4613 : JS_ALWAYS_INLINE ParseNode *
4614 1919560 : Parser::orExpr1()
4615 : {
4616 1919560 : ParseNode *pn = andExpr1i();
4617 3842027 : while (pn && tokenStream.isCurrentTokenType(TOK_OR))
4618 2907 : pn = ParseNode::newBinaryOrAppend(PNK_OR, JSOP_OR, pn, andExpr1n(), tc);
4619 1919560 : return pn;
4620 : }
4621 :
4622 : JS_ALWAYS_INLINE ParseNode *
4623 1919560 : Parser::condExpr1()
4624 : {
4625 1919560 : ParseNode *condition = orExpr1();
4626 1919560 : if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
4627 1912925 : return condition;
4628 :
4629 : /*
4630 : * Always accept the 'in' operator in the middle clause of a ternary,
4631 : * where it's unambiguous, even if we might be parsing the init of a
4632 : * for statement.
4633 : */
4634 6635 : unsigned oldflags = tc->flags;
4635 6635 : tc->flags &= ~TCF_IN_FOR_INIT;
4636 6635 : ParseNode *thenExpr = assignExpr();
4637 6635 : tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
4638 6635 : if (!thenExpr)
4639 0 : return NULL;
4640 :
4641 6635 : MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
4642 :
4643 6635 : ParseNode *elseExpr = assignExpr();
4644 6635 : if (!elseExpr)
4645 0 : return NULL;
4646 :
4647 6635 : tokenStream.getToken(); /* read one token past the end */
4648 6635 : return new_<ConditionalExpression>(condition, thenExpr, elseExpr);
4649 : }
4650 :
4651 : bool
4652 403430 : Parser::setAssignmentLhsOps(ParseNode *pn, JSOp op)
4653 : {
4654 403430 : switch (pn->getKind()) {
4655 : case PNK_NAME:
4656 372744 : if (!CheckStrictAssignment(context, tc, pn))
4657 0 : return false;
4658 372744 : pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
4659 372744 : NoteLValue(context, pn, tc);
4660 372744 : break;
4661 : case PNK_DOT:
4662 18224 : pn->setOp(JSOP_SETPROP);
4663 18224 : break;
4664 : case PNK_LB:
4665 12138 : pn->setOp(JSOP_SETELEM);
4666 12138 : break;
4667 : #if JS_HAS_DESTRUCTURING
4668 : case PNK_RB:
4669 : case PNK_RC:
4670 279 : if (op != JSOP_NOP) {
4671 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_DESTRUCT_ASS);
4672 0 : return false;
4673 : }
4674 279 : if (!CheckDestructuring(context, NULL, pn, tc))
4675 0 : return false;
4676 279 : break;
4677 : #endif
4678 : case PNK_LP:
4679 18 : if (!MakeSetCall(context, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
4680 0 : return false;
4681 18 : break;
4682 : #if JS_HAS_XML_SUPPORT
4683 : case PNK_XMLUNARY:
4684 18 : JS_ASSERT(pn->isOp(JSOP_XMLNAME));
4685 18 : pn->setOp(JSOP_SETXMLNAME);
4686 18 : break;
4687 : #endif
4688 : default:
4689 9 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
4690 9 : return false;
4691 : }
4692 403421 : return true;
4693 : }
4694 :
4695 : ParseNode *
4696 1920118 : Parser::assignExpr()
4697 : {
4698 1920118 : JS_CHECK_RECURSION(context, return NULL);
4699 :
4700 : #if JS_HAS_GENERATORS
4701 1920118 : if (tokenStream.matchToken(TOK_YIELD, TSF_OPERAND))
4702 558 : return returnOrYield(true);
4703 : #endif
4704 :
4705 1919560 : ParseNode *lhs = condExpr1();
4706 1919560 : if (!lhs)
4707 239 : return NULL;
4708 :
4709 : ParseNodeKind kind;
4710 1919321 : switch (tokenStream.currentToken().type) {
4711 373491 : case TOK_ASSIGN: kind = PNK_ASSIGN; break;
4712 27437 : case TOK_ADDASSIGN: kind = PNK_ADDASSIGN; break;
4713 315 : case TOK_SUBASSIGN: kind = PNK_SUBASSIGN; break;
4714 126 : case TOK_BITORASSIGN: kind = PNK_BITORASSIGN; break;
4715 54 : case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; break;
4716 36 : case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; break;
4717 18 : case TOK_LSHASSIGN: kind = PNK_LSHASSIGN; break;
4718 153 : case TOK_RSHASSIGN: kind = PNK_RSHASSIGN; break;
4719 0 : case TOK_URSHASSIGN: kind = PNK_URSHASSIGN; break;
4720 216 : case TOK_MULASSIGN: kind = PNK_MULASSIGN; break;
4721 63 : case TOK_DIVASSIGN: kind = PNK_DIVASSIGN; break;
4722 45 : case TOK_MODASSIGN: kind = PNK_MODASSIGN; break;
4723 : default:
4724 1517367 : JS_ASSERT(!tokenStream.isCurrentTokenAssignment());
4725 1517367 : tokenStream.ungetToken();
4726 1517367 : return lhs;
4727 : }
4728 :
4729 401954 : JSOp op = tokenStream.currentToken().t_op;
4730 401954 : if (!setAssignmentLhsOps(lhs, op))
4731 9 : return NULL;
4732 :
4733 401945 : ParseNode *rhs = assignExpr();
4734 401945 : if (!rhs)
4735 0 : return NULL;
4736 401945 : if (lhs->isKind(PNK_NAME) && lhs->isUsed()) {
4737 371322 : Definition *dn = lhs->pn_lexdef;
4738 :
4739 : /*
4740 : * If the definition is not flagged as assigned, we must have imputed
4741 : * the initialized flag to it, to optimize for flat closures. But that
4742 : * optimization uses source coordinates to check dominance relations,
4743 : * so we must extend the end of the definition to cover the right-hand
4744 : * side of this assignment, i.e., the initializer.
4745 : */
4746 371322 : if (!dn->isAssigned()) {
4747 1251 : JS_ASSERT(dn->isInitialized());
4748 1251 : dn->pn_pos.end = rhs->pn_pos.end;
4749 : }
4750 : }
4751 :
4752 401945 : return ParseNode::newBinaryOrAppend(kind, op, lhs, rhs, tc);
4753 : }
4754 :
4755 : static bool
4756 30131 : SetLvalKid(JSContext *cx, TokenStream *ts, TreeContext *tc, ParseNode *pn, ParseNode *kid,
4757 : const char *name)
4758 : {
4759 32471 : if (!kid->isKind(PNK_NAME) &&
4760 909 : !kid->isKind(PNK_DOT) &&
4761 486 : (!kid->isKind(PNK_LP) ||
4762 18 : (!kid->isOp(JSOP_CALL) && !kid->isOp(JSOP_EVAL) &&
4763 0 : !kid->isOp(JSOP_FUNCALL) && !kid->isOp(JSOP_FUNAPPLY))) &&
4764 : #if JS_HAS_XML_SUPPORT
4765 468 : !kid->isKind(PNK_XMLUNARY) &&
4766 : #endif
4767 459 : !kid->isKind(PNK_LB))
4768 : {
4769 0 : ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_OPERAND, name);
4770 0 : return false;
4771 : }
4772 30131 : if (!CheckStrictAssignment(cx, tc, kid))
4773 0 : return false;
4774 30131 : pn->pn_kid = kid;
4775 30131 : return true;
4776 : }
4777 :
4778 : static const char incop_name_str[][10] = {"increment", "decrement"};
4779 :
4780 : static JSBool
4781 30131 : SetIncOpKid(JSContext *cx, TokenStream *ts, TreeContext *tc, ParseNode *pn, ParseNode *kid,
4782 : TokenKind tt, bool preorder)
4783 : {
4784 : JSOp op;
4785 :
4786 30131 : if (!SetLvalKid(cx, ts, tc, pn, kid, incop_name_str[tt == TOK_DEC]))
4787 0 : return false;
4788 30131 : switch (kid->getKind()) {
4789 : case PNK_NAME:
4790 : op = (tt == TOK_INC)
4791 : ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
4792 29222 : : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
4793 29222 : NoteLValue(cx, kid, tc);
4794 29222 : break;
4795 :
4796 : case PNK_DOT:
4797 : op = (tt == TOK_INC)
4798 : ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
4799 423 : : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
4800 423 : break;
4801 :
4802 : case PNK_LP:
4803 18 : if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
4804 0 : return JS_FALSE;
4805 : /* FALL THROUGH */
4806 : #if JS_HAS_XML_SUPPORT
4807 : case PNK_XMLUNARY:
4808 27 : if (kid->isOp(JSOP_XMLNAME))
4809 9 : kid->setOp(JSOP_SETXMLNAME);
4810 : /* FALL THROUGH */
4811 : #endif
4812 : case PNK_LB:
4813 : op = (tt == TOK_INC)
4814 : ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
4815 486 : : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
4816 486 : break;
4817 :
4818 : default:
4819 0 : JS_ASSERT(0);
4820 0 : op = JSOP_NOP;
4821 : }
4822 30131 : pn->setOp(op);
4823 30131 : return JS_TRUE;
4824 : }
4825 :
4826 : ParseNode *
4827 109835 : Parser::unaryOpExpr(ParseNodeKind kind, JSOp op)
4828 : {
4829 109835 : TokenPtr begin = tokenStream.currentToken().pos.begin;
4830 109835 : ParseNode *kid = unaryExpr();
4831 109835 : if (!kid)
4832 4 : return NULL;
4833 109831 : return new_<UnaryNode>(kind, op, TokenPos::make(begin, kid->pn_pos.end), kid);
4834 : }
4835 :
4836 : ParseNode *
4837 3725324 : Parser::unaryExpr()
4838 : {
4839 : ParseNode *pn, *pn2;
4840 :
4841 3725324 : JS_CHECK_RECURSION(context, return NULL);
4842 :
4843 3725324 : switch (TokenKind tt = tokenStream.getToken(TSF_OPERAND)) {
4844 : case TOK_TYPEOF:
4845 2502 : return unaryOpExpr(PNK_TYPEOF, JSOP_TYPEOF);
4846 : case TOK_VOID:
4847 1206 : return unaryOpExpr(PNK_VOID, JSOP_VOID);
4848 : case TOK_NOT:
4849 76619 : return unaryOpExpr(PNK_NOT, JSOP_NOT);
4850 : case TOK_BITNOT:
4851 135 : return unaryOpExpr(PNK_BITNOT, JSOP_BITNOT);
4852 : case TOK_PLUS:
4853 333 : return unaryOpExpr(PNK_POS, JSOP_POS);
4854 : case TOK_MINUS:
4855 29040 : return unaryOpExpr(PNK_NEG, JSOP_NEG);
4856 :
4857 : case TOK_INC:
4858 : case TOK_DEC:
4859 7281 : pn = UnaryNode::create((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT, tc);
4860 7281 : if (!pn)
4861 0 : return NULL;
4862 7281 : pn2 = memberExpr(JS_TRUE);
4863 7281 : if (!pn2)
4864 0 : return NULL;
4865 7281 : if (!SetIncOpKid(context, &tokenStream, tc, pn, pn2, tt, true))
4866 0 : return NULL;
4867 7281 : pn->pn_pos.end = pn2->pn_pos.end;
4868 7281 : break;
4869 :
4870 : case TOK_DELETE:
4871 : {
4872 1260 : pn = UnaryNode::create(PNK_DELETE, tc);
4873 1260 : if (!pn)
4874 0 : return NULL;
4875 1260 : pn2 = unaryExpr();
4876 1260 : if (!pn2)
4877 0 : return NULL;
4878 1260 : pn->pn_pos.end = pn2->pn_pos.end;
4879 :
4880 : /*
4881 : * Under ECMA3, deleting any unary expression is valid -- it simply
4882 : * returns true. Here we fold constants before checking for a call
4883 : * expression, in order to rule out delete of a generator expression.
4884 : */
4885 1260 : if (foldConstants && !FoldConstants(context, pn2, tc))
4886 0 : return NULL;
4887 1260 : switch (pn2->getKind()) {
4888 : case PNK_LP:
4889 0 : if (!(pn2->pn_xflags & PNX_SETCALL)) {
4890 : /*
4891 : * Call MakeSetCall to check for errors, but clear PNX_SETCALL
4892 : * because the optimizer will eliminate the useless delete.
4893 : */
4894 0 : if (!MakeSetCall(context, pn2, tc, JSMSG_BAD_DELETE_OPERAND))
4895 0 : return NULL;
4896 0 : pn2->pn_xflags &= ~PNX_SETCALL;
4897 : }
4898 0 : break;
4899 : case PNK_NAME:
4900 486 : if (!ReportStrictModeError(context, &tokenStream, tc, pn,
4901 486 : JSMSG_DEPRECATED_DELETE_OPERAND)) {
4902 0 : return NULL;
4903 : }
4904 486 : pn2->setOp(JSOP_DELNAME);
4905 486 : if (pn2->pn_atom == context->runtime->atomState.argumentsAtom) {
4906 0 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
4907 0 : tc->countArgumentsUse(pn2);
4908 : }
4909 486 : break;
4910 : default:;
4911 : }
4912 1260 : pn->pn_kid = pn2;
4913 1260 : break;
4914 : }
4915 : case TOK_ERROR:
4916 0 : return NULL;
4917 :
4918 : default:
4919 3606948 : tokenStream.ungetToken();
4920 3606948 : pn = memberExpr(JS_TRUE);
4921 3606948 : if (!pn)
4922 239 : return NULL;
4923 :
4924 : /* Don't look across a newline boundary for a postfix incop. */
4925 3606709 : if (tokenStream.onCurrentLine(pn->pn_pos)) {
4926 3491329 : tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
4927 3491329 : if (tt == TOK_INC || tt == TOK_DEC) {
4928 22850 : tokenStream.consumeKnownToken(tt);
4929 22850 : pn2 = UnaryNode::create((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT, tc);
4930 22850 : if (!pn2)
4931 0 : return NULL;
4932 22850 : if (!SetIncOpKid(context, &tokenStream, tc, pn2, pn, tt, false))
4933 0 : return NULL;
4934 22850 : pn2->pn_pos.begin = pn->pn_pos.begin;
4935 22850 : pn = pn2;
4936 : }
4937 : }
4938 3606709 : break;
4939 : }
4940 3615250 : return pn;
4941 : }
4942 :
4943 : #if JS_HAS_GENERATORS
4944 :
4945 : /*
4946 : * A dedicated helper for transplanting the comprehension expression E in
4947 : *
4948 : * [E for (V in I)] // array comprehension
4949 : * (E for (V in I)) // generator expression
4950 : *
4951 : * from its initial location in the AST, on the left of the 'for', to its final
4952 : * position on the right. To avoid a separate pass we do this by adjusting the
4953 : * blockids and name binding links that were established when E was parsed.
4954 : *
4955 : * A generator expression desugars like so:
4956 : *
4957 : * (E for (V in I)) => (function () { for (var V in I) yield E; })()
4958 : *
4959 : * so the transplanter must adjust static level as well as blockid. E's source
4960 : * coordinates in root->pn_pos are critical to deciding which binding links to
4961 : * preserve and which to cut.
4962 : *
4963 : * NB: This is not a general tree transplanter -- it knows in particular that
4964 : * the one or more bindings induced by V have not yet been created.
4965 : */
4966 : class CompExprTransplanter {
4967 : ParseNode *root;
4968 : TreeContext *tc;
4969 : bool genexp;
4970 : unsigned adjust;
4971 : unsigned funcLevel;
4972 :
4973 : public:
4974 657 : CompExprTransplanter(ParseNode *pn, TreeContext *tc, bool ge, unsigned adj)
4975 657 : : root(pn), tc(tc), genexp(ge), adjust(adj), funcLevel(0)
4976 : {
4977 657 : }
4978 :
4979 : bool transplant(ParseNode *pn);
4980 : };
4981 :
4982 : /*
4983 : * A helper for lazily checking for the presence of illegal |yield| or |arguments|
4984 : * tokens inside of generator expressions. This must be done lazily since we don't
4985 : * know whether we're in a generator expression until we see the "for" token after
4986 : * we've already parsed the body expression.
4987 : *
4988 : * Use in any context which may turn out to be inside a generator expression. This
4989 : * includes parenthesized expressions and argument lists, and it includes the tail
4990 : * of generator expressions.
4991 : *
4992 : * The guard will keep track of any |yield| or |arguments| tokens that occur while
4993 : * parsing the body. As soon as the parser reaches the end of the body expression,
4994 : * call endBody() to reset the context's state, and then immediately call:
4995 : *
4996 : * - checkValidBody() if this *did* turn out to be a generator expression
4997 : * - maybeNoteGenerator() if this *did not* turn out to be a generator expression
4998 : */
4999 : class GenexpGuard {
5000 : TreeContext *tc;
5001 : uint32_t startYieldCount;
5002 : uint32_t startArgumentsCount;
5003 :
5004 : public:
5005 421178 : explicit GenexpGuard(TreeContext *tc)
5006 421178 : : tc(tc)
5007 : {
5008 421178 : if (tc->parenDepth == 0) {
5009 277167 : tc->yieldCount = tc->argumentsCount = 0;
5010 277167 : tc->yieldNode = tc->argumentsNode = NULL;
5011 : }
5012 421178 : startYieldCount = tc->yieldCount;
5013 421178 : startArgumentsCount = tc->argumentsCount;
5014 421178 : tc->parenDepth++;
5015 421178 : }
5016 :
5017 : void endBody();
5018 : bool checkValidBody(ParseNode *pn);
5019 : bool maybeNoteGenerator(ParseNode *pn);
5020 : };
5021 :
5022 : void
5023 421015 : GenexpGuard::endBody()
5024 : {
5025 421015 : tc->parenDepth--;
5026 421015 : }
5027 :
5028 : /*
5029 : * Check whether a |yield| or |arguments| token has been encountered in the
5030 : * body expression, and if so, report an error.
5031 : *
5032 : * Call this after endBody() when determining that the body *was* in a
5033 : * generator expression.
5034 : */
5035 : bool
5036 423 : GenexpGuard::checkValidBody(ParseNode *pn)
5037 : {
5038 423 : if (tc->yieldCount > startYieldCount) {
5039 0 : ParseNode *errorNode = tc->yieldNode;
5040 0 : if (!errorNode)
5041 0 : errorNode = pn;
5042 0 : tc->parser->reportErrorNumber(errorNode, JSREPORT_ERROR, JSMSG_BAD_GENEXP_BODY, js_yield_str);
5043 0 : return false;
5044 : }
5045 :
5046 423 : if (tc->argumentsCount > startArgumentsCount) {
5047 9 : ParseNode *errorNode = tc->argumentsNode;
5048 9 : if (!errorNode)
5049 0 : errorNode = pn;
5050 9 : tc->parser->reportErrorNumber(errorNode, JSREPORT_ERROR, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
5051 9 : return false;
5052 : }
5053 :
5054 414 : return true;
5055 : }
5056 :
5057 : /*
5058 : * Check whether a |yield| token has been encountered in the body expression,
5059 : * and if so, note that the current function is a generator function.
5060 : *
5061 : * Call this after endBody() when determining that the body *was not* in a
5062 : * generator expression.
5063 : */
5064 : bool
5065 420592 : GenexpGuard::maybeNoteGenerator(ParseNode *pn)
5066 : {
5067 420592 : if (tc->yieldCount > 0) {
5068 0 : tc->flags |= TCF_FUN_IS_GENERATOR;
5069 0 : if (!tc->inFunction()) {
5070 : tc->parser->reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD,
5071 0 : js_yield_str);
5072 0 : return false;
5073 : }
5074 0 : if (tc->flags & TCF_RETURN_EXPR) {
5075 : /* At the time we saw the yield, we might not have set TCF_FUN_IS_GENERATOR yet. */
5076 : ReportBadReturn(tc->parser->context, tc, pn, JSREPORT_ERROR,
5077 : JSMSG_BAD_GENERATOR_RETURN,
5078 0 : JSMSG_BAD_ANON_GENERATOR_RETURN);
5079 0 : return false;
5080 : }
5081 : }
5082 420592 : return true;
5083 : }
5084 :
5085 : /*
5086 : * Any definitions nested within the comprehension expression of a generator
5087 : * expression must move "down" one static level, which of course increases the
5088 : * upvar-frame-skip count.
5089 : */
5090 : static bool
5091 198 : BumpStaticLevel(ParseNode *pn, TreeContext *tc)
5092 : {
5093 198 : if (!pn->pn_cookie.isFree()) {
5094 0 : unsigned level = pn->pn_cookie.level() + 1;
5095 :
5096 0 : JS_ASSERT(level >= tc->staticLevel);
5097 0 : if (level >= UpvarCookie::FREE_LEVEL) {
5098 : JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
5099 0 : JSMSG_TOO_DEEP, js_function_str);
5100 0 : return false;
5101 : }
5102 :
5103 0 : pn->pn_cookie.set(level, pn->pn_cookie.slot());
5104 : }
5105 198 : return true;
5106 : }
5107 :
5108 : static void
5109 1251 : AdjustBlockId(ParseNode *pn, unsigned adjust, TreeContext *tc)
5110 : {
5111 1251 : JS_ASSERT(pn->isArity(PN_LIST) || pn->isArity(PN_FUNC) || pn->isArity(PN_NAME));
5112 1251 : pn->pn_blockid += adjust;
5113 1251 : if (pn->pn_blockid >= tc->blockidGen)
5114 18 : tc->blockidGen = pn->pn_blockid + 1;
5115 1251 : }
5116 :
5117 : bool
5118 2061 : CompExprTransplanter::transplant(ParseNode *pn)
5119 : {
5120 2061 : if (!pn)
5121 522 : return true;
5122 :
5123 1539 : switch (pn->getArity()) {
5124 : case PN_LIST:
5125 756 : for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
5126 504 : if (!transplant(pn2))
5127 0 : return false;
5128 : }
5129 252 : if (pn->pn_pos >= root->pn_pos)
5130 252 : AdjustBlockId(pn, adjust, tc);
5131 252 : break;
5132 :
5133 : case PN_TERNARY:
5134 0 : if (!transplant(pn->pn_kid1) ||
5135 0 : !transplant(pn->pn_kid2) ||
5136 0 : !transplant(pn->pn_kid3))
5137 0 : return false;
5138 0 : break;
5139 :
5140 : case PN_BINARY:
5141 72 : if (!transplant(pn->pn_left))
5142 0 : return false;
5143 :
5144 : /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
5145 72 : if (pn->pn_right != pn->pn_left) {
5146 72 : if (!transplant(pn->pn_right))
5147 0 : return false;
5148 : }
5149 72 : break;
5150 :
5151 : case PN_UNARY:
5152 216 : if (!transplant(pn->pn_kid))
5153 0 : return false;
5154 216 : break;
5155 :
5156 : case PN_FUNC:
5157 : {
5158 : /*
5159 : * Only the first level of transplant recursion through functions needs
5160 : * to reparent the funbox, since all descendant functions are correctly
5161 : * linked under the top-most funbox. But every visit to this case needs
5162 : * to update funbox->level.
5163 : *
5164 : * Recall that funbox->level is the static level of the code containing
5165 : * the definition or expression of the function and not the static level
5166 : * of the function's body.
5167 : */
5168 9 : FunctionBox *funbox = pn->pn_funbox;
5169 :
5170 9 : funbox->level = tc->staticLevel + funcLevel;
5171 9 : if (++funcLevel == 1 && genexp) {
5172 0 : FunctionBox *parent = tc->funbox;
5173 :
5174 0 : FunctionBox **funboxp = &tc->parent->functionList;
5175 0 : while (*funboxp != funbox)
5176 0 : funboxp = &(*funboxp)->siblings;
5177 0 : *funboxp = funbox->siblings;
5178 :
5179 0 : funbox->parent = parent;
5180 0 : funbox->siblings = parent->kids;
5181 0 : parent->kids = funbox;
5182 0 : funbox->level = tc->staticLevel;
5183 : }
5184 : /* FALL THROUGH */
5185 : }
5186 :
5187 : case PN_NAME:
5188 531 : if (!transplant(pn->maybeExpr()))
5189 0 : return false;
5190 531 : if (pn->isArity(PN_FUNC))
5191 9 : --funcLevel;
5192 :
5193 531 : if (pn->isDefn()) {
5194 0 : if (genexp && !BumpStaticLevel(pn, tc))
5195 0 : return false;
5196 531 : } else if (pn->isUsed()) {
5197 522 : JS_ASSERT(!pn->isOp(JSOP_NOP));
5198 522 : JS_ASSERT(pn->pn_cookie.isFree());
5199 :
5200 522 : Definition *dn = pn->pn_lexdef;
5201 522 : JS_ASSERT(dn->isDefn());
5202 :
5203 : /*
5204 : * Adjust the definition's block id only if it is a placeholder not
5205 : * to the left of the root node, and if pn is the last use visited
5206 : * in the comprehension expression (to avoid adjusting the blockid
5207 : * multiple times).
5208 : *
5209 : * Non-placeholder definitions within the comprehension expression
5210 : * will be visited further below.
5211 : */
5212 522 : if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
5213 468 : if (genexp && !BumpStaticLevel(dn, tc))
5214 0 : return false;
5215 468 : AdjustBlockId(dn, adjust, tc);
5216 : }
5217 :
5218 522 : JSAtom *atom = pn->pn_atom;
5219 : #ifdef DEBUG
5220 522 : StmtInfo *stmt = LexicalLookup(tc, atom, NULL);
5221 522 : JS_ASSERT(!stmt || stmt != tc->topStmt);
5222 : #endif
5223 522 : if (genexp && !dn->isOp(JSOP_CALLEE)) {
5224 216 : JS_ASSERT(!tc->decls.lookupFirst(atom));
5225 :
5226 216 : if (dn->pn_pos < root->pn_pos) {
5227 : /*
5228 : * The variable originally appeared to be a use of a
5229 : * definition or placeholder outside the generator, but now
5230 : * we know it is scoped within the comprehension tail's
5231 : * clauses. Make it (along with any other uses within the
5232 : * generator) a use of a new placeholder in the generator's
5233 : * lexdeps.
5234 : */
5235 0 : Definition *dn2 = MakePlaceholder(pn, tc);
5236 0 : if (!dn2)
5237 0 : return false;
5238 0 : dn2->pn_pos = root->pn_pos;
5239 :
5240 : /*
5241 : * Change all uses of |dn| that lie within the generator's
5242 : * |yield| expression into uses of dn2.
5243 : */
5244 0 : ParseNode **pnup = &dn->dn_uses;
5245 : ParseNode *pnu;
5246 0 : while ((pnu = *pnup) != NULL && pnu->pn_pos >= root->pn_pos) {
5247 0 : pnu->pn_lexdef = dn2;
5248 0 : dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
5249 0 : pnup = &pnu->pn_link;
5250 : }
5251 0 : dn2->dn_uses = dn->dn_uses;
5252 0 : dn->dn_uses = *pnup;
5253 0 : *pnup = NULL;
5254 0 : if (!tc->lexdeps->put(atom, dn2))
5255 0 : return false;
5256 216 : } else if (dn->isPlaceholder()) {
5257 : /*
5258 : * The variable first occurs free in the 'yield' expression;
5259 : * move the existing placeholder node (and all its uses)
5260 : * from the parent's lexdeps into the generator's lexdeps.
5261 : */
5262 216 : tc->parent->lexdeps->remove(atom);
5263 216 : if (!tc->lexdeps->put(atom, dn))
5264 0 : return false;
5265 : }
5266 : }
5267 : }
5268 :
5269 531 : if (pn->pn_pos >= root->pn_pos)
5270 531 : AdjustBlockId(pn, adjust, tc);
5271 531 : break;
5272 :
5273 : case PN_NAMESET:
5274 9 : if (!transplant(pn->pn_tree))
5275 0 : return false;
5276 9 : break;
5277 :
5278 : case PN_NULLARY:
5279 : /* Nothing. */
5280 459 : break;
5281 : }
5282 1539 : return true;
5283 : }
5284 :
5285 : /*
5286 : * Starting from a |for| keyword after the first array initialiser element or
5287 : * an expression in an open parenthesis, parse the tail of the comprehension
5288 : * or generator expression signified by this |for| keyword in context.
5289 : *
5290 : * Return null on failure, else return the top-most parse node for the array
5291 : * comprehension or generator expression, with a unary node as the body of the
5292 : * (possibly nested) for-loop, initialized by |kind, op, kid|.
5293 : */
5294 : ParseNode *
5295 657 : Parser::comprehensionTail(ParseNode *kid, unsigned blockid, bool isGenexp,
5296 : ParseNodeKind kind, JSOp op)
5297 : {
5298 : unsigned adjust;
5299 : ParseNode *pn, *pn2, *pn3, **pnp;
5300 : StmtInfo stmtInfo;
5301 657 : BindData data;
5302 : TokenKind tt;
5303 :
5304 657 : JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
5305 :
5306 657 : if (kind == PNK_SEMI) {
5307 : /*
5308 : * Generator expression desugars to an immediately applied lambda that
5309 : * yields the next value from a for-in loop (possibly nested, and with
5310 : * optional if guard). Make pn be the TOK_LC body node.
5311 : */
5312 207 : pn = PushLexicalScope(context, tc, &stmtInfo);
5313 207 : if (!pn)
5314 0 : return NULL;
5315 207 : adjust = pn->pn_blockid - blockid;
5316 : } else {
5317 450 : JS_ASSERT(kind == PNK_ARRAYPUSH);
5318 :
5319 : /*
5320 : * Make a parse-node and literal object representing the block scope of
5321 : * this array comprehension. Our caller in primaryExpr, the TOK_LB case
5322 : * aka the array initialiser case, has passed the blockid to claim for
5323 : * the comprehension's block scope. We allocate that id or one above it
5324 : * here, by calling PushLexicalScope.
5325 : *
5326 : * In the case of a comprehension expression that has nested blocks
5327 : * (e.g., let expressions), we will allocate a higher blockid but then
5328 : * slide all blocks "to the right" to make room for the comprehension's
5329 : * block scope.
5330 : */
5331 450 : adjust = tc->blockid();
5332 450 : pn = PushLexicalScope(context, tc, &stmtInfo);
5333 450 : if (!pn)
5334 0 : return NULL;
5335 :
5336 450 : JS_ASSERT(blockid <= pn->pn_blockid);
5337 450 : JS_ASSERT(blockid < tc->blockidGen);
5338 450 : JS_ASSERT(tc->bodyid < blockid);
5339 450 : pn->pn_blockid = stmtInfo.blockid = blockid;
5340 450 : JS_ASSERT(adjust < blockid);
5341 450 : adjust = blockid - adjust;
5342 : }
5343 :
5344 657 : pnp = &pn->pn_expr;
5345 :
5346 657 : CompExprTransplanter transplanter(kid, tc, kind == PNK_SEMI, adjust);
5347 657 : transplanter.transplant(kid);
5348 :
5349 657 : JS_ASSERT(tc->blockChain && tc->blockChain == pn->pn_objbox->object);
5350 657 : data.initLet(HoistVars, *tc->blockChain, JSMSG_ARRAY_INIT_TOO_BIG);
5351 :
5352 549 : do {
5353 : /*
5354 : * FOR node is binary, left is loop control and right is body. Use
5355 : * index to count each block-local let-variable on the left-hand side
5356 : * of the in/of.
5357 : */
5358 684 : pn2 = BinaryNode::create(PNK_FOR, tc);
5359 684 : if (!pn2)
5360 0 : return NULL;
5361 :
5362 684 : pn2->setOp(JSOP_ITER);
5363 684 : pn2->pn_iflags = JSITER_ENUMERATE;
5364 684 : if (tokenStream.matchToken(TOK_NAME)) {
5365 81 : if (tokenStream.currentToken().name() == context->runtime->atomState.eachAtom)
5366 81 : pn2->pn_iflags |= JSITER_FOREACH;
5367 : else
5368 0 : tokenStream.ungetToken();
5369 : }
5370 684 : MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
5371 :
5372 684 : GenexpGuard guard(tc);
5373 :
5374 684 : PropertyName *name = NULL;
5375 684 : tt = tokenStream.getToken();
5376 684 : switch (tt) {
5377 : #if JS_HAS_DESTRUCTURING
5378 : case TOK_LB:
5379 : case TOK_LC:
5380 9 : tc->flags |= TCF_DECL_DESTRUCTURING;
5381 9 : pn3 = primaryExpr(tt, JS_FALSE);
5382 9 : tc->flags &= ~TCF_DECL_DESTRUCTURING;
5383 9 : if (!pn3)
5384 0 : return NULL;
5385 9 : break;
5386 : #endif
5387 :
5388 : case TOK_NAME:
5389 621 : name = tokenStream.currentToken().name();
5390 :
5391 : /*
5392 : * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
5393 : * JSOP_GETLOCAL here, because we don't yet know the block's depth
5394 : * in the operand stack frame. The code generator computes that,
5395 : * and it tries to bind all names to slots, so we must let it do
5396 : * the deed.
5397 : */
5398 621 : pn3 = NewBindingNode(name, tc);
5399 621 : if (!pn3)
5400 0 : return NULL;
5401 621 : break;
5402 :
5403 : default:
5404 54 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME);
5405 :
5406 : case TOK_ERROR:
5407 54 : return NULL;
5408 : }
5409 :
5410 : bool forOf;
5411 630 : if (!matchInOrOf(&forOf)) {
5412 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_IN_AFTER_FOR_NAME);
5413 0 : return NULL;
5414 : }
5415 630 : if (forOf) {
5416 153 : if (pn2->pn_iflags != JSITER_ENUMERATE) {
5417 27 : JS_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE));
5418 27 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP);
5419 27 : return NULL;
5420 : }
5421 126 : pn2->pn_iflags = JSITER_FOR_OF;
5422 : }
5423 :
5424 603 : ParseNode *pn4 = expr();
5425 603 : if (!pn4)
5426 0 : return NULL;
5427 603 : MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
5428 :
5429 549 : guard.endBody();
5430 :
5431 549 : if (isGenexp) {
5432 207 : if (!guard.checkValidBody(pn2))
5433 0 : return NULL;
5434 : } else {
5435 342 : if (!guard.maybeNoteGenerator(pn2))
5436 0 : return NULL;
5437 : }
5438 :
5439 549 : switch (tt) {
5440 : #if JS_HAS_DESTRUCTURING
5441 : case TOK_LB:
5442 : case TOK_LC:
5443 9 : if (!CheckDestructuring(context, &data, pn3, tc))
5444 0 : return NULL;
5445 :
5446 9 : if (versionNumber() == JSVERSION_1_7) {
5447 : /* Destructuring requires [key, value] enumeration in JS1.7. */
5448 0 : if (!pn3->isKind(PNK_RB) || pn3->pn_count != 2) {
5449 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE);
5450 0 : return NULL;
5451 : }
5452 :
5453 0 : JS_ASSERT(pn2->isOp(JSOP_ITER));
5454 0 : JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
5455 0 : if (!(pn2->pn_iflags & JSITER_FOREACH))
5456 0 : pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
5457 : }
5458 9 : break;
5459 : #endif
5460 :
5461 : case TOK_NAME:
5462 540 : data.pn = pn3;
5463 540 : if (!data.binder(context, &data, name, tc))
5464 0 : return NULL;
5465 540 : break;
5466 :
5467 : default:;
5468 : }
5469 :
5470 : /*
5471 : * Synthesize a declaration. Every definition must appear in the parse
5472 : * tree in order for ComprehensionTranslator to work.
5473 : */
5474 549 : ParseNode *vars = ListNode::create(PNK_VAR, tc);
5475 549 : if (!vars)
5476 0 : return NULL;
5477 549 : vars->setOp(JSOP_NOP);
5478 549 : vars->pn_pos = pn3->pn_pos;
5479 549 : vars->makeEmpty();
5480 549 : vars->append(pn3);
5481 549 : vars->pn_xflags |= PNX_FORINVAR;
5482 :
5483 : /* Definitions can't be passed directly to EmitAssignment as lhs. */
5484 549 : pn3 = CloneLeftHandSide(pn3, tc);
5485 549 : if (!pn3)
5486 0 : return NULL;
5487 :
5488 549 : pn2->pn_left = new_<TernaryNode>(PNK_FORIN, JSOP_NOP, vars, pn3, pn4);
5489 549 : if (!pn2->pn_left)
5490 0 : return NULL;
5491 549 : *pnp = pn2;
5492 549 : pnp = &pn2->pn_right;
5493 549 : } while (tokenStream.matchToken(TOK_FOR));
5494 :
5495 522 : if (tokenStream.matchToken(TOK_IF)) {
5496 9 : pn2 = TernaryNode::create(PNK_IF, tc);
5497 9 : if (!pn2)
5498 0 : return NULL;
5499 9 : pn2->pn_kid1 = condition();
5500 9 : if (!pn2->pn_kid1)
5501 0 : return NULL;
5502 9 : *pnp = pn2;
5503 9 : pnp = &pn2->pn_kid2;
5504 : }
5505 :
5506 522 : pn2 = UnaryNode::create(kind, tc);
5507 522 : if (!pn2)
5508 0 : return NULL;
5509 522 : pn2->setOp(op);
5510 522 : pn2->pn_kid = kid;
5511 522 : *pnp = pn2;
5512 :
5513 522 : PopStatement(tc);
5514 522 : return pn;
5515 : }
5516 :
5517 : #if JS_HAS_GENERATOR_EXPRS
5518 :
5519 : /*
5520 : * Starting from a |for| keyword after an expression, parse the comprehension
5521 : * tail completing this generator expression. Wrap the expression at kid in a
5522 : * generator function that is immediately called to evaluate to the generator
5523 : * iterator that is the value of this generator expression.
5524 : *
5525 : * |kid| must be the expression before the |for| keyword; we return an
5526 : * application of a generator function that includes the |for| loops and
5527 : * |if| guards, with |kid| as the operand of a |yield| expression as the
5528 : * innermost loop body.
5529 : *
5530 : * Note how unlike Python, we do not evaluate the expression to the right of
5531 : * the first |in| in the chain of |for| heads. Instead, a generator expression
5532 : * is merely sugar for a generator function expression and its application.
5533 : */
5534 : ParseNode *
5535 207 : Parser::generatorExpr(ParseNode *kid)
5536 : {
5537 207 : JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
5538 :
5539 : /* Create a |yield| node for |kid|. */
5540 207 : ParseNode *pn = UnaryNode::create(PNK_YIELD, tc);
5541 207 : if (!pn)
5542 0 : return NULL;
5543 207 : pn->setOp(JSOP_YIELD);
5544 207 : pn->setInParens(true);
5545 207 : pn->pn_pos = kid->pn_pos;
5546 207 : pn->pn_kid = kid;
5547 207 : pn->pn_hidden = true;
5548 :
5549 : /* Make a new node for the desugared generator function. */
5550 207 : ParseNode *genfn = FunctionNode::create(PNK_FUNCTION, tc);
5551 207 : if (!genfn)
5552 0 : return NULL;
5553 207 : genfn->setOp(JSOP_LAMBDA);
5554 207 : JS_ASSERT(!genfn->pn_body);
5555 207 : genfn->pn_dflags = 0;
5556 :
5557 : {
5558 207 : TreeContext *outertc = tc;
5559 414 : TreeContext gentc(tc->parser);
5560 207 : if (!gentc.init(context))
5561 0 : return NULL;
5562 :
5563 207 : FunctionBox *funbox = EnterFunction(genfn, &gentc);
5564 207 : if (!funbox)
5565 0 : return NULL;
5566 :
5567 : /*
5568 : * We assume conservatively that any deoptimization flag in tc->flags
5569 : * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
5570 : * propagate these flags into genfn. For code simplicity we also do
5571 : * not detect if the flags were only set in the kid and could be
5572 : * removed from tc->flags.
5573 : */
5574 : gentc.flags |= TCF_FUN_IS_GENERATOR | TCF_GENEXP_LAMBDA |
5575 207 : (outertc->flags & (TCF_FUN_FLAGS & ~TCF_FUN_PARAM_ARGUMENTS));
5576 207 : funbox->tcflags |= gentc.flags;
5577 207 : genfn->pn_funbox = funbox;
5578 207 : genfn->pn_blockid = gentc.bodyid;
5579 :
5580 207 : ParseNode *body = comprehensionTail(pn, outertc->blockid(), true);
5581 207 : if (!body)
5582 0 : return NULL;
5583 207 : JS_ASSERT(!genfn->pn_body);
5584 207 : genfn->pn_body = body;
5585 207 : genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
5586 207 : genfn->pn_pos.end = body->pn_pos.end = tokenStream.currentToken().pos.end;
5587 :
5588 207 : if (!LeaveFunction(genfn, &gentc))
5589 0 : return NULL;
5590 : }
5591 :
5592 : /*
5593 : * Our result is a call expression that invokes the anonymous generator
5594 : * function object.
5595 : */
5596 207 : ParseNode *result = ListNode::create(PNK_LP, tc);
5597 207 : if (!result)
5598 0 : return NULL;
5599 207 : result->setOp(JSOP_CALL);
5600 207 : result->pn_pos.begin = genfn->pn_pos.begin;
5601 207 : result->initList(genfn);
5602 207 : return result;
5603 : }
5604 :
5605 : static const char js_generator_str[] = "generator";
5606 :
5607 : #endif /* JS_HAS_GENERATOR_EXPRS */
5608 : #endif /* JS_HAS_GENERATORS */
5609 :
5610 : JSBool
5611 223566 : Parser::argumentList(ParseNode *listNode)
5612 : {
5613 223566 : if (tokenStream.matchToken(TOK_RP, TSF_OPERAND))
5614 34165 : return JS_TRUE;
5615 :
5616 189401 : GenexpGuard guard(tc);
5617 189401 : bool arg0 = true;
5618 :
5619 318922 : do {
5620 318932 : ParseNode *argNode = assignExpr();
5621 318932 : if (!argNode)
5622 10 : return JS_FALSE;
5623 318922 : if (arg0)
5624 189391 : guard.endBody();
5625 :
5626 : #if JS_HAS_GENERATORS
5627 318922 : if (argNode->isKind(PNK_YIELD) &&
5628 0 : !argNode->isInParens() &&
5629 0 : tokenStream.peekToken() == TOK_COMMA) {
5630 0 : reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
5631 0 : return JS_FALSE;
5632 : }
5633 : #endif
5634 : #if JS_HAS_GENERATOR_EXPRS
5635 318922 : if (tokenStream.matchToken(TOK_FOR)) {
5636 27 : if (!guard.checkValidBody(argNode))
5637 0 : return JS_FALSE;
5638 27 : argNode = generatorExpr(argNode);
5639 27 : if (!argNode)
5640 0 : return JS_FALSE;
5641 54 : if (listNode->pn_count > 1 ||
5642 27 : tokenStream.peekToken() == TOK_COMMA) {
5643 : reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
5644 0 : js_generator_str);
5645 0 : return JS_FALSE;
5646 : }
5647 : } else
5648 : #endif
5649 318895 : if (arg0 && !guard.maybeNoteGenerator(argNode))
5650 0 : return JS_FALSE;
5651 :
5652 318922 : arg0 = false;
5653 :
5654 318922 : listNode->append(argNode);
5655 318922 : } while (tokenStream.matchToken(TOK_COMMA));
5656 :
5657 189391 : if (tokenStream.getToken() != TOK_RP) {
5658 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_AFTER_ARGS);
5659 0 : return JS_FALSE;
5660 : }
5661 189391 : return JS_TRUE;
5662 : }
5663 :
5664 : ParseNode *
5665 3648825 : Parser::memberExpr(JSBool allowCallSyntax)
5666 : {
5667 : ParseNode *lhs;
5668 :
5669 3648825 : JS_CHECK_RECURSION(context, return NULL);
5670 :
5671 : /* Check for new expression first. */
5672 3648825 : TokenKind tt = tokenStream.getToken(TSF_OPERAND);
5673 3648825 : if (tt == TOK_NEW) {
5674 34596 : lhs = ListNode::create(PNK_NEW, tc);
5675 34596 : if (!lhs)
5676 0 : return NULL;
5677 34596 : ParseNode *ctorExpr = memberExpr(JS_FALSE);
5678 34596 : if (!ctorExpr)
5679 0 : return NULL;
5680 34596 : lhs->setOp(JSOP_NEW);
5681 34596 : lhs->initList(ctorExpr);
5682 34596 : lhs->pn_pos.begin = ctorExpr->pn_pos.begin;
5683 :
5684 34596 : if (tokenStream.matchToken(TOK_LP) && !argumentList(lhs))
5685 0 : return NULL;
5686 34596 : if (lhs->pn_count > ARGC_LIMIT) {
5687 : JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
5688 0 : JSMSG_TOO_MANY_CON_ARGS);
5689 0 : return NULL;
5690 : }
5691 34596 : lhs->pn_pos.end = lhs->last()->pn_pos.end;
5692 : } else {
5693 3614229 : lhs = primaryExpr(tt, JS_FALSE);
5694 3614229 : if (!lhs)
5695 229 : return NULL;
5696 :
5697 3614000 : if (lhs->isXMLNameOp()) {
5698 90 : lhs = new_<UnaryNode>(PNK_XMLUNARY, JSOP_XMLNAME, lhs->pn_pos, lhs);
5699 90 : if (!lhs)
5700 0 : return NULL;
5701 : }
5702 : }
5703 :
5704 7680860 : while ((tt = tokenStream.getToken()) > TOK_EOF) {
5705 : ParseNode *nextMember;
5706 3980784 : if (tt == TOK_DOT) {
5707 143210 : tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
5708 143210 : if (tt == TOK_ERROR)
5709 0 : return NULL;
5710 143210 : if (tt == TOK_NAME) {
5711 : #if JS_HAS_XML_SUPPORT
5712 143138 : if (!tc->inStrictMode() && tokenStream.peekToken() == TOK_DBLCOLON) {
5713 9 : ParseNode *propertyId = propertyQualifiedIdentifier();
5714 9 : if (!propertyId)
5715 0 : return NULL;
5716 :
5717 : nextMember = new_<XMLDoubleColonProperty>(lhs, propertyId,
5718 : lhs->pn_pos.begin,
5719 9 : tokenStream.currentToken().pos.end);
5720 9 : if (!nextMember)
5721 0 : return NULL;
5722 : } else
5723 : #endif
5724 : {
5725 143129 : PropertyName *field = tokenStream.currentToken().name();
5726 : nextMember = new_<PropertyAccess>(lhs, field,
5727 : lhs->pn_pos.begin,
5728 143129 : tokenStream.currentToken().pos.end);
5729 143129 : if (!nextMember)
5730 0 : return NULL;
5731 : }
5732 : }
5733 : #if JS_HAS_XML_SUPPORT
5734 72 : else if (!tc->inStrictMode()) {
5735 72 : TokenPtr begin = lhs->pn_pos.begin;
5736 72 : if (tt == TOK_LP) {
5737 : /* Filters are effectively 'with', so deoptimize names. */
5738 72 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
5739 :
5740 : StmtInfo stmtInfo;
5741 72 : ParseNode *oldWith = tc->innermostWith;
5742 72 : tc->innermostWith = lhs;
5743 72 : PushStatement(tc, &stmtInfo, STMT_WITH, -1);
5744 :
5745 72 : ParseNode *filter = bracketedExpr();
5746 72 : if (!filter)
5747 0 : return NULL;
5748 72 : filter->setInParens(true);
5749 72 : MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
5750 :
5751 72 : tc->innermostWith = oldWith;
5752 72 : PopStatement(tc);
5753 :
5754 : nextMember =
5755 : new_<XMLFilterExpression>(lhs, filter,
5756 72 : begin, tokenStream.currentToken().pos.end);
5757 72 : if (!nextMember)
5758 0 : return NULL;
5759 0 : } else if (tt == TOK_AT || tt == TOK_STAR) {
5760 0 : ParseNode *propertyId = starOrAtPropertyIdentifier(tt);
5761 0 : if (!propertyId)
5762 0 : return NULL;
5763 : nextMember = new_<XMLProperty>(lhs, propertyId,
5764 0 : begin, tokenStream.currentToken().pos.end);
5765 0 : if (!nextMember)
5766 0 : return NULL;
5767 : } else {
5768 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT);
5769 0 : return NULL;
5770 : }
5771 : }
5772 : #endif
5773 : else {
5774 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT);
5775 0 : return NULL;
5776 : }
5777 : }
5778 : #if JS_HAS_XML_SUPPORT
5779 3837574 : else if (tt == TOK_DBLDOT) {
5780 9 : if (tc->inStrictMode()) {
5781 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT);
5782 0 : return NULL;
5783 : }
5784 :
5785 9 : nextMember = BinaryNode::create(PNK_DBLDOT, tc);
5786 9 : if (!nextMember)
5787 0 : return NULL;
5788 9 : tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
5789 9 : ParseNode *pn3 = primaryExpr(tt, JS_TRUE);
5790 9 : if (!pn3)
5791 0 : return NULL;
5792 9 : if (pn3->isKind(PNK_NAME) && !pn3->isInParens()) {
5793 9 : pn3->setKind(PNK_STRING);
5794 9 : pn3->setArity(PN_NULLARY);
5795 9 : pn3->setOp(JSOP_QNAMEPART);
5796 0 : } else if (!pn3->isXMLPropertyIdentifier()) {
5797 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT);
5798 0 : return NULL;
5799 : }
5800 9 : nextMember->setOp(JSOP_DESCENDANTS);
5801 9 : nextMember->pn_left = lhs;
5802 9 : nextMember->pn_right = pn3;
5803 9 : nextMember->pn_pos.begin = lhs->pn_pos.begin;
5804 9 : nextMember->pn_pos.end = tokenStream.currentToken().pos.end;
5805 : }
5806 : #endif
5807 3837565 : else if (tt == TOK_LB) {
5808 49445 : ParseNode *propExpr = expr();
5809 49445 : if (!propExpr)
5810 0 : return NULL;
5811 :
5812 49445 : MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
5813 49445 : TokenPtr begin = lhs->pn_pos.begin, end = tokenStream.currentToken().pos.end;
5814 :
5815 : /*
5816 : * Optimize property name lookups. If the name is a PropertyName,
5817 : * then make a name-based node so the emitter will use a name-based
5818 : * bytecode. Otherwise make a node using the property expression
5819 : * by value. If the node is a string containing an index, convert
5820 : * it to a number to save work later.
5821 : */
5822 : uint32_t index;
5823 49445 : PropertyName *name = NULL;
5824 49445 : if (propExpr->isKind(PNK_STRING)) {
5825 180 : JSAtom *atom = propExpr->pn_atom;
5826 180 : if (atom->isIndex(&index)) {
5827 9 : propExpr->setKind(PNK_NUMBER);
5828 9 : propExpr->setOp(JSOP_DOUBLE);
5829 9 : propExpr->pn_dval = index;
5830 : } else {
5831 171 : name = atom->asPropertyName();
5832 : }
5833 49265 : } else if (propExpr->isKind(PNK_NUMBER)) {
5834 : JSAtom *atom;
5835 18402 : if (!js_ValueToAtom(context, NumberValue(propExpr->pn_dval), &atom))
5836 0 : return NULL;
5837 18402 : if (!atom->isIndex(&index))
5838 72 : name = atom->asPropertyName();
5839 : }
5840 :
5841 49445 : if (name)
5842 243 : nextMember = new_<PropertyAccess>(lhs, name, begin, end);
5843 : else
5844 49202 : nextMember = new_<PropertyByValue>(lhs, propExpr, begin, end);
5845 49445 : if (!nextMember)
5846 0 : return NULL;
5847 3788120 : } else if (allowCallSyntax && tt == TOK_LP) {
5848 191014 : nextMember = ListNode::create(PNK_LP, tc);
5849 191014 : if (!nextMember)
5850 0 : return NULL;
5851 191014 : nextMember->setOp(JSOP_CALL);
5852 :
5853 191014 : if (lhs->isOp(JSOP_NAME)) {
5854 120748 : if (lhs->pn_atom == context->runtime->atomState.evalAtom) {
5855 : /* Select JSOP_EVAL and flag tc as heavyweight. */
5856 4453 : nextMember->setOp(JSOP_EVAL);
5857 4453 : tc->noteCallsEval();
5858 4453 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
5859 : /*
5860 : * In non-strict mode code, direct calls to eval can add
5861 : * variables to the call object.
5862 : */
5863 4453 : if (!tc->inStrictMode())
5864 4165 : tc->noteHasExtensibleScope();
5865 : }
5866 70266 : } else if (lhs->isOp(JSOP_GETPROP)) {
5867 : /* Select JSOP_FUNAPPLY given foo.apply(...). */
5868 62296 : if (lhs->pn_atom == context->runtime->atomState.applyAtom)
5869 1179 : nextMember->setOp(JSOP_FUNAPPLY);
5870 61117 : else if (lhs->pn_atom == context->runtime->atomState.callAtom)
5871 2198 : nextMember->setOp(JSOP_FUNCALL);
5872 : }
5873 :
5874 191014 : nextMember->initList(lhs);
5875 191014 : nextMember->pn_pos.begin = lhs->pn_pos.begin;
5876 :
5877 191014 : if (!argumentList(nextMember))
5878 10 : return NULL;
5879 191004 : if (nextMember->pn_count > ARGC_LIMIT) {
5880 : JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
5881 0 : JSMSG_TOO_MANY_FUN_ARGS);
5882 0 : return NULL;
5883 : }
5884 191004 : nextMember->pn_pos.end = tokenStream.currentToken().pos.end;
5885 : } else {
5886 3597106 : tokenStream.ungetToken();
5887 3597106 : return lhs;
5888 : }
5889 :
5890 383668 : lhs = nextMember;
5891 : }
5892 51480 : if (tt == TOK_ERROR)
5893 0 : return NULL;
5894 51480 : return lhs;
5895 : }
5896 :
5897 : ParseNode *
5898 231165 : Parser::bracketedExpr()
5899 : {
5900 : unsigned oldflags;
5901 : ParseNode *pn;
5902 :
5903 : /*
5904 : * Always accept the 'in' operator in a parenthesized expression,
5905 : * where it's unambiguous, even if we might be parsing the init of a
5906 : * for statement.
5907 : */
5908 231165 : oldflags = tc->flags;
5909 231165 : tc->flags &= ~TCF_IN_FOR_INIT;
5910 231165 : pn = expr();
5911 231165 : tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
5912 231165 : return pn;
5913 : }
5914 :
5915 : #if JS_HAS_XML_SUPPORT
5916 :
5917 : ParseNode *
5918 0 : Parser::endBracketedExpr()
5919 : {
5920 0 : JS_ASSERT(!tc->inStrictMode());
5921 :
5922 0 : ParseNode *pn = bracketedExpr();
5923 0 : if (!pn)
5924 0 : return NULL;
5925 :
5926 0 : MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
5927 0 : return pn;
5928 : }
5929 :
5930 : /*
5931 : * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
5932 : *
5933 : * AttributeIdentifier:
5934 : * @ PropertySelector
5935 : * @ QualifiedIdentifier
5936 : * @ [ Expression ]
5937 : *
5938 : * PropertySelector:
5939 : * Identifier
5940 : * *
5941 : *
5942 : * QualifiedIdentifier:
5943 : * PropertySelector :: PropertySelector
5944 : * PropertySelector :: [ Expression ]
5945 : *
5946 : * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
5947 : *
5948 : * AttributeIdentifier:
5949 : * @ QualifiedIdentifier
5950 : * @ [ Expression ]
5951 : *
5952 : * PropertySelector:
5953 : * Identifier
5954 : * *
5955 : *
5956 : * QualifiedIdentifier:
5957 : * PropertySelector :: PropertySelector
5958 : * PropertySelector :: [ Expression ]
5959 : * PropertySelector
5960 : *
5961 : * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
5962 : * for that rule to result in a name node, but ECMA-357 extends the grammar
5963 : * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
5964 : *
5965 : * QualifiedIdentifier:
5966 : * PropertySelector QualifiedSuffix
5967 : *
5968 : * QualifiedSuffix:
5969 : * :: PropertySelector
5970 : * :: [ Expression ]
5971 : * /nothing/
5972 : *
5973 : * And use this production instead of PrimaryExpression: QualifiedIdentifier:
5974 : *
5975 : * PrimaryExpression:
5976 : * Identifier QualifiedSuffix
5977 : *
5978 : * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
5979 : * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
5980 : */
5981 : ParseNode *
5982 54 : Parser::propertySelector()
5983 : {
5984 54 : JS_ASSERT(!tc->inStrictMode());
5985 :
5986 : ParseNode *selector;
5987 54 : if (tokenStream.isCurrentTokenType(TOK_STAR)) {
5988 45 : selector = NullaryNode::create(PNK_ANYNAME, tc);
5989 45 : if (!selector)
5990 0 : return NULL;
5991 45 : selector->setOp(JSOP_ANYNAME);
5992 45 : selector->pn_atom = context->runtime->atomState.starAtom;
5993 : } else {
5994 9 : JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
5995 9 : selector = NullaryNode::create(PNK_NAME, tc);
5996 9 : if (!selector)
5997 0 : return NULL;
5998 9 : selector->setOp(JSOP_QNAMEPART);
5999 9 : selector->setArity(PN_NAME);
6000 9 : selector->pn_atom = tokenStream.currentToken().name();
6001 9 : selector->pn_cookie.makeFree();
6002 : }
6003 54 : return selector;
6004 : }
6005 :
6006 : ParseNode *
6007 72 : Parser::qualifiedSuffix(ParseNode *pn)
6008 : {
6009 72 : JS_ASSERT(!tc->inStrictMode());
6010 :
6011 72 : JS_ASSERT(tokenStream.currentToken().type == TOK_DBLCOLON);
6012 72 : ParseNode *pn2 = NameNode::create(PNK_DBLCOLON, NULL, tc);
6013 72 : if (!pn2)
6014 0 : return NULL;
6015 :
6016 : /* This qualifiedSuffice may refer to 'arguments'. */
6017 72 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
6018 72 : tc->noteLocalOverwritesArguments();
6019 :
6020 : /* Left operand of :: must be evaluated if it is an identifier. */
6021 72 : if (pn->isOp(JSOP_QNAMEPART))
6022 0 : pn->setOp(JSOP_NAME);
6023 :
6024 72 : TokenKind tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
6025 72 : if (tt == TOK_STAR || tt == TOK_NAME) {
6026 : /* Inline and specialize propertySelector for JSOP_QNAMECONST. */
6027 72 : pn2->setOp(JSOP_QNAMECONST);
6028 72 : pn2->pn_pos.begin = pn->pn_pos.begin;
6029 : pn2->pn_atom = (tt == TOK_STAR)
6030 : ? context->runtime->atomState.starAtom
6031 72 : : tokenStream.currentToken().name();
6032 72 : pn2->pn_expr = pn;
6033 72 : pn2->pn_cookie.makeFree();
6034 72 : return pn2;
6035 : }
6036 :
6037 0 : if (tt != TOK_LB) {
6038 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
6039 0 : return NULL;
6040 : }
6041 0 : ParseNode *pn3 = endBracketedExpr();
6042 0 : if (!pn3)
6043 0 : return NULL;
6044 :
6045 0 : pn2->setOp(JSOP_QNAME);
6046 0 : pn2->setArity(PN_BINARY);
6047 0 : pn2->pn_pos.begin = pn->pn_pos.begin;
6048 0 : pn2->pn_pos.end = pn3->pn_pos.end;
6049 0 : pn2->pn_left = pn;
6050 0 : pn2->pn_right = pn3;
6051 0 : return pn2;
6052 : }
6053 :
6054 : ParseNode *
6055 54 : Parser::qualifiedIdentifier()
6056 : {
6057 54 : JS_ASSERT(!tc->inStrictMode());
6058 :
6059 54 : ParseNode *pn = propertySelector();
6060 54 : if (!pn)
6061 0 : return NULL;
6062 54 : if (tokenStream.matchToken(TOK_DBLCOLON)) {
6063 : /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
6064 27 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
6065 27 : tc->noteLocalOverwritesArguments();
6066 27 : pn = qualifiedSuffix(pn);
6067 : }
6068 54 : return pn;
6069 : }
6070 :
6071 : ParseNode *
6072 9 : Parser::attributeIdentifier()
6073 : {
6074 9 : JS_ASSERT(!tc->inStrictMode());
6075 :
6076 9 : JS_ASSERT(tokenStream.currentToken().type == TOK_AT);
6077 9 : ParseNode *pn = UnaryNode::create(PNK_AT, tc);
6078 9 : if (!pn)
6079 0 : return NULL;
6080 9 : pn->setOp(JSOP_TOATTRNAME);
6081 :
6082 : ParseNode *pn2;
6083 9 : TokenKind tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
6084 9 : if (tt == TOK_STAR || tt == TOK_NAME) {
6085 9 : pn2 = qualifiedIdentifier();
6086 0 : } else if (tt == TOK_LB) {
6087 0 : pn2 = endBracketedExpr();
6088 : } else {
6089 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
6090 0 : return NULL;
6091 : }
6092 9 : if (!pn2)
6093 0 : return NULL;
6094 9 : pn->pn_kid = pn2;
6095 9 : return pn;
6096 : }
6097 :
6098 : /*
6099 : * Make a TOK_LC unary node whose pn_kid is an expression.
6100 : */
6101 : ParseNode *
6102 0 : Parser::xmlExpr(JSBool inTag)
6103 : {
6104 0 : JS_ASSERT(!tc->inStrictMode());
6105 :
6106 0 : JS_ASSERT(tokenStream.currentToken().type == TOK_LC);
6107 0 : ParseNode *pn = UnaryNode::create(PNK_XMLCURLYEXPR, tc);
6108 0 : if (!pn)
6109 0 : return NULL;
6110 :
6111 : /*
6112 : * Turn off XML tag mode. We save the old value of the flag because it may
6113 : * already be off: XMLExpr is called both from within a tag, and from
6114 : * within text contained in an element, but outside of any start, end, or
6115 : * point tag.
6116 : */
6117 0 : bool oldflag = tokenStream.isXMLTagMode();
6118 0 : tokenStream.setXMLTagMode(false);
6119 0 : ParseNode *pn2 = expr();
6120 0 : if (!pn2)
6121 0 : return NULL;
6122 :
6123 0 : MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
6124 0 : tokenStream.setXMLTagMode(oldflag);
6125 0 : pn->pn_kid = pn2;
6126 0 : pn->setOp(inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR);
6127 0 : return pn;
6128 : }
6129 :
6130 : ParseNode *
6131 297581 : Parser::atomNode(ParseNodeKind kind, JSOp op)
6132 : {
6133 297581 : ParseNode *node = NullaryNode::create(kind, tc);
6134 297581 : if (!node)
6135 0 : return NULL;
6136 297581 : node->setOp(op);
6137 297581 : const Token &tok = tokenStream.currentToken();
6138 297581 : node->pn_atom = tok.atom();
6139 297581 : return node;
6140 : }
6141 :
6142 : /*
6143 : * Parse the productions:
6144 : *
6145 : * XMLNameExpr:
6146 : * XMLName XMLNameExpr?
6147 : * { Expr } XMLNameExpr?
6148 : *
6149 : * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
6150 : * a list of names and/or expressions, a single expression, or a single name.
6151 : * If PN_LIST or PN_NULLARY, getKind() will be PNK_XMLNAME. Otherwise if
6152 : * PN_UNARY, getKind() will be PNK_XMLCURLYEXPR.
6153 : */
6154 : ParseNode *
6155 5695 : Parser::xmlNameExpr()
6156 : {
6157 5695 : JS_ASSERT(!tc->inStrictMode());
6158 :
6159 : ParseNode *pn, *pn2, *list;
6160 : TokenKind tt;
6161 :
6162 5695 : pn = list = NULL;
6163 5695 : do {
6164 5695 : tt = tokenStream.currentToken().type;
6165 5695 : if (tt == TOK_LC) {
6166 0 : pn2 = xmlExpr(JS_TRUE);
6167 0 : if (!pn2)
6168 0 : return NULL;
6169 : } else {
6170 5695 : JS_ASSERT(tt == TOK_XMLNAME);
6171 5695 : JS_ASSERT(tokenStream.currentToken().t_op == JSOP_STRING);
6172 5695 : pn2 = atomNode(PNK_XMLNAME, JSOP_STRING);
6173 5695 : if (!pn2)
6174 0 : return NULL;
6175 : }
6176 :
6177 5695 : if (!pn) {
6178 5695 : pn = pn2;
6179 : } else {
6180 0 : if (!list) {
6181 0 : list = ListNode::create(PNK_XMLNAME, tc);
6182 0 : if (!list)
6183 0 : return NULL;
6184 0 : list->pn_pos.begin = pn->pn_pos.begin;
6185 0 : list->initList(pn);
6186 0 : list->pn_xflags = PNX_CANTFOLD;
6187 0 : pn = list;
6188 : }
6189 0 : pn->pn_pos.end = pn2->pn_pos.end;
6190 0 : pn->append(pn2);
6191 : }
6192 5695 : } while ((tt = tokenStream.getToken()) == TOK_XMLNAME || tt == TOK_LC);
6193 :
6194 5695 : tokenStream.ungetToken();
6195 5695 : return pn;
6196 : }
6197 :
6198 : /*
6199 : * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
6200 : * at compile time into a JSXML tree.
6201 : */
6202 : #define XML_FOLDABLE(pn) ((pn)->isArity(PN_LIST) \
6203 : ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
6204 : : !(pn)->isKind(PNK_XMLCURLYEXPR))
6205 :
6206 : /*
6207 : * Parse the productions:
6208 : *
6209 : * XMLTagContent:
6210 : * XMLNameExpr
6211 : * XMLTagContent S XMLNameExpr S? = S? XMLAttr
6212 : * XMLTagContent S XMLNameExpr S? = S? { Expr }
6213 : *
6214 : * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
6215 : * produces a list of name and attribute values and/or braced expressions, a
6216 : * single expression, or a single name.
6217 : *
6218 : * If PN_LIST or PN_NULLARY, getKind() will be PNK_XMLNAME for the case where
6219 : * XMLTagContent: XMLNameExpr. If getKind() is not PNK_XMLNAME but getArity()
6220 : * is PN_LIST, getKind() will be tagkind. If PN_UNARY, getKind() will be
6221 : * PNK_XMLCURLYEXPR and we parsed exactly one expression.
6222 : */
6223 : ParseNode *
6224 4299 : Parser::xmlTagContent(ParseNodeKind tagkind, JSAtom **namep)
6225 : {
6226 4299 : JS_ASSERT(!tc->inStrictMode());
6227 :
6228 : ParseNode *pn, *pn2, *list;
6229 : TokenKind tt;
6230 :
6231 4299 : pn = xmlNameExpr();
6232 4299 : if (!pn)
6233 0 : return NULL;
6234 4299 : *namep = (pn->isArity(PN_NULLARY)) ? pn->pn_atom : NULL;
6235 4299 : list = NULL;
6236 :
6237 9994 : while (tokenStream.matchToken(TOK_XMLSPACE)) {
6238 1396 : tt = tokenStream.getToken();
6239 1396 : if (tt != TOK_XMLNAME && tt != TOK_LC) {
6240 0 : tokenStream.ungetToken();
6241 0 : break;
6242 : }
6243 :
6244 1396 : pn2 = xmlNameExpr();
6245 1396 : if (!pn2)
6246 0 : return NULL;
6247 1396 : if (!list) {
6248 1396 : list = ListNode::create(tagkind, tc);
6249 1396 : if (!list)
6250 0 : return NULL;
6251 1396 : list->pn_pos.begin = pn->pn_pos.begin;
6252 1396 : list->initList(pn);
6253 1396 : pn = list;
6254 : }
6255 1396 : pn->append(pn2);
6256 1396 : if (!XML_FOLDABLE(pn2))
6257 0 : pn->pn_xflags |= PNX_CANTFOLD;
6258 :
6259 1396 : tokenStream.matchToken(TOK_XMLSPACE);
6260 1396 : MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
6261 1396 : tokenStream.matchToken(TOK_XMLSPACE);
6262 :
6263 1396 : tt = tokenStream.getToken();
6264 1396 : if (tt == TOK_XMLATTR) {
6265 1396 : JS_ASSERT(tokenStream.currentToken().t_op == JSOP_STRING);
6266 1396 : pn2 = atomNode(PNK_XMLATTR, JSOP_STRING);
6267 0 : } else if (tt == TOK_LC) {
6268 0 : pn2 = xmlExpr(JS_TRUE);
6269 0 : pn->pn_xflags |= PNX_CANTFOLD;
6270 : } else {
6271 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_ATTR_VALUE);
6272 0 : return NULL;
6273 : }
6274 1396 : if (!pn2)
6275 0 : return NULL;
6276 1396 : pn->pn_pos.end = pn2->pn_pos.end;
6277 1396 : pn->append(pn2);
6278 : }
6279 :
6280 4299 : return pn;
6281 : }
6282 :
6283 : #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
6284 : JS_BEGIN_MACRO \
6285 : if ((tt) <= TOK_EOF) { \
6286 : if ((tt) == TOK_EOF) { \
6287 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_END_OF_XML_SOURCE); \
6288 : } \
6289 : return result; \
6290 : } \
6291 : JS_END_MACRO
6292 :
6293 : /*
6294 : * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
6295 : * that opens the end tag for the container.
6296 : */
6297 : JSBool
6298 1929 : Parser::xmlElementContent(ParseNode *pn)
6299 : {
6300 1929 : JS_ASSERT(!tc->inStrictMode());
6301 :
6302 1929 : tokenStream.setXMLTagMode(false);
6303 703 : for (;;) {
6304 2632 : TokenKind tt = tokenStream.getToken(TSF_XMLTEXTMODE);
6305 2632 : XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
6306 :
6307 2632 : JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
6308 2632 : JSAtom *textAtom = tokenStream.currentToken().atom();
6309 2632 : if (textAtom) {
6310 : /* Non-zero-length XML text scanned. */
6311 909 : JS_ASSERT(tokenStream.currentToken().t_op == JSOP_STRING);
6312 : ParseNode *pn2 = atomNode(tt == TOK_XMLSPACE ? PNK_XMLSPACE : PNK_XMLTEXT,
6313 909 : JSOP_STRING);
6314 909 : if (!pn2)
6315 0 : return false;
6316 909 : pn->pn_pos.end = pn2->pn_pos.end;
6317 909 : pn->append(pn2);
6318 : }
6319 :
6320 2632 : tt = tokenStream.getToken(TSF_OPERAND);
6321 2632 : XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
6322 2632 : if (tt == TOK_XMLETAGO)
6323 : break;
6324 :
6325 : ParseNode *pn2;
6326 703 : if (tt == TOK_LC) {
6327 0 : pn2 = xmlExpr(JS_FALSE);
6328 0 : if (!pn2)
6329 0 : return false;
6330 0 : pn->pn_xflags |= PNX_CANTFOLD;
6331 703 : } else if (tt == TOK_XMLSTAGO) {
6332 703 : pn2 = xmlElementOrList(JS_FALSE);
6333 703 : if (!pn2)
6334 0 : return false;
6335 703 : pn2->pn_xflags &= ~PNX_XMLROOT;
6336 703 : pn->pn_xflags |= pn2->pn_xflags;
6337 0 : } else if (tt == TOK_XMLPI) {
6338 0 : const Token &tok = tokenStream.currentToken();
6339 0 : pn2 = new_<XMLProcessingInstruction>(tok.xmlPITarget(), tok.xmlPIData(), tok.pos);
6340 0 : if (!pn2)
6341 0 : return false;
6342 : } else {
6343 0 : JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT);
6344 : pn2 = atomNode(tt == TOK_XMLCDATA ? PNK_XMLCDATA : PNK_XMLCOMMENT,
6345 0 : tokenStream.currentToken().t_op);
6346 0 : if (!pn2)
6347 0 : return false;
6348 : }
6349 703 : pn->pn_pos.end = pn2->pn_pos.end;
6350 703 : pn->append(pn2);
6351 : }
6352 1929 : tokenStream.setXMLTagMode(true);
6353 :
6354 1929 : JS_ASSERT(tokenStream.currentToken().type == TOK_XMLETAGO);
6355 1929 : return JS_TRUE;
6356 : }
6357 :
6358 : /*
6359 : * Return a PN_LIST node containing an XML or XMLList Initialiser.
6360 : */
6361 : ParseNode *
6362 2406 : Parser::xmlElementOrList(JSBool allowList)
6363 : {
6364 2406 : JS_ASSERT(!tc->inStrictMode());
6365 :
6366 : ParseNode *pn, *pn2, *list;
6367 : TokenKind tt;
6368 : JSAtom *startAtom, *endAtom;
6369 :
6370 2406 : JS_CHECK_RECURSION(context, return NULL);
6371 :
6372 2406 : JS_ASSERT(tokenStream.currentToken().type == TOK_XMLSTAGO);
6373 2406 : pn = ListNode::create(PNK_XMLSTAGO, tc);
6374 2406 : if (!pn)
6375 0 : return NULL;
6376 :
6377 2406 : tokenStream.setXMLTagMode(true);
6378 2406 : tt = tokenStream.getToken();
6379 2406 : if (tt == TOK_ERROR)
6380 0 : return NULL;
6381 :
6382 2406 : if (tt == TOK_XMLNAME || tt == TOK_LC) {
6383 : /*
6384 : * XMLElement. Append the tag and its contents, if any, to pn.
6385 : */
6386 2388 : pn2 = xmlTagContent(PNK_XMLSTAGO, &startAtom);
6387 2388 : if (!pn2)
6388 0 : return NULL;
6389 2388 : tokenStream.matchToken(TOK_XMLSPACE);
6390 :
6391 2388 : tt = tokenStream.getToken();
6392 2388 : if (tt == TOK_XMLPTAGC) {
6393 : /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
6394 477 : if (pn2->isKind(PNK_XMLSTAGO)) {
6395 18 : pn->makeEmpty();
6396 18 : freeTree(pn);
6397 18 : pn = pn2;
6398 : } else {
6399 459 : JS_ASSERT(pn2->isKind(PNK_XMLNAME) || pn2->isKind(PNK_XMLCURLYEXPR));
6400 459 : pn->initList(pn2);
6401 459 : if (!XML_FOLDABLE(pn2))
6402 0 : pn->pn_xflags |= PNX_CANTFOLD;
6403 : }
6404 477 : pn->setKind(PNK_XMLPTAGC);
6405 477 : pn->pn_xflags |= PNX_XMLROOT;
6406 : } else {
6407 : /* We had better have a tag-close (>) at this point. */
6408 1911 : if (tt != TOK_XMLTAGC) {
6409 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
6410 0 : return NULL;
6411 : }
6412 1911 : pn2->pn_pos.end = tokenStream.currentToken().pos.end;
6413 :
6414 : /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
6415 1911 : if (!pn2->isKind(PNK_XMLSTAGO)) {
6416 533 : pn->initList(pn2);
6417 533 : if (!XML_FOLDABLE(pn2))
6418 0 : pn->pn_xflags |= PNX_CANTFOLD;
6419 533 : pn2 = pn;
6420 533 : pn = ListNode::create(PNK_XMLTAGC, tc);
6421 533 : if (!pn)
6422 0 : return NULL;
6423 : }
6424 :
6425 : /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
6426 1911 : pn->setKind(PNK_XMLELEM);
6427 1911 : pn->pn_pos.begin = pn2->pn_pos.begin;
6428 1911 : pn->initList(pn2);
6429 1911 : if (!XML_FOLDABLE(pn2))
6430 0 : pn->pn_xflags |= PNX_CANTFOLD;
6431 1911 : pn->pn_xflags |= PNX_XMLROOT;
6432 :
6433 : /* Get element contents and delimiting end-tag-open sequence. */
6434 1911 : if (!xmlElementContent(pn))
6435 0 : return NULL;
6436 :
6437 1911 : tt = tokenStream.getToken();
6438 1911 : XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
6439 1911 : if (tt != TOK_XMLNAME && tt != TOK_LC) {
6440 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
6441 0 : return NULL;
6442 : }
6443 :
6444 : /* Parse end tag; check mismatch at compile-time if we can. */
6445 1911 : pn2 = xmlTagContent(PNK_XMLETAGO, &endAtom);
6446 1911 : if (!pn2)
6447 0 : return NULL;
6448 1911 : if (pn2->isKind(PNK_XMLETAGO)) {
6449 : /* Oops, end tag has attributes! */
6450 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
6451 0 : return NULL;
6452 : }
6453 1911 : if (endAtom && startAtom && endAtom != startAtom) {
6454 : /* End vs. start tag name mismatch: point to the tag name. */
6455 : reportErrorNumber(pn2, JSREPORT_UC | JSREPORT_ERROR, JSMSG_XML_TAG_NAME_MISMATCH,
6456 0 : startAtom->chars());
6457 0 : return NULL;
6458 : }
6459 :
6460 : /* Make a TOK_XMLETAGO list with pn2 as its single child. */
6461 1911 : JS_ASSERT(pn2->isKind(PNK_XMLNAME) || pn2->isKind(PNK_XMLCURLYEXPR));
6462 1911 : list = ListNode::create(PNK_XMLETAGO, tc);
6463 1911 : if (!list)
6464 0 : return NULL;
6465 1911 : list->initList(pn2);
6466 1911 : pn->append(list);
6467 1911 : if (!XML_FOLDABLE(pn2)) {
6468 0 : list->pn_xflags |= PNX_CANTFOLD;
6469 0 : pn->pn_xflags |= PNX_CANTFOLD;
6470 : }
6471 :
6472 1911 : tokenStream.matchToken(TOK_XMLSPACE);
6473 1911 : MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
6474 : }
6475 :
6476 : /* Set pn_op now that pn has been updated to its final value. */
6477 2388 : pn->setOp(JSOP_TOXML);
6478 18 : } else if (allowList && tt == TOK_XMLTAGC) {
6479 : /* XMLList Initialiser. */
6480 18 : pn->setKind(PNK_XMLLIST);
6481 18 : pn->setOp(JSOP_TOXMLLIST);
6482 18 : pn->makeEmpty();
6483 18 : pn->pn_xflags |= PNX_XMLROOT;
6484 18 : if (!xmlElementContent(pn))
6485 0 : return NULL;
6486 :
6487 18 : MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
6488 : } else {
6489 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_NAME_SYNTAX);
6490 0 : return NULL;
6491 : }
6492 2406 : tokenStream.setXMLTagMode(false);
6493 :
6494 2406 : pn->pn_pos.end = tokenStream.currentToken().pos.end;
6495 2406 : return pn;
6496 : }
6497 :
6498 : ParseNode *
6499 1703 : Parser::xmlElementOrListRoot(JSBool allowList)
6500 : {
6501 1703 : JS_ASSERT(!tc->inStrictMode());
6502 :
6503 : /*
6504 : * Force XML support to be enabled so that comments and CDATA literals
6505 : * are recognized, instead of <! followed by -- starting an HTML comment
6506 : * to end of line (used in script tags to hide content from old browsers
6507 : * that don't recognize <script>).
6508 : */
6509 1703 : bool hadXML = tokenStream.hasXML();
6510 1703 : tokenStream.setXML(true);
6511 1703 : ParseNode *pn = xmlElementOrList(allowList);
6512 1703 : tokenStream.setXML(hadXML);
6513 1703 : return pn;
6514 : }
6515 :
6516 : ParseNode *
6517 1378 : Parser::parseXMLText(JSObject *chain, bool allowList)
6518 : {
6519 : /*
6520 : * Push a compiler frame if we have no frames, or if the top frame is a
6521 : * lightweight function activation, or if its scope chain doesn't match
6522 : * the one passed to us.
6523 : */
6524 2756 : TreeContext xmltc(this);
6525 1378 : if (!xmltc.init(context))
6526 0 : return NULL;
6527 1378 : JS_ASSERT(!xmltc.inStrictMode());
6528 1378 : xmltc.setScopeChain(chain);
6529 :
6530 : /* Set XML-only mode to turn off special treatment of {expr} in XML. */
6531 1378 : tokenStream.setXMLOnlyMode();
6532 1378 : TokenKind tt = tokenStream.getToken(TSF_OPERAND);
6533 :
6534 : ParseNode *pn;
6535 1378 : if (tt != TOK_XMLSTAGO) {
6536 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_MARKUP);
6537 0 : pn = NULL;
6538 : } else {
6539 1378 : pn = xmlElementOrListRoot(allowList);
6540 : }
6541 1378 : tokenStream.setXMLOnlyMode(false);
6542 :
6543 1378 : return pn;
6544 : }
6545 :
6546 : #endif /* JS_HAS_XMLSUPPORT */
6547 :
6548 : bool
6549 9 : Parser::checkForFunctionNode(PropertyName *name, ParseNode *node)
6550 : {
6551 : /*
6552 : * In |a.ns::name|, |ns| refers to an in-scope variable, so |ns| can't be a
6553 : * keyword. (Exception: |function::name| is the actual name property, not
6554 : * what E4X would expose.) We parsed |ns| accepting a keyword as a name,
6555 : * so we must implement the keyword restriction manually in this case.
6556 : */
6557 9 : if (const KeywordInfo *ki = FindKeyword(name->charsZ(), name->length())) {
6558 9 : if (ki->tokentype != TOK_FUNCTION) {
6559 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_KEYWORD_NOT_NS);
6560 0 : return false;
6561 : }
6562 :
6563 9 : node->setArity(PN_NULLARY);
6564 9 : node->setKind(PNK_FUNCTION);
6565 : }
6566 :
6567 9 : return true;
6568 : }
6569 :
6570 : #if JS_HAS_XML_SUPPORT
6571 : ParseNode *
6572 9 : Parser::propertyQualifiedIdentifier()
6573 : {
6574 9 : JS_ASSERT(!tc->inStrictMode());
6575 9 : JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
6576 9 : JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NAME);
6577 9 : JS_ASSERT(tokenStream.peekToken() == TOK_DBLCOLON);
6578 :
6579 : /* Deoptimize QualifiedIdentifier properties to avoid tricky analysis. */
6580 9 : tc->flags |= TCF_FUN_HEAVYWEIGHT;
6581 9 : tc->noteLocalOverwritesArguments();
6582 :
6583 9 : PropertyName *name = tokenStream.currentToken().name();
6584 9 : ParseNode *node = NameNode::create(PNK_NAME, name, tc);
6585 9 : if (!node)
6586 0 : return NULL;
6587 9 : node->setOp(JSOP_NAME);
6588 9 : node->pn_dflags |= PND_DEOPTIMIZED;
6589 :
6590 9 : if (!checkForFunctionNode(name, node))
6591 0 : return NULL;
6592 :
6593 9 : tokenStream.consumeKnownToken(TOK_DBLCOLON);
6594 9 : return qualifiedSuffix(node);
6595 : }
6596 : #endif
6597 :
6598 : ParseNode *
6599 2494533 : Parser::identifierName(bool afterDoubleDot)
6600 : {
6601 2494533 : JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
6602 :
6603 2494533 : PropertyName *name = tokenStream.currentToken().name();
6604 2494533 : ParseNode *node = NameNode::create(PNK_NAME, name, tc);
6605 2494533 : if (!node)
6606 0 : return NULL;
6607 2494533 : JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NAME);
6608 2494533 : node->setOp(JSOP_NAME);
6609 :
6610 2494533 : if ((tc->flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION &&
6611 : name == context->runtime->atomState.argumentsAtom)
6612 : {
6613 : /*
6614 : * Bind early to JSOP_ARGUMENTS to relieve later code from having
6615 : * to do this work (new rule for the emitter to count on).
6616 : */
6617 8586 : if (!afterDoubleDot) {
6618 : /*
6619 : * Note use of |arguments| to ensure we can properly create the
6620 : * |arguments| object for this function.
6621 : */
6622 4284 : tc->noteArgumentsNameUse(node);
6623 :
6624 4284 : if (!(tc->flags & TCF_DECL_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
6625 4284 : node->setOp(JSOP_ARGUMENTS);
6626 4284 : node->pn_dflags |= PND_BOUND;
6627 : }
6628 : }
6629 4980480 : } else if ((!afterDoubleDot
6630 : #if JS_HAS_XML_SUPPORT
6631 0 : || (!tc->inStrictMode() && tokenStream.peekToken() == TOK_DBLCOLON)
6632 : #endif
6633 2490240 : ) && !(tc->flags & TCF_DECL_DESTRUCTURING))
6634 : {
6635 : /* In case this is a generator expression outside of any function. */
6636 2485992 : if (!tc->inFunction() && name == context->runtime->atomState.argumentsAtom)
6637 350 : tc->countArgumentsUse(node);
6638 :
6639 2485992 : StmtInfo *stmt = LexicalLookup(tc, name, NULL);
6640 :
6641 2485992 : MultiDeclRange mdl = tc->decls.lookupMulti(name);
6642 :
6643 : Definition *dn;
6644 2485992 : if (!mdl.empty()) {
6645 697132 : dn = mdl.front();
6646 : } else {
6647 3577720 : if (AtomDefnAddPtr p = tc->lexdeps->lookupForAdd(name)) {
6648 1557469 : dn = p.value();
6649 : } else {
6650 : /*
6651 : * No definition before this use in any lexical scope.
6652 : * Create a placeholder definition node to either:
6653 : * - Be adopted when we parse the real defining
6654 : * declaration, or
6655 : * - Be left as a free variable definition if we never
6656 : * see the real definition.
6657 : */
6658 231391 : dn = MakePlaceholder(node, tc);
6659 231391 : if (!dn || !tc->lexdeps->add(p, name, dn))
6660 0 : return NULL;
6661 : }
6662 : }
6663 :
6664 2485992 : JS_ASSERT(dn->isDefn());
6665 2485992 : LinkUseToDef(node, dn, tc);
6666 :
6667 2485992 : if (stmt && stmt->type == STMT_WITH)
6668 720 : node->pn_dflags |= PND_DEOPTIMIZED;
6669 : }
6670 :
6671 : #if JS_HAS_XML_SUPPORT
6672 2494533 : if (!tc->inStrictMode() && tokenStream.matchToken(TOK_DBLCOLON)) {
6673 9 : if (afterDoubleDot) {
6674 0 : if (!checkForFunctionNode(name, node))
6675 0 : return NULL;
6676 : }
6677 9 : node = qualifiedSuffix(node);
6678 9 : if (!node)
6679 0 : return NULL;
6680 : }
6681 : #endif
6682 :
6683 2494533 : return node;
6684 : }
6685 :
6686 : #if JS_HAS_XML_SUPPORT
6687 : ParseNode *
6688 72 : Parser::starOrAtPropertyIdentifier(TokenKind tt)
6689 : {
6690 72 : JS_ASSERT(tt == TOK_AT || tt == TOK_STAR);
6691 72 : if (tc->inStrictMode()) {
6692 18 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
6693 18 : return NULL;
6694 : }
6695 54 : return (tt == TOK_AT) ? attributeIdentifier() : qualifiedIdentifier();
6696 : }
6697 : #endif
6698 :
6699 : ParseNode *
6700 3621600 : Parser::primaryExpr(TokenKind tt, bool afterDoubleDot)
6701 : {
6702 3621600 : JS_ASSERT(tokenStream.isCurrentTokenType(tt));
6703 :
6704 : ParseNode *pn, *pn2, *pn3;
6705 : JSOp op;
6706 :
6707 3621600 : JS_CHECK_RECURSION(context, return NULL);
6708 :
6709 3621600 : switch (tt) {
6710 : case TOK_FUNCTION:
6711 : #if JS_HAS_XML_SUPPORT
6712 127680 : if (!tc->inStrictMode() && tokenStream.matchToken(TOK_DBLCOLON, TSF_KEYWORD_IS_NAME)) {
6713 27 : pn2 = NullaryNode::create(PNK_FUNCTION, tc);
6714 27 : if (!pn2)
6715 0 : return NULL;
6716 27 : pn = qualifiedSuffix(pn2);
6717 27 : if (!pn)
6718 0 : return NULL;
6719 27 : break;
6720 : }
6721 : #endif
6722 127653 : pn = functionExpr();
6723 127653 : if (!pn)
6724 9 : return NULL;
6725 127644 : break;
6726 :
6727 : case TOK_LB:
6728 : {
6729 : JSBool matched;
6730 : unsigned index;
6731 :
6732 28294 : pn = ListNode::create(PNK_RB, tc);
6733 28294 : if (!pn)
6734 0 : return NULL;
6735 28294 : pn->setOp(JSOP_NEWINIT);
6736 28294 : pn->makeEmpty();
6737 :
6738 : #if JS_HAS_GENERATORS
6739 28294 : pn->pn_blockid = tc->blockidGen;
6740 : #endif
6741 :
6742 28294 : matched = tokenStream.matchToken(TOK_RB, TSF_OPERAND);
6743 28294 : if (!matched) {
6744 64831 : for (index = 0; ; index++) {
6745 64831 : if (index == StackSpace::ARGS_LENGTH_MAX) {
6746 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ARRAY_INIT_TOO_BIG);
6747 0 : return NULL;
6748 : }
6749 :
6750 64831 : tt = tokenStream.peekToken(TSF_OPERAND);
6751 64831 : if (tt == TOK_RB) {
6752 603 : pn->pn_xflags |= PNX_ENDCOMMA;
6753 603 : break;
6754 : }
6755 :
6756 64228 : if (tt == TOK_COMMA) {
6757 : /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
6758 5922 : tokenStream.matchToken(TOK_COMMA);
6759 5922 : pn2 = NullaryNode::create(PNK_COMMA, tc);
6760 5922 : pn->pn_xflags |= PNX_HOLEY | PNX_NONCONST;
6761 : } else {
6762 58306 : pn2 = assignExpr();
6763 58306 : if (pn2 && !pn2->isConstant())
6764 18704 : pn->pn_xflags |= PNX_NONCONST;
6765 : }
6766 64228 : if (!pn2)
6767 0 : return NULL;
6768 64228 : pn->append(pn2);
6769 :
6770 64228 : if (tt != TOK_COMMA) {
6771 : /* If we didn't already match TOK_COMMA in above case. */
6772 58306 : if (!tokenStream.matchToken(TOK_COMMA))
6773 18029 : break;
6774 : }
6775 : }
6776 :
6777 : #if JS_HAS_GENERATORS
6778 : /*
6779 : * At this point, (index == 0 && pn->pn_count != 0) implies one
6780 : * element initialiser was parsed.
6781 : *
6782 : * An array comprehension of the form:
6783 : *
6784 : * [i * j for (i in o) for (j in p) if (i != j)]
6785 : *
6786 : * translates to roughly the following let expression:
6787 : *
6788 : * let (array = new Array, i, j) {
6789 : * for (i in o) let {
6790 : * for (j in p)
6791 : * if (i != j)
6792 : * array.push(i * j)
6793 : * }
6794 : * array
6795 : * }
6796 : *
6797 : * where array is a nameless block-local variable. The "roughly"
6798 : * means that an implementation may optimize away the array.push.
6799 : * An array comprehension opens exactly one block scope, no matter
6800 : * how many for heads it contains.
6801 : *
6802 : * Each let () {...} or for (let ...) ... compiles to:
6803 : *
6804 : * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
6805 : *
6806 : * where <o> is a literal object representing the block scope,
6807 : * with <n> properties, naming each var declared in the block.
6808 : *
6809 : * Each var declaration in a let-block binds a name in <o> at
6810 : * compile time, and allocates a slot on the operand stack at
6811 : * runtime via JSOP_ENTERBLOCK. A block-local var is accessed by
6812 : * the JSOP_GETLOCAL and JSOP_SETLOCAL ops. These ops have an
6813 : * immediate operand, the local slot's stack index from fp->spbase.
6814 : *
6815 : * The array comprehension iteration step, array.push(i * j) in
6816 : * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
6817 : * where <array> is the index of array's stack slot.
6818 : */
6819 18632 : if (index == 0 && pn->pn_count != 0 && tokenStream.matchToken(TOK_FOR)) {
6820 : ParseNode *pnexp, *pntop;
6821 :
6822 : /* Relabel pn as an array comprehension node. */
6823 450 : pn->setKind(PNK_ARRAYCOMP);
6824 :
6825 : /*
6826 : * Remove the comprehension expression from pn's linked list
6827 : * and save it via pnexp. We'll re-install it underneath the
6828 : * ARRAYPUSH node after we parse the rest of the comprehension.
6829 : */
6830 450 : pnexp = pn->last();
6831 450 : JS_ASSERT(pn->pn_count == 1);
6832 450 : pn->pn_count = 0;
6833 450 : pn->pn_tail = &pn->pn_head;
6834 450 : *pn->pn_tail = NULL;
6835 :
6836 : pntop = comprehensionTail(pnexp, pn->pn_blockid, false,
6837 450 : PNK_ARRAYPUSH, JSOP_ARRAYPUSH);
6838 450 : if (!pntop)
6839 135 : return NULL;
6840 315 : pn->append(pntop);
6841 : }
6842 : #endif /* JS_HAS_GENERATORS */
6843 :
6844 18497 : MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
6845 : }
6846 28159 : pn->pn_pos.end = tokenStream.currentToken().pos.end;
6847 28159 : return pn;
6848 : }
6849 :
6850 : case TOK_LC:
6851 : {
6852 : ParseNode *pnval;
6853 :
6854 : /*
6855 : * A map from property names we've seen thus far to a mask of property
6856 : * assignment types, stored and retrieved with ALE_SET_INDEX/ALE_INDEX.
6857 : */
6858 31626 : AtomIndexMap seen(context);
6859 :
6860 : enum AssignmentType {
6861 : GET = 0x1,
6862 : SET = 0x2,
6863 : VALUE = 0x4 | GET | SET
6864 : };
6865 :
6866 15813 : pn = ListNode::create(PNK_RC, tc);
6867 15813 : if (!pn)
6868 0 : return NULL;
6869 15813 : pn->setOp(JSOP_NEWINIT);
6870 15813 : pn->makeEmpty();
6871 :
6872 15220 : for (;;) {
6873 : JSAtom *atom;
6874 31033 : TokenKind ltok = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
6875 31033 : TokenPtr begin = tokenStream.currentToken().pos.begin;
6876 31033 : switch (ltok) {
6877 : case TOK_NUMBER:
6878 531 : pn3 = NullaryNode::create(PNK_NUMBER, tc);
6879 531 : if (!pn3)
6880 0 : return NULL;
6881 531 : pn3->pn_dval = tokenStream.currentToken().number();
6882 531 : if (!js_ValueToAtom(context, DoubleValue(pn3->pn_dval), &atom))
6883 0 : return NULL;
6884 531 : break;
6885 : case TOK_NAME:
6886 : {
6887 16905 : atom = tokenStream.currentToken().name();
6888 16905 : if (atom == context->runtime->atomState.getAtom) {
6889 567 : op = JSOP_GETTER;
6890 16338 : } else if (atom == context->runtime->atomState.setAtom) {
6891 379 : op = JSOP_SETTER;
6892 : } else {
6893 15959 : pn3 = NullaryNode::create(PNK_NAME, tc);
6894 15959 : if (!pn3)
6895 0 : return NULL;
6896 15959 : pn3->pn_atom = atom;
6897 15959 : break;
6898 : }
6899 :
6900 946 : tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
6901 946 : if (tt == TOK_NAME) {
6902 297 : atom = tokenStream.currentToken().name();
6903 297 : pn3 = NameNode::create(PNK_NAME, atom, tc);
6904 297 : if (!pn3)
6905 0 : return NULL;
6906 649 : } else if (tt == TOK_STRING) {
6907 0 : atom = tokenStream.currentToken().atom();
6908 :
6909 : uint32_t index;
6910 0 : if (atom->isIndex(&index)) {
6911 0 : pn3 = NullaryNode::create(PNK_NUMBER, tc);
6912 0 : if (!pn3)
6913 0 : return NULL;
6914 0 : pn3->pn_dval = index;
6915 0 : if (!js_ValueToAtom(context, DoubleValue(pn3->pn_dval), &atom))
6916 0 : return NULL;
6917 : } else {
6918 0 : pn3 = NameNode::create(PNK_STRING, atom, tc);
6919 0 : if (!pn3)
6920 0 : return NULL;
6921 : }
6922 649 : } else if (tt == TOK_NUMBER) {
6923 0 : pn3 = NullaryNode::create(PNK_NUMBER, tc);
6924 0 : if (!pn3)
6925 0 : return NULL;
6926 0 : pn3->pn_dval = tokenStream.currentToken().number();
6927 0 : if (!js_ValueToAtom(context, DoubleValue(pn3->pn_dval), &atom))
6928 0 : return NULL;
6929 : } else {
6930 649 : tokenStream.ungetToken();
6931 649 : pn3 = NullaryNode::create(PNK_NAME, tc);
6932 649 : if (!pn3)
6933 0 : return NULL;
6934 649 : pn3->pn_atom = atom;
6935 649 : break;
6936 : }
6937 :
6938 297 : pn->pn_xflags |= PNX_NONCONST;
6939 :
6940 : /* NB: Getter function in { get x(){} } is unnamed. */
6941 297 : pn2 = functionDef(NULL, op == JSOP_GETTER ? Getter : Setter, Expression);
6942 297 : if (!pn2)
6943 0 : return NULL;
6944 297 : TokenPos pos = {begin, pn2->pn_pos.end};
6945 297 : pn2 = new_<BinaryNode>(PNK_COLON, op, pos, pn3, pn2);
6946 297 : goto skip;
6947 : }
6948 : case TOK_STRING: {
6949 7875 : atom = tokenStream.currentToken().atom();
6950 : uint32_t index;
6951 7875 : if (atom->isIndex(&index)) {
6952 513 : pn3 = NullaryNode::create(PNK_NUMBER, tc);
6953 513 : if (!pn3)
6954 0 : return NULL;
6955 513 : pn3->pn_dval = index;
6956 : } else {
6957 7362 : pn3 = NullaryNode::create(PNK_STRING, tc);
6958 7362 : if (!pn3)
6959 0 : return NULL;
6960 7362 : pn3->pn_atom = atom;
6961 : }
6962 7875 : break;
6963 : }
6964 : case TOK_RC:
6965 5722 : goto end_obj_init;
6966 : default:
6967 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_PROP_ID);
6968 0 : return NULL;
6969 : }
6970 :
6971 25014 : op = JSOP_INITPROP;
6972 25014 : tt = tokenStream.getToken();
6973 25014 : if (tt == TOK_COLON) {
6974 24816 : pnval = assignExpr();
6975 24816 : if (!pnval)
6976 9 : return NULL;
6977 :
6978 : /*
6979 : * Treat initializers which mutate __proto__ as non-constant,
6980 : * so that we can later assume singleton objects delegate to
6981 : * the default Object.prototype.
6982 : */
6983 24807 : if (!pnval->isConstant() || atom == context->runtime->atomState.protoAtom)
6984 12806 : pn->pn_xflags |= PNX_NONCONST;
6985 : }
6986 : #if JS_HAS_DESTRUCTURING_SHORTHAND
6987 198 : else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
6988 : /*
6989 : * Support, e.g., |var {x, y} = o| as destructuring shorthand
6990 : * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
6991 : */
6992 198 : tokenStream.ungetToken();
6993 198 : if (!tokenStream.checkForKeyword(atom->charsZ(), atom->length(), NULL, NULL))
6994 0 : return NULL;
6995 198 : pn->pn_xflags |= PNX_DESTRUCT | PNX_NONCONST;
6996 198 : pnval = pn3;
6997 198 : JS_ASSERT(pnval->isKind(PNK_NAME));
6998 198 : pnval->setArity(PN_NAME);
6999 198 : ((NameNode *)pnval)->initCommon(tc);
7000 : }
7001 : #endif
7002 : else {
7003 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_COLON_AFTER_ID);
7004 0 : return NULL;
7005 : }
7006 :
7007 : {
7008 25005 : TokenPos pos = {begin, pnval->pn_pos.end};
7009 25005 : pn2 = new_<BinaryNode>(PNK_COLON, op, pos, pn3, pnval);
7010 : }
7011 : skip:
7012 25302 : if (!pn2)
7013 0 : return NULL;
7014 25302 : pn->append(pn2);
7015 :
7016 : /*
7017 : * Check for duplicate property names. Duplicate data properties
7018 : * only conflict in strict mode. Duplicate getter or duplicate
7019 : * setter halves always conflict. A data property conflicts with
7020 : * any part of an accessor property.
7021 : */
7022 : AssignmentType assignType;
7023 25302 : if (op == JSOP_INITPROP) {
7024 25005 : assignType = VALUE;
7025 297 : } else if (op == JSOP_GETTER) {
7026 207 : assignType = GET;
7027 90 : } else if (op == JSOP_SETTER) {
7028 90 : assignType = SET;
7029 : } else {
7030 0 : JS_NOT_REACHED("bad opcode in object initializer");
7031 : assignType = VALUE; /* try to error early */
7032 : }
7033 :
7034 50604 : AtomIndexAddPtr p = seen.lookupForAdd(atom);
7035 25302 : if (p) {
7036 90 : jsatomid index = p.value();
7037 90 : AssignmentType oldAssignType = AssignmentType(index);
7038 144 : if ((oldAssignType & assignType) &&
7039 54 : (oldAssignType != VALUE || assignType != VALUE || tc->needStrictChecks()))
7040 : {
7041 0 : JSAutoByteString name;
7042 0 : if (!js_AtomToPrintableString(context, atom, &name))
7043 0 : return NULL;
7044 :
7045 : unsigned flags = (oldAssignType == VALUE &&
7046 : assignType == VALUE &&
7047 0 : !tc->inStrictMode())
7048 : ? JSREPORT_WARNING
7049 0 : : JSREPORT_ERROR;
7050 0 : if (!ReportCompileErrorNumber(context, &tokenStream, NULL, flags,
7051 0 : JSMSG_DUPLICATE_PROPERTY, name.ptr()))
7052 : {
7053 0 : return NULL;
7054 : }
7055 : }
7056 90 : p.value() = assignType | oldAssignType;
7057 : } else {
7058 25212 : if (!seen.add(p, atom, assignType))
7059 0 : return NULL;
7060 : }
7061 :
7062 25302 : tt = tokenStream.getToken();
7063 25302 : if (tt == TOK_RC)
7064 : goto end_obj_init;
7065 15220 : if (tt != TOK_COMMA) {
7066 0 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CURLY_AFTER_LIST);
7067 0 : return NULL;
7068 : }
7069 : }
7070 :
7071 : end_obj_init:
7072 15804 : pn->pn_pos.end = tokenStream.currentToken().pos.end;
7073 15804 : return pn;
7074 : }
7075 :
7076 : #if JS_HAS_BLOCK_SCOPE
7077 : case TOK_LET:
7078 1765 : pn = letBlock(LetExpresion);
7079 1765 : if (!pn)
7080 0 : return NULL;
7081 1765 : break;
7082 : #endif
7083 :
7084 : case TOK_LP:
7085 : {
7086 : JSBool genexp;
7087 :
7088 133831 : pn = parenExpr(&genexp);
7089 133831 : if (!pn)
7090 27 : return NULL;
7091 133804 : pn->setInParens(true);
7092 133804 : if (!genexp)
7093 133624 : MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
7094 133804 : break;
7095 : }
7096 :
7097 : case TOK_STRING:
7098 289554 : pn = atomNode(PNK_STRING, JSOP_STRING);
7099 289554 : if (!pn)
7100 0 : return NULL;
7101 289554 : break;
7102 :
7103 : #if JS_HAS_XML_SUPPORT
7104 : case TOK_AT:
7105 : case TOK_STAR:
7106 72 : pn = starOrAtPropertyIdentifier(tt);
7107 72 : break;
7108 :
7109 : case TOK_XMLSTAGO:
7110 325 : pn = xmlElementOrListRoot(JS_TRUE);
7111 325 : if (!pn)
7112 0 : return NULL;
7113 325 : break;
7114 :
7115 : case TOK_XMLCDATA:
7116 27 : JS_ASSERT(!tc->inStrictMode());
7117 27 : pn = atomNode(PNK_XMLCDATA, JSOP_XMLCDATA);
7118 27 : if (!pn)
7119 0 : return NULL;
7120 27 : break;
7121 :
7122 : case TOK_XMLCOMMENT:
7123 0 : JS_ASSERT(!tc->inStrictMode());
7124 0 : pn = atomNode(PNK_XMLCOMMENT, JSOP_XMLCOMMENT);
7125 0 : if (!pn)
7126 0 : return NULL;
7127 0 : break;
7128 :
7129 : case TOK_XMLPI: {
7130 0 : JS_ASSERT(!tc->inStrictMode());
7131 0 : const Token &tok = tokenStream.currentToken();
7132 0 : pn = new_<XMLProcessingInstruction>(tok.xmlPITarget(), tok.xmlPIData(), tok.pos);
7133 0 : if (!pn)
7134 0 : return NULL;
7135 0 : break;
7136 : }
7137 : #endif
7138 :
7139 : case TOK_NAME:
7140 2494533 : pn = identifierName(afterDoubleDot);
7141 2494533 : break;
7142 :
7143 : case TOK_REGEXP:
7144 : {
7145 3686 : pn = NullaryNode::create(PNK_REGEXP, tc);
7146 3686 : if (!pn)
7147 0 : return NULL;
7148 :
7149 3686 : const jschar *chars = tokenStream.getTokenbuf().begin();
7150 3686 : size_t length = tokenStream.getTokenbuf().length();
7151 3686 : RegExpFlag flags = tokenStream.currentToken().regExpFlags();
7152 3686 : RegExpStatics *res = context->regExpStatics();
7153 :
7154 : RegExpObject *reobj;
7155 3686 : if (context->hasfp())
7156 126 : reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream);
7157 : else
7158 3560 : reobj = RegExpObject::createNoStatics(context, chars, length, flags, &tokenStream);
7159 :
7160 3686 : if (!reobj)
7161 9 : return NULL;
7162 :
7163 3677 : if (!tc->compileAndGo()) {
7164 18 : if (!reobj->clearParent(context))
7165 0 : return NULL;
7166 18 : if (!reobj->clearType(context))
7167 0 : return NULL;
7168 : }
7169 :
7170 3677 : pn->pn_objbox = tc->parser->newObjectBox(reobj);
7171 3677 : if (!pn->pn_objbox)
7172 0 : return NULL;
7173 :
7174 3677 : pn->setOp(JSOP_REGEXP);
7175 3677 : break;
7176 : }
7177 :
7178 : case TOK_NUMBER:
7179 399588 : pn = NullaryNode::create(PNK_NUMBER, tc);
7180 399588 : if (!pn)
7181 0 : return NULL;
7182 399588 : pn->setOp(JSOP_DOUBLE);
7183 399588 : pn->pn_dval = tokenStream.currentToken().number();
7184 399588 : break;
7185 :
7186 : case TOK_TRUE:
7187 12483 : return new_<BooleanLiteral>(true, tokenStream.currentToken().pos);
7188 : case TOK_FALSE:
7189 9484 : return new_<BooleanLiteral>(false, tokenStream.currentToken().pos);
7190 : case TOK_THIS:
7191 90824 : return new_<ThisLiteral>(tokenStream.currentToken().pos);
7192 : case TOK_NULL:
7193 13619 : return new_<NullLiteral>(tokenStream.currentToken().pos);
7194 :
7195 : case TOK_ERROR:
7196 : /* The scanner or one of its subroutines reported the error. */
7197 0 : return NULL;
7198 :
7199 : default:
7200 22 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
7201 22 : return NULL;
7202 : }
7203 3451016 : return pn;
7204 : }
7205 :
7206 : ParseNode *
7207 231093 : Parser::parenExpr(JSBool *genexp)
7208 : {
7209 : TokenPtr begin;
7210 : ParseNode *pn;
7211 :
7212 231093 : JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
7213 231093 : begin = tokenStream.currentToken().pos.begin;
7214 :
7215 231093 : if (genexp)
7216 133831 : *genexp = JS_FALSE;
7217 :
7218 231093 : GenexpGuard guard(tc);
7219 :
7220 231093 : pn = bracketedExpr();
7221 231093 : if (!pn)
7222 18 : return NULL;
7223 231075 : guard.endBody();
7224 :
7225 : #if JS_HAS_GENERATOR_EXPRS
7226 231075 : if (tokenStream.matchToken(TOK_FOR)) {
7227 189 : if (!guard.checkValidBody(pn))
7228 9 : return NULL;
7229 180 : JS_ASSERT(!pn->isKind(PNK_YIELD));
7230 180 : if (pn->isKind(PNK_COMMA) && !pn->isInParens()) {
7231 : reportErrorNumber(pn->last(), JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
7232 0 : js_generator_str);
7233 0 : return NULL;
7234 : }
7235 180 : pn = generatorExpr(pn);
7236 180 : if (!pn)
7237 0 : return NULL;
7238 180 : pn->pn_pos.begin = begin;
7239 180 : if (genexp) {
7240 180 : if (tokenStream.getToken() != TOK_RP) {
7241 : reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
7242 0 : js_generator_str);
7243 0 : return NULL;
7244 : }
7245 180 : pn->pn_pos.end = tokenStream.currentToken().pos.end;
7246 180 : *genexp = JS_TRUE;
7247 : }
7248 : } else
7249 : #endif /* JS_HAS_GENERATOR_EXPRS */
7250 :
7251 230886 : if (!guard.maybeNoteGenerator(pn))
7252 0 : return NULL;
7253 :
7254 231066 : return pn;
7255 : }
|