Parent Directory
|
Revision Log
Use SpiderMonkey from Firefox 3.1b2.
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 jsparse.h). |
48 | * After tree construction, it rewrites trees to fold constants and evaluate |
49 | * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to |
50 | * generate bytecode. |
51 | * |
52 | * This parser attempts no error recovery. |
53 | */ |
54 | #include "jsstddef.h" |
55 | #include <stdlib.h> |
56 | #include <string.h> |
57 | #include <math.h> |
58 | #include "jstypes.h" |
59 | #include "jsarena.h" /* Added by JSIFY */ |
60 | #include "jsutil.h" /* Added by JSIFY */ |
61 | #include "jsapi.h" |
62 | #include "jsarray.h" |
63 | #include "jsatom.h" |
64 | #include "jscntxt.h" |
65 | #include "jsversion.h" |
66 | #include "jsemit.h" |
67 | #include "jsfun.h" |
68 | #include "jsinterp.h" |
69 | #include "jsiter.h" |
70 | #include "jslock.h" |
71 | #include "jsnum.h" |
72 | #include "jsobj.h" |
73 | #include "jsopcode.h" |
74 | #include "jsparse.h" |
75 | #include "jsscan.h" |
76 | #include "jsscope.h" |
77 | #include "jsscript.h" |
78 | #include "jsstr.h" |
79 | #include "jsstaticcheck.h" |
80 | |
81 | #if JS_HAS_XML_SUPPORT |
82 | #include "jsxml.h" |
83 | #endif |
84 | |
85 | #if JS_HAS_DESTRUCTURING |
86 | #include "jsdhash.h" |
87 | #endif |
88 | |
89 | /* |
90 | * Asserts to verify assumptions behind pn_ macros. |
91 | */ |
92 | JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.atom) == |
93 | offsetof(JSParseNode, pn_u.apair.atom)); |
94 | JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.slot) == |
95 | offsetof(JSParseNode, pn_u.lexical.slot)); |
96 | |
97 | /* |
98 | * JS parsers, from lowest to highest precedence. |
99 | * |
100 | * Each parser takes a context, a token stream, and a tree context struct. |
101 | * Each returns a parse node tree or null on error. |
102 | */ |
103 | |
104 | typedef JSParseNode * |
105 | JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc); |
106 | |
107 | typedef JSParseNode * |
108 | JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, |
109 | JSBool allowCallSyntax); |
110 | |
111 | typedef JSParseNode * |
112 | JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, |
113 | JSTokenType tt, JSBool afterDot); |
114 | |
115 | typedef JSParseNode * |
116 | JSParenParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, |
117 | JSParseNode *pn1, JSBool *genexp); |
118 | |
119 | static JSParser FunctionStmt; |
120 | static JSParser FunctionExpr; |
121 | static JSParser Statements; |
122 | static JSParser Statement; |
123 | static JSParser Variables; |
124 | static JSParser Expr; |
125 | static JSParser AssignExpr; |
126 | static JSParser CondExpr; |
127 | static JSParser OrExpr; |
128 | static JSParser AndExpr; |
129 | static JSParser BitOrExpr; |
130 | static JSParser BitXorExpr; |
131 | static JSParser BitAndExpr; |
132 | static JSParser EqExpr; |
133 | static JSParser RelExpr; |
134 | static JSParser ShiftExpr; |
135 | static JSParser AddExpr; |
136 | static JSParser MulExpr; |
137 | static JSParser UnaryExpr; |
138 | static JSMemberParser MemberExpr; |
139 | static JSPrimaryParser PrimaryExpr; |
140 | static JSParenParser ParenExpr; |
141 | |
142 | /* |
143 | * Insist that the next token be of type tt, or report errno and return null. |
144 | * NB: this macro uses cx and ts from its lexical environment. |
145 | */ |
146 | #define MUST_MATCH_TOKEN(tt, errno) \ |
147 | JS_BEGIN_MACRO \ |
148 | if (js_GetToken(cx, ts) != tt) { \ |
149 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \ |
150 | return NULL; \ |
151 | } \ |
152 | JS_END_MACRO |
153 | |
154 | #ifdef METER_PARSENODES |
155 | static uint32 parsenodes = 0; |
156 | static uint32 maxparsenodes = 0; |
157 | static uint32 recyclednodes = 0; |
158 | #endif |
159 | |
160 | JSBool |
161 | js_InitParseContext(JSContext *cx, JSParseContext *pc, JSPrincipals *principals, |
162 | JSStackFrame *callerFrame, |
163 | const jschar *base, size_t length, |
164 | FILE *fp, const char *filename, uintN lineno) |
165 | { |
166 | JS_ASSERT_IF(callerFrame, callerFrame->script); |
167 | |
168 | pc->tempPoolMark = JS_ARENA_MARK(&cx->tempPool); |
169 | if (!js_InitTokenStream(cx, TS(pc), base, length, fp, filename, lineno)) { |
170 | JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark); |
171 | return JS_FALSE; |
172 | } |
173 | if (principals) |
174 | JSPRINCIPALS_HOLD(cx, principals); |
175 | pc->principals = principals; |
176 | pc->callerFrame = callerFrame; |
177 | pc->nodeList = NULL; |
178 | pc->traceListHead = NULL; |
179 | |
180 | /* Root atoms and objects allocated for the parsed tree. */ |
181 | JS_KEEP_ATOMS(cx->runtime); |
182 | JS_PUSH_TEMP_ROOT_PARSE_CONTEXT(cx, pc, &pc->tempRoot); |
183 | return JS_TRUE; |
184 | } |
185 | |
186 | void |
187 | js_FinishParseContext(JSContext *cx, JSParseContext *pc) |
188 | { |
189 | if (pc->principals) |
190 | JSPRINCIPALS_DROP(cx, pc->principals); |
191 | JS_ASSERT(pc->tempRoot.u.parseContext == pc); |
192 | JS_POP_TEMP_ROOT(cx, &pc->tempRoot); |
193 | JS_UNKEEP_ATOMS(cx->runtime); |
194 | js_CloseTokenStream(cx, TS(pc)); |
195 | JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark); |
196 | } |
197 | |
198 | void |
199 | js_InitCompilePrincipals(JSContext *cx, JSParseContext *pc, |
200 | JSPrincipals *principals) |
201 | { |
202 | JS_ASSERT(!pc->principals); |
203 | if (principals) |
204 | JSPRINCIPALS_HOLD(cx, principals); |
205 | pc->principals = principals; |
206 | } |
207 | |
208 | JSParsedObjectBox * |
209 | js_NewParsedObjectBox(JSContext *cx, JSParseContext *pc, JSObject *obj) |
210 | { |
211 | JSParsedObjectBox *pob; |
212 | |
213 | /* |
214 | * We use JSContext.tempPool to allocate parsed objects and place them on |
215 | * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas |
216 | * containing the entries must be alive until we are done with scanning, |
217 | * parsing and code generation for the whole script or top-level function. |
218 | */ |
219 | JS_ASSERT(obj); |
220 | JS_ARENA_ALLOCATE_TYPE(pob, JSParsedObjectBox, &cx->tempPool); |
221 | if (!pob) { |
222 | js_ReportOutOfScriptQuota(cx); |
223 | return NULL; |
224 | } |
225 | pob->traceLink = pc->traceListHead; |
226 | pob->emitLink = NULL; |
227 | pob->object = obj; |
228 | pc->traceListHead = pob; |
229 | return pob; |
230 | } |
231 | |
232 | |
233 | void |
234 | js_TraceParseContext(JSTracer *trc, JSParseContext *pc) |
235 | { |
236 | JSParsedObjectBox *pob; |
237 | |
238 | JS_ASSERT(pc->tempRoot.u.parseContext == pc); |
239 | pob = pc->traceListHead; |
240 | while (pob) { |
241 | JS_CALL_OBJECT_TRACER(trc, pob->object, "parser.object"); |
242 | pob = pob->traceLink; |
243 | } |
244 | } |
245 | |
246 | static JSParseNode * |
247 | RecycleTree(JSParseNode *pn, JSTreeContext *tc) |
248 | { |
249 | JSParseNode *next; |
250 | |
251 | if (!pn) |
252 | return NULL; |
253 | |
254 | /* Catch back-to-back dup recycles. */ |
255 | JS_ASSERT(pn != tc->parseContext->nodeList); |
256 | next = pn->pn_next; |
257 | pn->pn_next = tc->parseContext->nodeList; |
258 | tc->parseContext->nodeList = pn; |
259 | #ifdef METER_PARSENODES |
260 | recyclednodes++; |
261 | #endif |
262 | return next; |
263 | } |
264 | |
265 | static JSParseNode * |
266 | NewOrRecycledNode(JSContext *cx, JSTreeContext *tc) |
267 | { |
268 | JSParseNode *pn; |
269 | |
270 | pn = tc->parseContext->nodeList; |
271 | if (!pn) { |
272 | JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool); |
273 | if (!pn) |
274 | js_ReportOutOfScriptQuota(cx); |
275 | } else { |
276 | tc->parseContext->nodeList = pn->pn_next; |
277 | |
278 | /* Recycle immediate descendents only, to save work and working set. */ |
279 | switch (pn->pn_arity) { |
280 | case PN_FUNC: |
281 | RecycleTree(pn->pn_body, tc); |
282 | break; |
283 | case PN_LIST: |
284 | if (pn->pn_head) { |
285 | /* XXX check for dup recycles in the list */ |
286 | *pn->pn_tail = tc->parseContext->nodeList; |
287 | tc->parseContext->nodeList = pn->pn_head; |
288 | #ifdef METER_PARSENODES |
289 | recyclednodes += pn->pn_count; |
290 | #endif |
291 | } |
292 | break; |
293 | case PN_TERNARY: |
294 | RecycleTree(pn->pn_kid1, tc); |
295 | RecycleTree(pn->pn_kid2, tc); |
296 | RecycleTree(pn->pn_kid3, tc); |
297 | break; |
298 | case PN_BINARY: |
299 | if (pn->pn_left != pn->pn_right) |
300 | RecycleTree(pn->pn_left, tc); |
301 | RecycleTree(pn->pn_right, tc); |
302 | break; |
303 | case PN_UNARY: |
304 | RecycleTree(pn->pn_kid, tc); |
305 | break; |
306 | case PN_NAME: |
307 | RecycleTree(pn->pn_expr, tc); |
308 | break; |
309 | case PN_NULLARY: |
310 | break; |
311 | } |
312 | } |
313 | if (pn) { |
314 | #ifdef METER_PARSENODES |
315 | parsenodes++; |
316 | if (parsenodes - recyclednodes > maxparsenodes) |
317 | maxparsenodes = parsenodes - recyclednodes; |
318 | #endif |
319 | memset(&pn->pn_u, 0, sizeof pn->pn_u); |
320 | pn->pn_next = NULL; |
321 | } |
322 | return pn; |
323 | } |
324 | |
325 | /* |
326 | * Allocate a JSParseNode from cx's temporary arena. |
327 | */ |
328 | static JSParseNode * |
329 | NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity, |
330 | JSTreeContext *tc) |
331 | { |
332 | JSParseNode *pn; |
333 | JSToken *tp; |
334 | |
335 | pn = NewOrRecycledNode(cx, tc); |
336 | if (!pn) |
337 | return NULL; |
338 | tp = &CURRENT_TOKEN(ts); |
339 | pn->pn_type = tp->type; |
340 | pn->pn_pos = tp->pos; |
341 | pn->pn_op = JSOP_NOP; |
342 | pn->pn_arity = arity; |
343 | return pn; |
344 | } |
345 | |
346 | static JSParseNode * |
347 | NewBinary(JSContext *cx, JSTokenType tt, |
348 | JSOp op, JSParseNode *left, JSParseNode *right, |
349 | JSTreeContext *tc) |
350 | { |
351 | JSParseNode *pn, *pn1, *pn2; |
352 | |
353 | if (!left || !right) |
354 | return NULL; |
355 | |
356 | /* |
357 | * Flatten a left-associative (left-heavy) tree of a given operator into |
358 | * a list, to reduce js_FoldConstants and js_EmitTree recursion. |
359 | */ |
360 | if (left->pn_type == tt && |
361 | left->pn_op == op && |
362 | (js_CodeSpec[op].format & JOF_LEFTASSOC)) { |
363 | if (left->pn_arity != PN_LIST) { |
364 | pn1 = left->pn_left, pn2 = left->pn_right; |
365 | left->pn_arity = PN_LIST; |
366 | PN_INIT_LIST_1(left, pn1); |
367 | PN_APPEND(left, pn2); |
368 | if (tt == TOK_PLUS) { |
369 | if (pn1->pn_type == TOK_STRING) |
370 | left->pn_extra |= PNX_STRCAT; |
371 | else if (pn1->pn_type != TOK_NUMBER) |
372 | left->pn_extra |= PNX_CANTFOLD; |
373 | if (pn2->pn_type == TOK_STRING) |
374 | left->pn_extra |= PNX_STRCAT; |
375 | else if (pn2->pn_type != TOK_NUMBER) |
376 | left->pn_extra |= PNX_CANTFOLD; |
377 | } |
378 | } |
379 | PN_APPEND(left, right); |
380 | left->pn_pos.end = right->pn_pos.end; |
381 | if (tt == TOK_PLUS) { |
382 | if (right->pn_type == TOK_STRING) |
383 | left->pn_extra |= PNX_STRCAT; |
384 | else if (right->pn_type != TOK_NUMBER) |
385 | left->pn_extra |= PNX_CANTFOLD; |
386 | } |
387 | return left; |
388 | } |
389 | |
390 | /* |
391 | * Fold constant addition immediately, to conserve node space and, what's |
392 | * more, so js_FoldConstants never sees mixed addition and concatenation |
393 | * operations with more than one leading non-string operand in a PN_LIST |
394 | * generated for expressions such as 1 + 2 + "pt" (which should evaluate |
395 | * to "3pt", not "12pt"). |
396 | */ |
397 | if (tt == TOK_PLUS && |
398 | left->pn_type == TOK_NUMBER && |
399 | right->pn_type == TOK_NUMBER) { |
400 | left->pn_dval += right->pn_dval; |
401 | left->pn_pos.end = right->pn_pos.end; |
402 | RecycleTree(right, tc); |
403 | return left; |
404 | } |
405 | |
406 | pn = NewOrRecycledNode(cx, tc); |
407 | if (!pn) |
408 | return NULL; |
409 | pn->pn_type = tt; |
410 | pn->pn_pos.begin = left->pn_pos.begin; |
411 | pn->pn_pos.end = right->pn_pos.end; |
412 | pn->pn_op = op; |
413 | pn->pn_arity = PN_BINARY; |
414 | pn->pn_left = left; |
415 | pn->pn_right = right; |
416 | return pn; |
417 | } |
418 | |
419 | #if JS_HAS_GETTER_SETTER |
420 | static JSTokenType |
421 | CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt) |
422 | { |
423 | JSAtom *atom; |
424 | JSRuntime *rt; |
425 | JSOp op; |
426 | const char *name; |
427 | |
428 | JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME); |
429 | atom = CURRENT_TOKEN(ts).t_atom; |
430 | rt = cx->runtime; |
431 | if (atom == rt->atomState.getterAtom) |
432 | op = JSOP_GETTER; |
433 | else if (atom == rt->atomState.setterAtom) |
434 | op = JSOP_SETTER; |
435 | else |
436 | return TOK_NAME; |
437 | if (js_PeekTokenSameLine(cx, ts) != tt) |
438 | return TOK_NAME; |
439 | (void) js_GetToken(cx, ts); |
440 | if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) { |
441 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
442 | JSMSG_BAD_GETTER_OR_SETTER, |
443 | (op == JSOP_GETTER) |
444 | ? js_getter_str |
445 | : js_setter_str); |
446 | return TOK_ERROR; |
447 | } |
448 | CURRENT_TOKEN(ts).t_op = op; |
449 | if (JS_HAS_STRICT_OPTION(cx)) { |
450 | name = js_AtomToPrintableString(cx, atom); |
451 | if (!name || |
452 | !js_ReportCompileErrorNumber(cx, ts, NULL, |
453 | JSREPORT_WARNING | JSREPORT_STRICT, |
454 | JSMSG_DEPRECATED_USAGE, |
455 | name)) { |
456 | return TOK_ERROR; |
457 | } |
458 | } |
459 | return tt; |
460 | } |
461 | #endif |
462 | |
463 | /* |
464 | * Parse a top-level JS script. |
465 | */ |
466 | JSParseNode * |
467 | js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc) |
468 | { |
469 | JSTreeContext tc; |
470 | JSParseNode *pn; |
471 | |
472 | /* |
473 | * Protect atoms from being collected by a GC activation, which might |
474 | * - nest on this thread due to out of memory (the so-called "last ditch" |
475 | * GC attempted within js_NewGCThing), or |
476 | * - run for any reason on another thread if this thread is suspended on |
477 | * an object lock before it finishes generating bytecode into a script |
478 | * protected from the GC by a root or a stack frame reference. |
479 | */ |
480 | TREE_CONTEXT_INIT(&tc, pc); |
481 | tc.u.scopeChain = chain; |
482 | pn = Statements(cx, TS(pc), &tc); |
483 | if (pn) { |
484 | if (!js_MatchToken(cx, TS(pc), TOK_EOF)) { |
485 | js_ReportCompileErrorNumber(cx, TS(pc), NULL, JSREPORT_ERROR, |
486 | JSMSG_SYNTAX_ERROR); |
487 | pn = NULL; |
488 | } else { |
489 | pn->pn_type = TOK_LC; |
490 | if (!js_FoldConstants(cx, pn, &tc)) |
491 | pn = NULL; |
492 | } |
493 | } |
494 | |
495 | TREE_CONTEXT_FINISH(cx, &tc); |
496 | return pn; |
497 | } |
498 | |
499 | /* |
500 | * Compile a top-level script. |
501 | */ |
502 | extern JSScript * |
503 | js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame, |
504 | JSPrincipals *principals, uint32 tcflags, |
505 | const jschar *chars, size_t length, |
506 | FILE *file, const char *filename, uintN lineno) |
507 | { |
508 | JSParseContext pc; |
509 | JSArenaPool codePool, notePool; |
510 | JSCodeGenerator cg; |
511 | JSTokenType tt; |
512 | JSParseNode *pn; |
513 | uint32 scriptGlobals; |
514 | JSScript *script; |
515 | #ifdef METER_PARSENODES |
516 | void *sbrk(ptrdiff_t), *before = sbrk(0); |
517 | #endif |
518 | |
519 | JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | |
520 | TCF_STATIC_DEPTH_MASK))); |
521 | |
522 | /* |
523 | * The scripted callerFrame can only be given for compile-and-go scripts |
524 | * and non-zero static depth requires callerFrame. |
525 | */ |
526 | JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO); |
527 | JS_ASSERT_IF(TCF_GET_STATIC_DEPTH(tcflags) != 0, callerFrame); |
528 | |
529 | if (!js_InitParseContext(cx, &pc, principals, callerFrame, chars, length, |
530 | file, filename, lineno)) { |
531 | return NULL; |
532 | } |
533 | |
534 | JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode), |
535 | &cx->scriptStackQuota); |
536 | JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote), |
537 | &cx->scriptStackQuota); |
538 | js_InitCodeGenerator(cx, &cg, &pc, &codePool, ¬ePool, |
539 | pc.tokenStream.lineno); |
540 | |
541 | MUST_FLOW_THROUGH("out"); |
542 | cg.treeContext.flags |= (uint16) tcflags; |
543 | cg.treeContext.u.scopeChain = scopeChain; |
544 | cg.staticDepth = TCF_GET_STATIC_DEPTH(tcflags); |
545 | |
546 | if ((tcflags & TCF_COMPILE_N_GO) && callerFrame && callerFrame->fun) { |
547 | /* |
548 | * An eval script in a caller frame needs to have its enclosing function |
549 | * captured in case it uses an upvar reference, and someone wishes to |
550 | * decompile it while running. |
551 | */ |
552 | JSParsedObjectBox *pob = js_NewParsedObjectBox(cx, &pc, callerFrame->callee); |
553 | pob->emitLink = cg.objectList.lastPob; |
554 | cg.objectList.lastPob = pob; |
555 | cg.objectList.length++; |
556 | } |
557 | |
558 | /* Inline Statements() to emit as we go to save space. */ |
559 | for (;;) { |
560 | pc.tokenStream.flags |= TSF_OPERAND; |
561 | tt = js_PeekToken(cx, &pc.tokenStream); |
562 | pc.tokenStream.flags &= ~TSF_OPERAND; |
563 | if (tt <= TOK_EOF) { |
564 | if (tt == TOK_EOF) |
565 | break; |
566 | JS_ASSERT(tt == TOK_ERROR); |
567 | script = NULL; |
568 | goto out; |
569 | } |
570 | |
571 | pn = Statement(cx, &pc.tokenStream, &cg.treeContext); |
572 | if (!pn) { |
573 | script = NULL; |
574 | goto out; |
575 | } |
576 | JS_ASSERT(!cg.treeContext.blockNode); |
577 | |
578 | if (!js_FoldConstants(cx, pn, &cg.treeContext) || |
579 | !js_EmitTree(cx, &cg, pn)) { |
580 | script = NULL; |
581 | goto out; |
582 | } |
583 | RecycleTree(pn, &cg.treeContext); |
584 | } |
585 | |
586 | /* |
587 | * Global variables and regexps shares the index space with locals. Due to |
588 | * incremental code generation we need to patch the bytecode to adjust the |
589 | * local references to skip the globals. |
590 | */ |
591 | scriptGlobals = cg.treeContext.ngvars + cg.regexpList.length; |
592 | if (scriptGlobals != 0) { |
593 | jsbytecode *code, *end; |
594 | JSOp op; |
595 | const JSCodeSpec *cs; |
596 | uintN len, slot; |
597 | |
598 | if (scriptGlobals >= SLOTNO_LIMIT) |
599 | goto too_many_slots; |
600 | code = CG_BASE(&cg); |
601 | for (end = code + CG_OFFSET(&cg); code != end; code += len) { |
602 | JS_ASSERT(code < end); |
603 | op = (JSOp) *code; |
604 | cs = &js_CodeSpec[op]; |
605 | len = (cs->length > 0) |
606 | ? (uintN) cs->length |
607 | : js_GetVariableBytecodeLength(code); |
608 | if (JOF_TYPE(cs->format) == JOF_LOCAL || |
609 | (JOF_TYPE(cs->format) == JOF_SLOTATOM)) { |
610 | /* |
611 | * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be |
612 | * emitted only for a function. |
613 | */ |
614 | JS_ASSERT((JOF_TYPE(cs->format) == JOF_SLOTATOM) == |
615 | (op == JSOP_GETLOCALPROP)); |
616 | slot = GET_SLOTNO(code); |
617 | slot += scriptGlobals; |
618 | if (slot >= SLOTNO_LIMIT) |
619 | goto too_many_slots; |
620 | SET_SLOTNO(code, slot); |
621 | } |
622 | } |
623 | } |
624 | |
625 | #ifdef METER_PARSENODES |
626 | printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n", |
627 | (char *)sbrk(0) - (char *)before, |
628 | parsenodes, |
629 | maxparsenodes, |
630 | parsenodes - recyclednodes); |
631 | before = sbrk(0); |
632 | #endif |
633 | |
634 | /* |
635 | * Nowadays the threaded interpreter needs a stop instruction, so we |
636 | * do have to emit that here. |
637 | */ |
638 | if (js_Emit1(cx, &cg, JSOP_STOP) < 0) { |
639 | script = NULL; |
640 | goto out; |
641 | } |
642 | #ifdef METER_PARSENODES |
643 | printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n", |
644 | (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount); |
645 | #endif |
646 | #ifdef JS_ARENAMETER |
647 | JS_DumpArenaStats(stdout); |
648 | #endif |
649 | script = js_NewScriptFromCG(cx, &cg); |
650 | |
651 | #ifdef JS_SCOPE_DEPTH_METER |
652 | if (script) { |
653 | JSObject *obj = scopeChain; |
654 | uintN depth = 1; |
655 | while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) |
656 | ++depth; |
657 | JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth); |
658 | } |
659 | #endif |
660 | |
661 | out: |
662 | js_FinishCodeGenerator(cx, &cg); |
663 | JS_FinishArenaPool(&codePool); |
664 | JS_FinishArenaPool(¬ePool); |
665 | js_FinishParseContext(cx, &pc); |
666 | return script; |
667 | |
668 | too_many_slots: |
669 | js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL, |
670 | JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS); |
671 | script = NULL; |
672 | goto out; |
673 | } |
674 | |
675 | /* |
676 | * Insist on a final return before control flows out of pn. Try to be a bit |
677 | * smart about loops: do {...; return e2;} while(0) at the end of a function |
678 | * that contains an early return e1 will get a strict warning. Similarly for |
679 | * iloops: while (true){...} is treated as though ... returns. |
680 | */ |
681 | #define ENDS_IN_OTHER 0 |
682 | #define ENDS_IN_RETURN 1 |
683 | #define ENDS_IN_BREAK 2 |
684 | |
685 | static int |
686 | HasFinalReturn(JSParseNode *pn) |
687 | { |
688 | JSParseNode *pn2, *pn3; |
689 | uintN rv, rv2, hasDefault; |
690 | |
691 | switch (pn->pn_type) { |
692 | case TOK_LC: |
693 | if (!pn->pn_head) |
694 | return ENDS_IN_OTHER; |
695 | return HasFinalReturn(PN_LAST(pn)); |
696 | |
697 | case TOK_IF: |
698 | if (!pn->pn_kid3) |
699 | return ENDS_IN_OTHER; |
700 | return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3); |
701 | |
702 | case TOK_WHILE: |
703 | pn2 = pn->pn_left; |
704 | if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE) |
705 | return ENDS_IN_RETURN; |
706 | if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval) |
707 | return ENDS_IN_RETURN; |
708 | return ENDS_IN_OTHER; |
709 | |
710 | case TOK_DO: |
711 | pn2 = pn->pn_right; |
712 | if (pn2->pn_type == TOK_PRIMARY) { |
713 | if (pn2->pn_op == JSOP_FALSE) |
714 | return HasFinalReturn(pn->pn_left); |
715 | if (pn2->pn_op == JSOP_TRUE) |
716 | return ENDS_IN_RETURN; |
717 | } |
718 | if (pn2->pn_type == TOK_NUMBER) { |
719 | if (pn2->pn_dval == 0) |
720 | return HasFinalReturn(pn->pn_left); |
721 | return ENDS_IN_RETURN; |
722 | } |
723 | return ENDS_IN_OTHER; |
724 | |
725 | case TOK_FOR: |
726 | pn2 = pn->pn_left; |
727 | if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2) |
728 | return ENDS_IN_RETURN; |
729 | return ENDS_IN_OTHER; |
730 | |
731 | case TOK_SWITCH: |
732 | rv = ENDS_IN_RETURN; |
733 | hasDefault = ENDS_IN_OTHER; |
734 | pn2 = pn->pn_right; |
735 | if (pn2->pn_type == TOK_LEXICALSCOPE) |
736 | pn2 = pn2->pn_expr; |
737 | for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) { |
738 | if (pn2->pn_type == TOK_DEFAULT) |
739 | hasDefault = ENDS_IN_RETURN; |
740 | pn3 = pn2->pn_right; |
741 | JS_ASSERT(pn3->pn_type == TOK_LC); |
742 | if (pn3->pn_head) { |
743 | rv2 = HasFinalReturn(PN_LAST(pn3)); |
744 | if (rv2 == ENDS_IN_OTHER && pn2->pn_next) |
745 | /* Falling through to next case or default. */; |
746 | else |
747 | rv &= rv2; |
748 | } |
749 | } |
750 | /* If a final switch has no default case, we judge it harshly. */ |
751 | rv &= hasDefault; |
752 | return rv; |
753 | |
754 | case TOK_BREAK: |
755 | return ENDS_IN_BREAK; |
756 | |
757 | case TOK_WITH: |
758 | return HasFinalReturn(pn->pn_right); |
759 | |
760 | case TOK_RETURN: |
761 | return ENDS_IN_RETURN; |
762 | |
763 | case TOK_COLON: |
764 | case TOK_LEXICALSCOPE: |
765 | return HasFinalReturn(pn->pn_expr); |
766 | |
767 | case TOK_THROW: |
768 | return ENDS_IN_RETURN; |
769 | |
770 | case TOK_TRY: |
771 | /* If we have a finally block that returns, we are done. */ |
772 | if (pn->pn_kid3) { |
773 | rv = HasFinalReturn(pn->pn_kid3); |
774 | if (rv == ENDS_IN_RETURN) |
775 | return rv; |
776 | } |
777 | |
778 | /* Else check the try block and any and all catch statements. */ |
779 | rv = HasFinalReturn(pn->pn_kid1); |
780 | if (pn->pn_kid2) { |
781 | JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST); |
782 | for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next) |
783 | rv &= HasFinalReturn(pn2); |
784 | } |
785 | return rv; |
786 | |
787 | case TOK_CATCH: |
788 | /* Check this catch block's body. */ |
789 | return HasFinalReturn(pn->pn_kid3); |
790 | |
791 | case TOK_LET: |
792 | /* Non-binary let statements are let declarations. */ |
793 | if (pn->pn_arity != PN_BINARY) |
794 | return ENDS_IN_OTHER; |
795 | return HasFinalReturn(pn->pn_right); |
796 | |
797 | default: |
798 | return ENDS_IN_OTHER; |
799 | } |
800 | } |
801 | |
802 | static JSBool |
803 | ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum, |
804 | uintN anonerrnum) |
805 | { |
806 | const char *name; |
807 | |
808 | JS_ASSERT(tc->flags & TCF_IN_FUNCTION); |
809 | if (tc->u.fun->atom) { |
810 | name = js_AtomToPrintableString(cx, tc->u.fun->atom); |
811 | } else { |
812 | errnum = anonerrnum; |
813 | name = NULL; |
814 | } |
815 | return js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, flags, |
816 | errnum, name); |
817 | } |
818 | |
819 | static JSBool |
820 | CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn) |
821 | { |
822 | JS_ASSERT(tc->flags & TCF_IN_FUNCTION); |
823 | return HasFinalReturn(pn) == ENDS_IN_RETURN || |
824 | ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT, |
825 | JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE); |
826 | } |
827 | |
828 | static JSParseNode * |
829 | FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) |
830 | { |
831 | JSStmtInfo stmtInfo; |
832 | uintN oldflags, firstLine; |
833 | JSParseNode *pn; |
834 | |
835 | JS_ASSERT(tc->flags & TCF_IN_FUNCTION); |
836 | js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); |
837 | stmtInfo.flags = SIF_BODY_BLOCK; |
838 | |
839 | oldflags = tc->flags; |
840 | tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID); |
841 | |
842 | /* |
843 | * Save the body's first line, and store it in pn->pn_pos.begin.lineno |
844 | * later, because we may have not peeked in ts yet, so Statements won't |
845 | * acquire a valid pn->pn_pos.begin from the current token. |
846 | */ |
847 | firstLine = ts->lineno; |
848 | #if JS_HAS_EXPR_CLOSURES |
849 | if (CURRENT_TOKEN(ts).type == TOK_LC) { |
850 | pn = Statements(cx, ts, tc); |
851 | } else { |
852 | pn = NewParseNode(cx, ts, PN_UNARY, tc); |
853 | if (pn) { |
854 | pn->pn_kid = AssignExpr(cx, ts, tc); |
855 | if (!pn->pn_kid) { |
856 | pn = NULL; |
857 | } else { |
858 | if (tc->flags & TCF_FUN_IS_GENERATOR) { |
859 | ReportBadReturn(cx, tc, JSREPORT_ERROR, |
860 | JSMSG_BAD_GENERATOR_RETURN, |
861 | JSMSG_BAD_ANON_GENERATOR_RETURN); |
862 | pn = NULL; |
863 | } else { |
864 | pn->pn_type = TOK_RETURN; |
865 | pn->pn_op = JSOP_RETURN; |
866 | pn->pn_pos.end = pn->pn_kid->pn_pos.end; |
867 | } |
868 | } |
869 | } |
870 | } |
871 | #else |
872 | pn = Statements(cx, ts, tc); |
873 | #endif |
874 | |
875 | if (pn) { |
876 | js_PopStatement(tc); |
877 | pn->pn_pos.begin.lineno = firstLine; |
878 | |
879 | /* Check for falling off the end of a function that returns a value. */ |
880 | if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) && |
881 | !CheckFinalReturn(cx, tc, pn)) { |
882 | pn = NULL; |
883 | } |
884 | } |
885 | |
886 | tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS)); |
887 | return pn; |
888 | } |
889 | |
890 | /* |
891 | * Compile a JS function body, which might appear as the value of an event |
892 | * handler attribute in an HTML <INPUT> tag. |
893 | */ |
894 | JSBool |
895 | js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals, |
896 | const jschar *chars, size_t length, |
897 | const char *filename, uintN lineno) |
898 | { |
899 | JSParseContext pc; |
900 | JSArenaPool codePool, notePool; |
901 | JSCodeGenerator funcg; |
902 | JSParseNode *pn; |
903 | |
904 | if (!js_InitParseContext(cx, &pc, principals, NULL, chars, length, NULL, |
905 | filename, lineno)) { |
906 | return JS_FALSE; |
907 | } |
908 | |
909 | /* No early return from this point until js_FinishParseContext call. */ |
910 | JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode), |
911 | &cx->scriptStackQuota); |
912 | JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote), |
913 | &cx->scriptStackQuota); |
914 | js_InitCodeGenerator(cx, &funcg, &pc, &codePool, ¬ePool, |
915 | pc.tokenStream.lineno); |
916 | funcg.treeContext.flags |= TCF_IN_FUNCTION; |
917 | funcg.treeContext.u.fun = fun; |
918 | |
919 | /* |
920 | * Farble the body so that it looks like a block statement to js_EmitTree, |
921 | * which is called beneath FunctionBody; see Statements, further below in |
922 | * this file. FunctionBody pushes a STMT_BLOCK record around its call to |
923 | * Statements, so Statements will not compile each statement as it loops |
924 | * to save JSParseNode space -- it will not compile at all, only build a |
925 | * JSParseNode tree. |
926 | * |
927 | * Therefore we must fold constants, allocate try notes, and generate code |
928 | * for this function, including a stop opcode at the end. |
929 | */ |
930 | CURRENT_TOKEN(&pc.tokenStream).type = TOK_LC; |
931 | pn = FunctionBody(cx, &pc.tokenStream, &funcg.treeContext); |
932 | if (pn) { |
933 | if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) { |
934 | js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL, |
935 | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); |
936 | pn = NULL; |
937 | } else { |
938 | if (!js_FoldConstants(cx, pn, &funcg.treeContext) || |
939 | !js_EmitFunctionScript(cx, &funcg, pn)) { |
940 | pn = NULL; |
941 | } |
942 | } |
943 | } |
944 | |
945 | /* Restore saved state and release code generation arenas. */ |
946 | js_FinishCodeGenerator(cx, &funcg); |
947 | JS_FinishArenaPool(&codePool); |
948 | JS_FinishArenaPool(¬ePool); |
949 | js_FinishParseContext(cx, &pc); |
950 | return pn != NULL; |
951 | } |
952 | |
953 | /* |
954 | * Parameter block types for the several Binder functions. We use a common |
955 | * helper function signature in order to share code among destructuring and |
956 | * simple variable declaration parsers. In the destructuring case, the binder |
957 | * function is called indirectly from the variable declaration parser by way |
958 | * of CheckDestructuring and its friends. |
959 | */ |
960 | typedef struct BindData BindData; |
961 | |
962 | typedef JSBool |
963 | (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc); |
964 | |
965 | struct BindData { |
966 | JSParseNode *pn; /* error source coordinate */ |
967 | JSOp op; /* prolog bytecode or nop */ |
968 | Binder binder; /* binder, discriminates u */ |
969 | union { |
970 | struct { |
971 | uintN overflow; |
972 | } let; |
973 | } u; |
974 | }; |
975 | |
976 | static JSBool |
977 | BindArg(JSContext *cx, JSAtom *atom, JSTreeContext *tc) |
978 | { |
979 | const char *name; |
980 | |
981 | /* |
982 | * Check for a duplicate parameter name, a "feature" required by ECMA-262. |
983 | */ |
984 | JS_ASSERT(tc->flags & TCF_IN_FUNCTION); |
985 | if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) { |
986 | name = js_AtomToPrintableString(cx, atom); |
987 | if (!name || |
988 | !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, |
989 | JSREPORT_WARNING | JSREPORT_STRICT, |
990 | JSMSG_DUPLICATE_FORMAL, |
991 | name)) { |
992 | return JS_FALSE; |
993 | } |
994 | } |
995 | |
996 | return js_AddLocal(cx, tc->u.fun, atom, JSLOCAL_ARG); |
997 | } |
998 | |
999 | static JSBool |
1000 | BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom, |
1001 | JSLocalKind localKind) |
1002 | { |
1003 | JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); |
1004 | |
1005 | /* |
1006 | * Don't bind a variable with the hidden name 'arguments', per ECMA-262. |
1007 | * Instead 'var arguments' always restates the predefined property of the |
1008 | * activation objects with unhidden name 'arguments'. Assignment to such |
1009 | * a variable must be handled specially. |
1010 | */ |
1011 | if (atom == cx->runtime->atomState.argumentsAtom) |
1012 | return JS_TRUE; |
1013 | |
1014 | return js_AddLocal(cx, fun, atom, localKind); |
1015 | } |
1016 | |
1017 | #if JS_HAS_DESTRUCTURING |
1018 | /* |
1019 | * Forward declaration to maintain top-down presentation. |
1020 | */ |
1021 | static JSParseNode * |
1022 | DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, |
1023 | JSTokenType tt); |
1024 | |
1025 | static JSBool |
1026 | BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, |
1027 | JSTreeContext *tc) |
1028 | { |
1029 | JSAtomListElement *ale; |
1030 | const char *name; |
1031 | |
1032 | JS_ASSERT(tc->flags & TCF_IN_FUNCTION); |
1033 | ATOM_LIST_SEARCH(ale, &tc->decls, atom); |
1034 | if (!ale) { |
1035 | ale = js_IndexAtom(cx, atom, &tc->decls); |
1036 | if (!ale) |
1037 | return JS_FALSE; |
1038 | ALE_SET_JSOP(ale, data->op); |
1039 | } |
1040 | |
1041 | if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) { |
1042 | name = js_AtomToPrintableString(cx, atom); |
1043 | if (!name || |
1044 | !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, |
1045 | JSREPORT_WARNING | JSREPORT_STRICT, |
1046 | JSMSG_DUPLICATE_FORMAL, |
1047 | name)) { |
1048 | return JS_FALSE; |
1049 | } |
1050 | } else { |
1051 | if (!BindLocalVariable(cx, tc->u.fun, atom, JSLOCAL_VAR)) |
1052 | return JS_FALSE; |
1053 | } |
1054 | return JS_TRUE; |
1055 | } |
1056 | #endif /* JS_HAS_DESTRUCTURING */ |
1057 | |
1058 | static JSFunction * |
1059 | NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom, |
1060 | uintN lambda) |
1061 | { |
1062 | JSObject *parent; |
1063 | JSFunction *fun; |
1064 | |
1065 | JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0); |
1066 | parent = (tc->flags & TCF_IN_FUNCTION) |
1067 | ? FUN_OBJECT(tc->u.fun) |
1068 | : tc->u.scopeChain; |
1069 | fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED | lambda, |
1070 | parent, atom); |
1071 | if (fun && !(tc->flags & TCF_COMPILE_N_GO)) { |
1072 | STOBJ_CLEAR_PARENT(FUN_OBJECT(fun)); |
1073 | STOBJ_CLEAR_PROTO(FUN_OBJECT(fun)); |
1074 | } |
1075 | return fun; |
1076 | } |
1077 | |
1078 | static JSParseNode * |
1079 | FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, |
1080 | uintN lambda) |
1081 | { |
1082 | JSOp op, prevop; |
1083 | JSParseNode *pn, *body, *result; |
1084 | JSTokenType tt; |
1085 | JSAtom *funAtom; |
1086 | JSParsedObjectBox *funpob; |
1087 | JSAtomListElement *ale; |
1088 | JSFunction *fun; |
1089 | JSTreeContext funtc; |
1090 | #if JS_HAS_DESTRUCTURING |
1091 | JSParseNode *item, *list = NULL; |
1092 | #endif |
1093 | |
1094 | /* Make a TOK_FUNCTION node. */ |
1095 | #if JS_HAS_GETTER_SETTER |
1096 | op = CURRENT_TOKEN(ts).t_op; |
1097 | #endif |
1098 | pn = NewParseNode(cx, ts, PN_FUNC, tc); |
1099 | if (!pn) |
1100 | return NULL; |
1101 | #ifdef DEBUG |
1102 | pn->pn_index = (uint32) -1; |
1103 | #endif |
1104 | |
1105 | /* Scan the optional function name into funAtom. */ |
1106 | ts->flags |= TSF_KEYWORD_IS_NAME; |
1107 | tt = js_GetToken(cx, ts); |
1108 | ts->flags &= ~TSF_KEYWORD_IS_NAME; |
1109 | if (tt == TOK_NAME) { |
1110 | funAtom = CURRENT_TOKEN(ts).t_atom; |
1111 | } else { |
1112 | if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) { |
1113 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
1114 | JSMSG_SYNTAX_ERROR); |
1115 | return NULL; |
1116 | } |
1117 | funAtom = NULL; |
1118 | js_UngetToken(ts); |
1119 | } |
1120 | |
1121 | /* |
1122 | * Record names for function statements in tc->decls so we know when to |
1123 | * avoid optimizing variable references that might name a function. |
1124 | */ |
1125 | if (lambda == 0 && funAtom) { |
1126 | ATOM_LIST_SEARCH(ale, &tc->decls, funAtom); |
1127 | if (ale) { |
1128 | prevop = ALE_JSOP(ale); |
1129 | if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) { |
1130 | const char *name = js_AtomToPrintableString(cx, funAtom); |
1131 | if (!name || |
1132 | !js_ReportCompileErrorNumber(cx, ts, NULL, |
1133 | (prevop != JSOP_DEFCONST) |
1134 | ? JSREPORT_WARNING | |
1135 | JSREPORT_STRICT |
1136 | : JSREPORT_ERROR, |
1137 | JSMSG_REDECLARED_VAR, |
1138 | (prevop == JSOP_DEFFUN) |
1139 | ? js_function_str |
1140 | : (prevop == JSOP_DEFCONST) |
1141 | ? js_const_str |
1142 | : js_var_str, |
1143 | name)) { |
1144 | return NULL; |
1145 | } |
1146 | } |
1147 | if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR) |
1148 | tc->flags |= TCF_FUN_CLOSURE_VS_VAR; |
1149 | } else { |
1150 | ale = js_IndexAtom(cx, funAtom, &tc->decls); |
1151 | if (!ale) |
1152 | return NULL; |
1153 | } |
1154 | ALE_SET_JSOP(ale, JSOP_DEFFUN); |
1155 | |
1156 | /* |
1157 | * A function nested at top level inside another's body needs only a |
1158 | * local variable to bind its name to its value, and not an activation |
1159 | * object property (it might also need the activation property, if the |
1160 | * outer function contains with statements, e.g., but the stack slot |
1161 | * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a |
1162 | * JSOP_GETLOCAL bytecode). |
1163 | */ |
1164 | if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) { |
1165 | JSLocalKind localKind; |
1166 | |
1167 | /* |
1168 | * Define a property on the outer function so that BindNameToSlot |
1169 | * can properly optimize accesses. Note that we need a variable, |
1170 | * not an argument, for the function statement. Thus we add a |
1171 | * variable even if the parameter with the given name already |
1172 | * exists. |
1173 | */ |
1174 | localKind = js_LookupLocal(cx, tc->u.fun, funAtom, NULL); |
1175 | if (localKind == JSLOCAL_NONE || localKind == JSLOCAL_ARG) { |
1176 | if (!js_AddLocal(cx, tc->u.fun, funAtom, JSLOCAL_VAR)) |
1177 | return NULL; |
1178 | } |
1179 | } |
1180 | } |
1181 | |
1182 | fun = NewCompilerFunction(cx, tc, funAtom, lambda); |
1183 | if (!fun) |
1184 | return NULL; |
1185 | |
1186 | #if JS_HAS_GETTER_SETTER |
1187 | if (op != JSOP_NOP) |
1188 | fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER; |
1189 | #endif |
1190 | |
1191 | /* |
1192 | * Create wrapping box for fun->object early to protect against a |
1193 | * last-ditch GC. |
1194 | */ |
1195 | funpob = js_NewParsedObjectBox(cx, tc->parseContext, FUN_OBJECT(fun)); |
1196 | if (!funpob) |
1197 | return NULL; |
1198 | |
1199 | /* Initialize early for possible flags mutation via DestructuringExpr. */ |
1200 | TREE_CONTEXT_INIT(&funtc, tc->parseContext); |
1201 | funtc.flags |= TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO); |
1202 | funtc.u.fun = fun; |
1203 | |
1204 | /* Now parse formal argument list and compute fun->nargs. */ |
1205 | MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL); |
1206 | if (!js_MatchToken(cx, ts, TOK_RP)) { |
1207 | do { |
1208 | tt = js_GetToken(cx, ts); |
1209 | switch (tt) { |
1210 | #if JS_HAS_DESTRUCTURING |
1211 | case TOK_LB: |
1212 | case TOK_LC: |
1213 | { |
1214 | BindData data; |
1215 | JSParseNode *lhs, *rhs; |
1216 | jsint slot; |
1217 | |
1218 | /* |
1219 | * A destructuring formal parameter turns into one or more |
1220 | * local variables initialized from properties of a single |
1221 | * anonymous positional parameter, so here we must tweak our |
1222 | * binder and its data. |
1223 | */ |
1224 | data.pn = NULL; |
1225 | data.op = JSOP_DEFVAR; |
1226 | data.binder = BindDestructuringArg; |
1227 | lhs = DestructuringExpr(cx, &data, &funtc, tt); |
1228 | if (!lhs) |
1229 | return NULL; |
1230 | |
1231 | /* |
1232 | * Adjust fun->nargs to count the single anonymous positional |
1233 | * parameter that is to be destructured. |
1234 | */ |
1235 | slot = fun->nargs; |
1236 | if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG)) |
1237 | return NULL; |
1238 | |
1239 | /* |
1240 | * Synthesize a destructuring assignment from the single |
1241 | * anonymous positional parameter into the destructuring |
1242 | * left-hand-side expression and accumulate it in list. |
1243 | */ |
1244 | rhs = NewParseNode(cx, ts, PN_NAME, tc); |
1245 | if (!rhs) |
1246 | return NULL; |
1247 | rhs->pn_type = TOK_NAME; |
1248 | rhs->pn_op = JSOP_GETARG; |
1249 | rhs->pn_atom = cx->runtime->atomState.emptyAtom; |
1250 | rhs->pn_slot = slot; |
1251 | |
1252 | item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc); |
1253 | if (!item) |
1254 | return NULL; |
1255 | if (!list) { |
1256 | list = NewParseNode(cx, ts, PN_LIST, tc); |
1257 | if (!list) |
1258 | return NULL; |
1259 | list->pn_type = TOK_COMMA; |
1260 | PN_INIT_LIST(list); |
1261 | } |
1262 | PN_APPEND(list, item); |
1263 | break; |
1264 | } |
1265 | #endif /* JS_HAS_DESTRUCTURING */ |
1266 | |
1267 | case TOK_NAME: |
1268 | if (!BindArg(cx, CURRENT_TOKEN(ts).t_atom, &funtc)) |
1269 | return NULL; |
1270 | break; |
1271 | |
1272 | default: |
1273 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
1274 | JSMSG_MISSING_FORMAL); |
1275 | return NULL; |
1276 | } |
1277 | } while (js_MatchToken(cx, ts, TOK_COMMA)); |
1278 | |
1279 | MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL); |
1280 | } |
1281 | |
1282 | #if JS_HAS_EXPR_CLOSURES |
1283 | ts->flags |= TSF_OPERAND; |
1284 | tt = js_GetToken(cx, ts); |
1285 | ts->flags &= ~TSF_OPERAND; |
1286 | if (tt != TOK_LC) { |
1287 | js_UngetToken(ts); |
1288 | fun->flags |= JSFUN_EXPR_CLOSURE; |
1289 | } |
1290 | #else |
1291 | MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY); |
1292 | #endif |
1293 | pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin; |
1294 | |
1295 | body = FunctionBody(cx, ts, &funtc); |
1296 | if (!body) |
1297 | return NULL; |
1298 | |
1299 | #if JS_HAS_EXPR_CLOSURES |
1300 | if (tt == TOK_LC) |
1301 | MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY); |
1302 | else if (lambda == 0) |
1303 | js_MatchToken(cx, ts, TOK_SEMI); |
1304 | #else |
1305 | MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY); |
1306 | #endif |
1307 | pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; |
1308 | |
1309 | #if JS_HAS_DESTRUCTURING |
1310 | /* |
1311 | * If there were destructuring formal parameters, prepend the initializing |
1312 | * comma expression that we synthesized to body. If the body is a lexical |
1313 | * scope node, we must make a special TOK_SEQ node, to prepend the formal |
1314 | * parameter destructuring code without bracing the decompilation of the |
1315 | * function body's lexical scope. |
1316 | */ |
1317 | if (list) { |
1318 | if (body->pn_arity != PN_LIST) { |
1319 | JSParseNode *block; |
1320 | |
1321 | block = NewParseNode(cx, ts, PN_LIST, tc); |
1322 | if (!block) |
1323 | return NULL; |
1324 | block->pn_type = TOK_SEQ; |
1325 | block->pn_pos = body->pn_pos; |
1326 | PN_INIT_LIST_1(block, body); |
1327 | |
1328 | body = block; |
1329 | } |
1330 | |
1331 | item = NewParseNode(cx, ts, PN_UNARY, tc); |
1332 | if (!item) |
1333 | return NULL; |
1334 | |
1335 | item->pn_type = TOK_SEMI; |
1336 | item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin; |
1337 | item->pn_kid = list; |
1338 | item->pn_next = body->pn_head; |
1339 | body->pn_head = item; |
1340 | if (body->pn_tail == &body->pn_head) |
1341 | body->pn_tail = &item->pn_next; |
1342 | ++body->pn_count; |
1343 | } |
1344 | #endif |
1345 | |
1346 | /* |
1347 | * If we collected flags that indicate nested heavyweight functions, or |
1348 | * this function contains heavyweight-making statements (references to |
1349 | * __parent__ or __proto__; use of with, or eval; and assignment to |
1350 | * arguments), flag the function as heavyweight (requiring a call object |
1351 | * per invocation). |
1352 | */ |
1353 | if (funtc.flags & TCF_FUN_HEAVYWEIGHT) { |
1354 | fun->flags |= JSFUN_HEAVYWEIGHT; |
1355 | tc->flags |= TCF_FUN_HEAVYWEIGHT; |
1356 | } else { |
1357 | /* |
1358 | * If this function is a named statement function not at top-level |
1359 | * (i.e. not a top-level function definiton or expression), then |
1360 | * our enclosing function, if any, must be heavyweight. |
1361 | * |
1362 | * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator, |
1363 | * so it won't be set here. Assert that it's not. We have to check |
1364 | * it later, in js_EmitTree, after js_EmitFunctionScript has traversed |
1365 | * the function's body. |
1366 | */ |
1367 | JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS)); |
1368 | if (lambda == 0 && funAtom && !AT_TOP_LEVEL(tc)) |
1369 | tc->flags |= TCF_FUN_HEAVYWEIGHT; |
1370 | } |
1371 | |
1372 | result = pn; |
1373 | if (lambda != 0) { |
1374 | /* |
1375 | * ECMA ed. 3 standard: function expression, possibly anonymous. |
1376 | */ |
1377 | op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ; |
1378 | } else if (!funAtom) { |
1379 | /* |
1380 | * If this anonymous function definition is *not* embedded within a |
1381 | * larger expression, we treat it as an expression statement, not as |
1382 | * a function declaration -- and not as a syntax error (as ECMA-262 |
1383 | * Edition 3 would have it). Backward compatibility must trump all, |
1384 | * unless JSOPTION_ANONFUNFIX is set. |
1385 | */ |
1386 | result = NewParseNode(cx, ts, PN_UNARY, tc); |
1387 | if (!result) |
1388 | return NULL; |
1389 | result->pn_type = TOK_SEMI; |
1390 | result->pn_pos = pn->pn_pos; |
1391 | result->pn_kid = pn; |
1392 | op = JSOP_ANONFUNOBJ; |
1393 | } else if (!AT_TOP_LEVEL(tc)) { |
1394 | /* |
1395 | * ECMA ed. 3 extension: a function expression statement not at the |
1396 | * top level, e.g., in a compound statement such as the "then" part |
1397 | * of an "if" statement, binds a closure only if control reaches that |
1398 | * sub-statement. |
1399 | */ |
1400 | op = JSOP_DEFFUN; |
1401 | } else { |
1402 | op = JSOP_NOP; |
1403 | } |
1404 | |
1405 | pn->pn_funpob = funpob; |
1406 | pn->pn_op = op; |
1407 | pn->pn_body = body; |
1408 | pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS | TCF_COMPILE_N_GO); |
1409 | TREE_CONTEXT_FINISH(cx, &funtc); |
1410 | return result; |
1411 | } |
1412 | |
1413 | static JSParseNode * |
1414 | FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) |
1415 | { |
1416 | return FunctionDef(cx, ts, tc, 0); |
1417 | } |
1418 | |
1419 | static JSParseNode * |
1420 | FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) |
1421 | { |
1422 | return FunctionDef(cx, ts, tc, JSFUN_LAMBDA); |
1423 | } |
1424 | |
1425 | /* |
1426 | * Parse the statements in a block, creating a TOK_LC node that lists the |
1427 | * statements' trees. If called from block-parsing code, the caller must |
1428 | * match { before and } after. |
1429 | */ |
1430 | static JSParseNode * |
1431 | Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) |
1432 | { |
1433 | JSParseNode *pn, *pn2, *saveBlock; |
1434 | JSTokenType tt; |
1435 | |
1436 | JS_CHECK_RECURSION(cx, return NULL); |
1437 | |
1438 | pn = NewParseNode(cx, ts, PN_LIST, tc); |
1439 | if (!pn) |
1440 | return NULL; |
1441 | saveBlock = tc->blockNode; |
1442 | tc->blockNode = pn; |
1443 | PN_INIT_LIST(pn); |
1444 | |
1445 | for (;;) { |
1446 | ts->flags |= TSF_OPERAND; |
1447 | tt = js_PeekToken(cx, ts); |
1448 | ts->flags &= ~TSF_OPERAND; |
1449 | if (tt <= TOK_EOF || tt == TOK_RC) { |
1450 | if (tt == TOK_ERROR) |
1451 | return NULL; |
1452 | break; |
1453 | } |
1454 | pn2 = Statement(cx, ts, tc); |
1455 | if (!pn2) { |
1456 | if (ts->flags & TSF_EOF) |
1457 | ts->flags |= TSF_UNEXPECTED_EOF; |
1458 | return NULL; |
1459 | } |
1460 | |
1461 | if (pn2->pn_type == TOK_FUNCTION) { |
1462 | /* |
1463 | * PNX_FUNCDEFS notifies the emitter that the block contains top- |
1464 | * level function definitions that should be processed before the |
1465 | * rest of nodes. |
1466 | * |
1467 | * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It |
1468 | * is relevant only for function definitions not at top-level, |
1469 | * which we call function statements. |
1470 | */ |
1471 | if (AT_TOP_LEVEL(tc)) |
1472 | pn->pn_extra |= PNX_FUNCDEFS; |
1473 | else |
1474 | tc->flags |= TCF_HAS_FUNCTION_STMT; |
1475 | } |
1476 | PN_APPEND(pn, pn2); |
1477 | } |
1478 | |
1479 | /* |
1480 | * Handle the case where there was a let declaration under this block. If |
1481 | * it replaced tc->blockNode with a new block node then we must refresh pn |
1482 | * and then restore tc->blockNode. |
1483 | */ |
1484 | if (tc->blockNode != pn) |
1485 | pn = tc->blockNode; |
1486 | tc->blockNode = saveBlock; |
1487 | |
1488 | pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; |
1489 | return pn; |
1490 | } |
1491 | |
1492 | static JSParseNode * |
1493 | Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) |
1494 | { |
1495 | JSParseNode *pn; |
1496 | |
1497 | MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND); |
1498 | pn = ParenExpr(cx, ts, tc, NULL, NULL); |
1499 | if (!pn) |
1500 | return NULL; |
1501 | MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); |
1502 | |
1503 | /* |
1504 | * Check for (a = b) and warn about possible (a == b) mistype iff b's |
1505 | * operator has greater precedence than ==. |
1506 | */ |
1507 | if (pn->pn_type == TOK_ASSIGN && |
1508 | pn->pn_op == JSOP_NOP && |
1509 | pn->pn_right->pn_type > TOK_EQOP) |
1510 | { |
1511 | if (!js_ReportCompileErrorNumber(cx, ts, NULL, |
1512 | JSREPORT_WARNING | JSREPORT_STRICT, |
1513 | JSMSG_EQUAL_AS_ASSIGN, |
1514 | "")) { |
1515 | return NULL; |
1516 | } |
1517 | } |
1518 | return pn; |
1519 | } |
1520 | |
1521 | static JSBool |
1522 | MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) |
1523 | { |
1524 | JSAtom *label; |
1525 | JSTokenType tt; |
1526 | |
1527 | tt = js_PeekTokenSameLine(cx, ts); |
1528 | if (tt == TOK_ERROR) |
1529 | return JS_FALSE; |
1530 | if (tt == TOK_NAME) { |
1531 | (void) js_GetToken(cx, ts); |
1532 | label = CURRENT_TOKEN(ts).t_atom; |
1533 | } else { |
1534 | label = NULL; |
1535 | } |
1536 | pn->pn_atom = label; |
1537 | return JS_TRUE; |
1538 | } |
1539 | |
1540 | static JSBool |
1541 | BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) |
1542 | { |
1543 | JSObject *blockObj; |
1544 | JSScopeProperty *sprop; |
1545 | JSAtomListElement *ale; |
1546 | uintN n; |
1547 | |
1548 | blockObj = tc->blockChain; |
1549 | sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom)); |
1550 | ATOM_LIST_SEARCH(ale, &tc->decls, atom); |
1551 | if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) { |
1552 | const char *name; |
1553 | |
1554 | if (sprop) { |
1555 | JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); |
1556 | JS_ASSERT((uint16)sprop->shortid < OBJ_BLOCK_COUNT(cx, blockObj)); |
1557 | } |
1558 | |
1559 | name = js_AtomToPrintableString(cx, atom); |
1560 | if (name) { |
1561 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, |
1562 | JSREPORT_ERROR, JSMSG_REDECLARED_VAR, |
1563 | (ale && ALE_JSOP(ale) == JSOP_DEFCONST) |
1564 | ? js_const_str |
1565 | : "variable", |
1566 | name); |
1567 | } |
1568 | return JS_FALSE; |
1569 | } |
1570 | |
1571 | n = OBJ_BLOCK_COUNT(cx, blockObj); |
1572 | if (n == JS_BIT(16)) { |
1573 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, |
1574 | JSREPORT_ERROR, data->u.let.overflow); |
1575 | return JS_FALSE; |
1576 | } |
1577 | |
1578 | /* Use JSPROP_ENUMERATE to aid the disassembler. */ |
1579 | return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom), |
1580 | JSVAL_VOID, NULL, NULL, |
1581 | JSPROP_ENUMERATE | |
1582 | JSPROP_PERMANENT | |
1583 | JSPROP_SHARED, |
1584 | SPROP_HAS_SHORTID, (int16) n, NULL); |
1585 | } |
1586 | |
1587 | static JSBool |
1588 | BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) |
1589 | { |
1590 | JSStmtInfo *stmt; |
1591 | JSAtomListElement *ale; |
1592 | JSOp op, prevop; |
1593 | const char *name; |
1594 | JSLocalKind localKind; |
1595 | |
1596 | stmt = js_LexicalLookup(tc, atom, NULL); |
1597 | ATOM_LIST_SEARCH(ale, &tc->decls, atom); |
1598 | op = data->op; |
1599 | if ((stmt && stmt->type != STMT_WITH) || ale) { |
1600 | prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR; |
1601 | if (JS_HAS_STRICT_OPTION(cx) |
1602 | ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR |
1603 | : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) { |
1604 | name = js_AtomToPrintableString(cx, atom); |
1605 | if (!name || |
1606 | !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, |
1607 | (op != JSOP_DEFCONST && |
1608 | prevop != JSOP_DEFCONST) |
1609 | ? JSREPORT_WARNING | |
1610 | JSREPORT_STRICT |
1611 | : JSREPORT_ERROR, |
1612 | JSMSG_REDECLARED_VAR, |
1613 | (prevop == JSOP_DEFFUN) |
1614 | ? js_function_str |
1615 | : (prevop == JSOP_DEFCONST) |
1616 | ? js_const_str |
1617 | : js_var_str, |
1618 | name)) { |
1619 | return JS_FALSE; |
1620 | } |
1621 | } |
1622 | if (op == JSOP_DEFVAR && prevop == JSOP_DEFFUN) |
1623 | tc->flags |= TCF_FUN_CLOSURE_VS_VAR; |
1624 | } |
1625 | if (!ale) { |
1626 | ale = js_IndexAtom(cx, atom, &tc->decls); |
1627 | if (!ale) |
1628 | return JS_FALSE; |
1629 | } |
1630 | ALE_SET_JSOP(ale, op); |
1631 | |
1632 | if (!(tc->flags & TCF_IN_FUNCTION)) { |
1633 | /* |
1634 | * Don't lookup global variables or variables in an active frame at |
1635 | * compile time. |
1636 | */ |
1637 | return JS_TRUE; |
1638 | } |
1639 | |
1640 | localKind = js_LookupLocal(cx, tc->u.fun, atom, NULL); |
1641 | if (localKind == JSLOCAL_NONE) { |
1642 | /* |
1643 | * Property not found in current variable scope: we have not seen this |
1644 | * variable before. Define a new local variable by adding a property |
1645 | * to the function's scope, allocating one slot in the function's vars |
1646 | * frame. Any locals declared in with statement bodies are handled at |
1647 | * runtime, by script prolog JSOP_DEFVAR opcodes generated for |
1648 | * slot-less vars. |
1649 | */ |
1650 | localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR; |
1651 | if (!js_InWithStatement(tc) && |
1652 | !BindLocalVariable(cx, tc->u.fun, atom, localKind)) { |
1653 | return JS_FALSE; |
1654 | } |
1655 | } else if (localKind == JSLOCAL_ARG) { |
1656 | name = js_AtomToPrintableString(cx, atom); |
1657 | if (!name) |
1658 | return JS_FALSE; |
1659 | |
1660 | if (op == JSOP_DEFCONST) { |
1661 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, |
1662 | JSREPORT_ERROR, JSMSG_REDECLARED_PARAM, |
1663 | name); |
1664 | return JS_FALSE; |
1665 | } |
1666 | if (!js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, |
1667 | JSREPORT_WARNING | JSREPORT_STRICT, |
1668 | JSMSG_VAR_HIDES_ARG, name)) { |
1669 | return JS_FALSE; |
1670 | } |
1671 | } else { |
1672 | /* Not an argument, must be a redeclared local var. */ |
1673 | JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); |
1674 | } |
1675 | return JS_TRUE; |
1676 | } |
1677 | |
1678 | static JSBool |
1679 | MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg) |
1680 | { |
1681 | JSParseNode *pn2; |
1682 | |
1683 | JS_ASSERT(pn->pn_arity == PN_LIST); |
1684 | JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL || pn->pn_op == JSOP_APPLY); |
1685 | pn2 = pn->pn_head; |
1686 | if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_flags & TCF_GENEXP_LAMBDA)) { |
1687 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn, |
1688 | JSREPORT_ERROR, msg); |
1689 | return JS_FALSE; |
1690 | } |
1691 | pn->pn_op = JSOP_SETCALL; |
1692 | return JS_TRUE; |
1693 | } |
1694 | |
1695 | #if JS_HAS_DESTRUCTURING |
1696 | |
1697 | static JSBool |
1698 | BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn, |
1699 | JSTreeContext *tc) |
1700 | { |
1701 | JSAtom *atom; |
1702 | |
1703 | /* |
1704 | * Destructuring is a form of assignment, so just as for an initialized |
1705 | * simple variable, we must check for assignment to 'arguments' and flag |
1706 | * the enclosing function (if any) as heavyweight. |
1707 | */ |
1708 | JS_ASSERT(pn->pn_type == TOK_NAME); |
1709 | atom = pn->pn_atom; |
1710 | if (atom == cx->runtime->atomState.argumentsAtom) |
1711 | tc->flags |= TCF_FUN_HEAVYWEIGHT; |
1712 | |
1713 | data->pn = pn; |
1714 | if (!data->binder(cx, data, atom, tc)) |
1715 | return JS_FALSE; |
1716 | data->pn = NULL; |
1717 | |
1718 | /* |
1719 | * Select the appropriate name-setting opcode, which may be specialized |
1720 | * further for local variable and argument slot optimizations. At this |
1721 | * point, we can't select the optimal final opcode, yet we must preserve |
1722 | * the CONST bit and convey "set", not "get". |
1723 | */ |
1724 | if (data->op == JSOP_DEFCONST) { |
1725 | pn->pn_op = JSOP_SETCONST; |
1726 | pn->pn_const = JS_TRUE; |
1727 | } else { |
1728 | pn->pn_op = JSOP_SETNAME; |
1729 | pn->pn_const = JS_FALSE; |
1730 | } |
1731 | return JS_TRUE; |
1732 | } |
1733 | |
1734 | /* |
1735 | * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any |
1736 | * LHS expression except a destructuring initialiser, and R is on the stack. |
1737 | * Because R is already evaluated, the usual LHS-specialized bytecodes won't |
1738 | * work. After pushing R[P] we need to evaluate Q's "reference base" QB and |
1739 | * then push its property name QN. At this point the stack looks like |
1740 | * |
1741 | * [... R, R[P], QB, QN] |
1742 | * |
1743 | * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes |
1744 | * its operands with left-hand side above right-hand side: |
1745 | * |
1746 | * [rval, lval, xval] |
1747 | * |
1748 | * and pops all three values, setting lval[xval] = rval. But we cannot select |
1749 | * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var, |
1750 | * which can be optimized further. So we select JSOP_SETNAME. |
1751 | */ |
1752 | static JSBool |
1753 | BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) |
1754 | { |
1755 | while (pn->pn_type == TOK_RP) |
1756 | pn = pn->pn_kid; |
1757 | |
1758 | switch (pn->pn_type) { |
1759 | case TOK_NAME: |
1760 | if (pn->pn_atom == cx->runtime->atomState.argumentsAtom) |
1761 | tc->flags |= TCF_FUN_HEAVYWEIGHT; |
1762 | /* FALL THROUGH */ |
1763 | case TOK_DOT: |
1764 | case TOK_LB: |
1765 | pn->pn_op = JSOP_SETNAME; |
1766 | break; |
1767 | |
1768 | #if JS_HAS_LVALUE_RETURN |
1769 | case TOK_LP: |
1770 | if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) |
1771 | return JS_FALSE; |
1772 | break; |
1773 | #endif |
1774 | |
1775 | #if JS_HAS_XML_SUPPORT |
1776 | case TOK_UNARYOP: |
1777 | if (pn->pn_op == JSOP_XMLNAME) { |
1778 | pn->pn_op = JSOP_BINDXMLNAME; |
1779 | break; |
1780 | } |
1781 | /* FALL THROUGH */ |
1782 | #endif |
1783 | |
1784 | default: |
1785 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn, |
1786 | JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS); |
1787 | return JS_FALSE; |
1788 | } |
1789 | |
1790 | return JS_TRUE; |
1791 | } |
1792 | |
1793 | typedef struct FindPropValData { |
1794 | uint32 numvars; /* # of destructuring vars in left side */ |
1795 | uint32 maxstep; /* max # of steps searching right side */ |
1796 | JSDHashTable table; /* hash table for O(1) right side search */ |
1797 | } FindPropValData; |
1798 | |
1799 | typedef struct FindPropValEntry { |
1800 | JSDHashEntryHdr hdr; |
1801 | JSParseNode *pnkey; |
1802 | JSParseNode *pnval; |
1803 | } FindPropValEntry; |
1804 | |
1805 | #define ASSERT_VALID_PROPERTY_KEY(pnkey) \ |
1806 | JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \ |
1807 | ((pnkey)->pn_type == TOK_NUMBER || \ |
1808 | (pnkey)->pn_type == TOK_STRING || \ |
1809 | (pnkey)->pn_type == TOK_NAME)) |
1810 | |
1811 | static JSDHashNumber |
1812 | HashFindPropValKey(JSDHashTable *table, const void *key) |
1813 | { |
1814 | const JSParseNode *pnkey = (const JSParseNode *)key; |
1815 | |
1816 | ASSERT_VALID_PROPERTY_KEY(pnkey); |
1817 | return (pnkey->pn_type == TOK_NUMBER) |
1818 | ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^ |
1819 | JSDOUBLE_LO32(pnkey->pn_dval)) |
1820 | : ATOM_HASH(pnkey->pn_atom); |
1821 | } |
1822 | |
1823 | static JSBool |
1824 | MatchFindPropValEntry(JSDHashTable *table, |
1825 | const JSDHashEntryHdr *entry, |
1826 | const void *key) |
1827 | { |
1828 | const FindPropValEntry *fpve = (const FindPropValEntry *)entry; |
1829 | const JSParseNode *pnkey = (const JSParseNode *)key; |
1830 | |
1831 | ASSERT_VALID_PROPERTY_KEY(pnkey); |
1832 | return pnkey->pn_type == fpve->pnkey->pn_type && |
1833 | ((pnkey->pn_type == TOK_NUMBER) |
1834 | ? pnkey->pn_dval == fpve->pnkey->pn_dval |
1835 | : pnkey->pn_atom == fpve->pnkey->pn_atom); |
1836 | } |
1837 | |
1838 | static const JSDHashTableOps FindPropValOps = { |
1839 | JS_DHashAllocTable, |
1840 | JS_DHashFreeTable, |
1841 | HashFindPropValKey, |
1842 | MatchFindPropValEntry, |
1843 | JS_DHashMoveEntryStub, |
1844 | JS_DHashClearEntryStub, |
1845 | JS_DHashFinalizeStub, |
1846 | NULL |
1847 | }; |
1848 | |
1849 | #define STEP_HASH_THRESHOLD 10 |
1850 | #define BIG_DESTRUCTURING 5 |
1851 | #define BIG_OBJECT_INIT 20 |
1852 | |
1853 | static JSParseNode * |
1854 | FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data) |
1855 | { |
1856 | FindPropValEntry *entry; |
1857 | JSParseNode *pnhit, *pnhead, *pnprop, *pnkey; |
1858 | uint32 step; |
1859 | |
1860 | /* If we have a hash table, use it as the sole source of truth. */ |
1861 | if (data->table.ops) { |
1862 | entry = (FindPropValEntry *) |
1863 | JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP); |
1864 | return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL; |
1865 | } |
1866 | |
1867 | /* If pn is not an object initialiser node, we can't do anything here. */ |
1868 | if (pn->pn_type != TOK_RC) |
1869 | return NULL; |
1870 | |
1871 | /* |
1872 | * We must search all the way through pn's list, to handle the case of an |
1873 | * id duplicated for two or more property initialisers. |
1874 | */ |
1875 | pnhit = NULL; |
1876 | step = 0; |
1877 | ASSERT_VALID_PROPERTY_KEY(pnid); |
1878 | pnhead = pn->pn_head; |
1879 | if (pnhead && pnhead->pn_type == TOK_DEFSHARP) |
1880 | pnhead = pnhead->pn_next; |
1881 | if (pnid->pn_type == TOK_NUMBER) { |
1882 | for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) { |
1883 | JS_ASSERT(pnprop->pn_type == TOK_COLON); |
1884 | if (pnprop->pn_op == JSOP_NOP) { |
1885 | pnkey = pnprop->pn_left; |
1886 | ASSERT_VALID_PROPERTY_KEY(pnkey); |
1887 | if (pnkey->pn_type == TOK_NUMBER && |
1888 | pnkey->pn_dval == pnid->pn_dval) { |
1889 | pnhit = pnprop; |
1890 | } |
1891 | ++step; |
1892 | } |
1893 | } |
1894 | } else { |
1895 | for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) { |
1896 | JS_ASSERT(pnprop->pn_type == TOK_COLON); |
1897 | if (pnprop->pn_op == JSOP_NOP) { |
1898 | pnkey = pnprop->pn_left; |
1899 | ASSERT_VALID_PROPERTY_KEY(pnkey); |
1900 | if (pnkey->pn_type == pnid->pn_type && |
1901 | pnkey->pn_atom == pnid->pn_atom) { |
1902 | pnhit = pnprop; |
1903 | } |
1904 | ++step; |
1905 | } |
1906 | } |
1907 | } |
1908 | if (!pnhit) |
1909 | return NULL; |
1910 | |
1911 | /* Hit via full search -- see whether it's time to create the hash table. */ |
1912 | JS_ASSERT(!data->table.ops); |
1913 | if (step > data->maxstep) { |
1914 | data->maxstep = step; |
1915 | if (step >= STEP_HASH_THRESHOLD && |
1916 | data->numvars >= BIG_DESTRUCTURING && |
1917 | pn->pn_count >= BIG_OBJECT_INIT && |
1918 | JS_DHashTableInit(&data->table, &FindPropValOps, pn, |
1919 | sizeof(FindPropValEntry), |
1920 | JS_DHASH_DEFAULT_CAPACITY(pn->pn_count))) |
1921 | { |
1922 | for (pn = pnhead; pn; pn = pn->pn_next) { |
1923 | JS_ASSERT(pnprop->pn_type == TOK_COLON); |
1924 | ASSERT_VALID_PROPERTY_KEY(pn->pn_left); |
1925 | entry = (FindPropValEntry *) |
1926 | JS_DHashTableOperate(&data->table, pn->pn_left, |
1927 | JS_DHASH_ADD); |
1928 | entry->pnval = pn->pn_right; |
1929 | } |
1930 | } |
1931 | } |
1932 | return pnhit->pn_right; |
1933 | } |
1934 | |
1935 | /* |
1936 | * If data is null, the caller is AssignExpr and instead of binding variables, |
1937 | * we specialize lvalues in the propery value positions of the left-hand side. |
1938 | * If right is null, just check for well-formed lvalues. |
1939 | */ |
1940 | static JSBool |
1941 | CheckDestructuring(JSContext *cx, BindData *data, |
1942 | JSParseNode *left, JSParseNode *right, |
1943 | JSTreeContext *tc) |
1944 | { |
1945 | JSBool ok; |
1946 | FindPropValData fpvd; |
1947 | JSParseNode *lhs, *rhs, *pn, *pn2; |
1948 | |
1949 | if (left->pn_type == TOK_ARRAYCOMP) { |
1950 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), left, |
1951 | JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE); |
1952 | return JS_FALSE; |
1953 | } |
1954 | |
1955 | #if JS_HAS_DESTRUCTURING_SHORTHAND |
1956 | if (right && right->pn_arity == PN_LIST && (right->pn_extra & PNX_SHORTHAND)) { |
1957 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), right, |
1958 | JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT); |
1959 | return JS_FALSE; |
1960 | } |
1961 | #endif |
1962 | |
1963 | fpvd.table.ops = NULL; |
1964 | lhs = left->pn_head; |
1965 | if (lhs && lhs->pn_type == TOK_DEFSHARP) { |
1966 | pn = lhs; |
1967 | goto no_var_name; |
1968 | } |
1969 | |
1970 | if (left->pn_type == TOK_RB) { |
1971 | rhs = (right && right->pn_type == left->pn_type) |
1972 | ? right->pn_head |
1973 | : NULL; |
1974 | |
1975 | while (lhs) { |
1976 | pn = lhs, pn2 = rhs; |
1977 | if (!data) { |
1978 | /* Skip parenthesization if not in a variable declaration. */ |
1979 | while (pn->pn_type == TOK_RP) |
1980 | pn = pn->pn_kid; |
1981 | if (pn2) { |
1982 | while (pn2->pn_type == TOK_RP) |
1983 | pn2 = pn2->pn_kid; |
1984 | } |
1985 | } |
1986 | |
1987 | /* Nullary comma is an elision; binary comma is an expression.*/ |
1988 | if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) { |
1989 | if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { |
1990 | ok = CheckDestructuring(cx, data, pn, pn2, tc); |
1991 | } else { |
1992 | if (data) { |
1993 | if (pn->pn_type != TOK_NAME) |
1994 | goto no_var_name; |
1995 | |
1996 | ok = BindDestructuringVar(cx, data, pn, tc); |
1997 | } else { |
1998 | ok = BindDestructuringLHS(cx, pn, tc); |
1999 | } |
2000 | } |
2001 | if (!ok) |
2002 | goto out; |
2003 | } |
2004 | |
2005 | lhs = lhs->pn_next; |
2006 | if (rhs) |
2007 | rhs = rhs->pn_next; |
2008 | } |
2009 | } else { |
2010 | JS_ASSERT(left->pn_type == TOK_RC); |
2011 | fpvd.numvars = left->pn_count; |
2012 | fpvd.maxstep = 0; |
2013 | rhs = NULL; |
2014 | |
2015 | while (lhs) { |
2016 | JS_ASSERT(lhs->pn_type == TOK_COLON); |
2017 | pn = lhs->pn_right; |
2018 | if (!data) { |
2019 | /* Skip parenthesization if not in a variable declaration. */ |
2020 | while (pn->pn_type == TOK_RP) |
2021 | pn = pn->pn_kid; |
2022 | } |
2023 | |
2024 | if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { |
2025 | if (right) { |
2026 | rhs = FindPropertyValue(right, lhs->pn_left, &fpvd); |
2027 | if (rhs && !data) { |
2028 | while (rhs->pn_type == TOK_RP) |
2029 | rhs = rhs->pn_kid; |
2030 | } |
2031 | } |
2032 | |
2033 | ok = CheckDestructuring(cx, data, pn, rhs, tc); |
2034 | } else if (data) { |
2035 | if (pn->pn_type != TOK_NAME) |
2036 | goto no_var_name; |
2037 | |
2038 | ok = BindDestructuringVar(cx, data, pn, tc); |
2039 | } else { |
2040 | ok = BindDestructuringLHS(cx, pn, tc); |
2041 | } |
2042 | if (!ok) |
2043 | goto out; |
2044 | |
2045 | lhs = lhs->pn_next; |
2046 | } |
2047 | } |
2048 | |
2049 | /* |
2050 | * The catch/finally handler implementation in the interpreter assumes |
2051 | * that any operation that introduces a new scope (like a "let" or "with" |
2052 | * block) increases the stack depth. This way, it is possible to restore |
2053 | * the scope chain based on stack depth of the handler alone. "let" with |
2054 | * an empty destructuring pattern like in |
2055 | * |
2056 | * let [] = 1; |
2057 | * |
2058 | * would violate this assumption as the there would be no let locals to |
2059 | * store on the stack. To satisfy it we add an empty property to such |
2060 | * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of |
2061 | * slots, would be always positive. |
2062 | * |
2063 | * Note that we add such a property even if the block has locals due to |
2064 | * later let declarations in it. We optimize for code simplicity here, |
2065 | * not the fastest runtime performance with empty [] or {}. |
2066 | */ |
2067 | if (data && |
2068 | data->binder == BindLet && |
2069 | OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) { |
2070 | ok = js_DefineNativeProperty(cx, tc->blockChain, |
2071 | ATOM_TO_JSID(cx->runtime-> |
2072 | atomState.emptyAtom), |
2073 | JSVAL_VOID, NULL, NULL, |
2074 | JSPROP_ENUMERATE | |
2075 | JSPROP_PERMANENT | |
2076 | JSPROP_SHARED, |
2077 | SPROP_HAS_SHORTID, 0, NULL); |
2078 | if (!ok) |
2079 | goto out; |
2080 | } |
2081 | |
2082 | ok = JS_TRUE; |
2083 | |
2084 | out: |
2085 | if (fpvd.table.ops) |
2086 | JS_DHashTableFinish(&fpvd.table); |
2087 | return ok; |
2088 | |
2089 | no_var_name: |
2090 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn, JSREPORT_ERROR, |
2091 | JSMSG_NO_VARIABLE_NAME); |
2092 | ok = JS_FALSE; |
2093 | goto out; |
2094 | } |
2095 | |
2096 | static JSParseNode * |
2097 | DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, |
2098 | JSTokenType tt) |
2099 | { |
2100 | JSParseNode *pn; |
2101 | |
2102 | pn = PrimaryExpr(cx, TS(tc->parseContext), tc, tt, JS_FALSE); |
2103 | if (!pn) |
2104 | return NULL; |
2105 | if (!CheckDestructuring(cx, data, pn, NULL, tc)) |
2106 | return NULL; |
2107 | return pn; |
2108 | } |
2109 | |
2110 | // Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case. |
2111 | static JSParseNode * |
2112 | CloneParseTree(JSContext *cx, JSParseNode *opn, JSTreeContext *tc) |
2113 | { |
2114 | JSParseNode *pn, *pn2, *opn2; |
2115 | |
2116 | pn = NewOrRecycledNode(cx, tc); |
2117 | if (!pn) |
2118 | return NULL; |
2119 | pn->pn_type = opn->pn_type; |
2120 | pn->pn_pos = opn->pn_pos; |
2121 | pn->pn_op = opn->pn_op; |
2122 | pn->pn_arity = opn->pn_arity; |
2123 | |
2124 | switch (pn->pn_arity) { |
2125 | #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO |
2126 | |
2127 | case PN_FUNC: |
2128 | NULLCHECK(pn->pn_funpob = |
2129 | js_NewParsedObjectBox(cx, tc->parseContext, opn->pn_funpob->object)); |
2130 | NULLCHECK(pn->pn_body = CloneParseTree(cx, opn->pn_body, tc)); |
2131 | pn->pn_flags = opn->pn_flags; |
2132 | pn->pn_index = opn->pn_index; |
2133 | break; |
2134 | |
2135 | case PN_LIST: |
2136 | PN_INIT_LIST(pn); |
2137 | for (opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) { |
2138 | NULLCHECK(pn2 = CloneParseTree(cx, opn2, tc)); |
2139 | PN_APPEND(pn, pn2); |
2140 | } |
2141 | pn->pn_extra = opn->pn_extra; |
2142 | break; |
2143 | |
2144 | case PN_TERNARY: |
2145 | NULLCHECK(pn->pn_kid1 = CloneParseTree(cx, opn->pn_kid1, tc)); |
2146 | NULLCHECK(pn->pn_kid2 = CloneParseTree(cx, opn->pn_kid2, tc)); |
2147 | NULLCHECK(pn->pn_kid3 = CloneParseTree(cx, opn->pn_kid3, tc)); |
2148 | break; |
2149 | |
2150 | case PN_BINARY: |
2151 | NULLCHECK(pn->pn_left = CloneParseTree(cx, opn->pn_left, tc)); |
2152 | if (opn->pn_right != opn->pn_left) |
2153 | NULLCHECK(pn->pn_right = CloneParseTree(cx, opn->pn_right, tc)); |
2154 | else |
2155 | pn->pn_right = pn->pn_left; |
2156 | pn->pn_val = opn->pn_val; |
2157 | pn->pn_iflags = opn->pn_iflags; |
2158 | break; |
2159 | |
2160 | case PN_UNARY: |
2161 | NULLCHECK(pn->pn_kid = CloneParseTree(cx, opn->pn_kid, tc)); |
2162 | pn->pn_num = opn->pn_num; |
2163 | pn->pn_hidden = opn->pn_hidden; |
2164 | break; |
2165 | |
2166 | case PN_NAME: |
2167 | // PN_NAME could mean several arms in pn_u, so copy the whole thing. |
2168 | pn->pn_u = opn->pn_u; |
2169 | if (opn->pn_expr) |
2170 | NULLCHECK(pn->pn_expr = CloneParseTree(cx, opn->pn_expr, tc)); |
2171 | break; |
2172 | |
2173 | case PN_NULLARY: |
2174 | // Even PN_NULLARY may have data (apair for E4X -- what a botch). |
2175 | pn->pn_u = opn->pn_u; |
2176 | break; |
2177 | |
2178 | #undef NULLCHECK |
2179 | } |
2180 | return pn; |
2181 | } |
2182 | |
2183 | #endif /* JS_HAS_DESTRUCTURING */ |
2184 | |
2185 | extern const char js_with_statement_str[]; |
2186 | |
2187 | static JSParseNode * |
2188 | ContainsStmt(JSParseNode *pn, JSTokenType tt) |
2189 | { |
2190 | JSParseNode *pn2, *pnt; |
2191 | |
2192 | if (!pn) |
2193 | return NULL; |
2194 | if (pn->pn_type == tt) |
2195 | return pn; |
2196 | switch (pn->pn_arity) { |
2197 | case PN_LIST: |
2198 | for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
2199 | pnt = ContainsStmt(pn2, tt); |
2200 | if (pnt) |
2201 | return pnt; |
2202 | } |
2203 | break; |
2204 | case PN_TERNARY: |
2205 | pnt = ContainsStmt(pn->pn_kid1, tt); |
2206 | if (pnt) |
2207 | return pnt; |
2208 | pnt = ContainsStmt(pn->pn_kid2, tt); |
2209 | if (pnt) |
2210 | return pnt; |
2211 | return ContainsStmt(pn->pn_kid3, tt); |
2212 | case PN_BINARY: |
2213 | /* |
2214 | * Limit recursion if pn is a binary expression, which can't contain a |
2215 | * var statement. |
2216 | */ |
2217 | if (pn->pn_op != JSOP_NOP) |
2218 | return NULL; |
2219 | pnt = ContainsStmt(pn->pn_left, tt); |
2220 | if (pnt) |
2221 | return pnt; |
2222 | return ContainsStmt(pn->pn_right, tt); |
2223 | case PN_UNARY: |
2224 | if (pn->pn_op != JSOP_NOP) |
2225 | return NULL; |
2226 | return ContainsStmt(pn->pn_kid, tt); |
2227 | case PN_NAME: |
2228 | return ContainsStmt(pn->pn_expr, tt); |
2229 | default:; |
2230 | } |
2231 | return NULL; |
2232 | } |
2233 | |
2234 | static JSParseNode * |
2235 | ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, |
2236 | JSParser operandParser) |
2237 | { |
2238 | JSTokenType tt, tt2; |
2239 | JSParseNode *pn, *pn2; |
2240 | |
2241 | tt = CURRENT_TOKEN(ts).type; |
2242 | if (tt == TOK_RETURN && !(tc->flags & TCF_IN_FUNCTION)) { |
2243 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
2244 | JSMSG_BAD_RETURN_OR_YIELD, js_return_str); |
2245 | return NULL; |
2246 | } |
2247 | |
2248 | pn = NewParseNode(cx, ts, PN_UNARY, tc); |
2249 | if (!pn) |
2250 | return NULL; |
2251 | |
2252 | #if JS_HAS_GENERATORS |
2253 | if (tt == TOK_YIELD) |
2254 | tc->flags |= TCF_FUN_IS_GENERATOR; |
2255 | #endif |
2256 | |
2257 | /* This is ugly, but we don't want to require a semicolon. */ |
2258 | ts->flags |= TSF_OPERAND; |
2259 | tt2 = js_PeekTokenSameLine(cx, ts); |
2260 | ts->flags &= ~TSF_OPERAND; |
2261 | if (tt2 == TOK_ERROR) |
2262 | return NULL; |
2263 | |
2264 | if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC |
2265 | #if JS_HAS_GENERATORS |
2266 | && (tt != TOK_YIELD || |
2267 | (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP && |
2268 | tt2 != TOK_COLON && tt2 != TOK_COMMA)) |
2269 | #endif |
2270 | ) { |
2271 | pn2 = operandParser(cx, ts, tc); |
2272 | if (!pn2) |
2273 | return NULL; |
2274 | #if JS_HAS_GENERATORS |
2275 | if (tt == TOK_RETURN) |
2276 | #endif |
2277 | tc->flags |= TCF_RETURN_EXPR; |
2278 | pn->pn_pos.end = pn2->pn_pos.end; |
2279 | pn->pn_kid = pn2; |
2280 | } else { |
2281 | #if JS_HAS_GENERATORS |
2282 | if (tt == TOK_RETURN) |
2283 | #endif |
2284 | tc->flags |= TCF_RETURN_VOID; |
2285 | } |
2286 | |
2287 | if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) { |
2288 | /* As in Python (see PEP-255), disallow return v; in generators. */ |
2289 | ReportBadReturn(cx, tc, JSREPORT_ERROR, |
2290 | JSMSG_BAD_GENERATOR_RETURN, |
2291 | JSMSG_BAD_ANON_GENERATOR_RETURN); |
2292 | return NULL; |
2293 | } |
2294 | |
2295 | if (JS_HAS_STRICT_OPTION(cx) && |
2296 | (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 && |
2297 | !ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT, |
2298 | JSMSG_NO_RETURN_VALUE, |
2299 | JSMSG_ANON_NO_RETURN_VALUE)) { |
2300 | return NULL; |
2301 | } |
2302 | |
2303 | return pn; |
2304 | } |
2305 | |
2306 | static JSParseNode * |
2307 | PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, |
2308 | JSStmtInfo *stmtInfo) |
2309 | { |
2310 | JSParseNode *pn; |
2311 | JSObject *obj; |
2312 | JSParsedObjectBox *blockpob; |
2313 | |
2314 | pn = NewParseNode(cx, ts, PN_NAME, tc); |
2315 | if (!pn) |
2316 | return NULL; |
2317 | |
2318 | obj = js_NewBlockObject(cx); |
2319 | if (!obj) |
2320 | return NULL; |
2321 | |
2322 | blockpob = js_NewParsedObjectBox(cx, tc->parseContext, obj); |
2323 | if (!blockpob) |
2324 | return NULL; |
2325 | |
2326 | js_PushBlockScope(tc, stmtInfo, obj, -1); |
2327 | pn->pn_type = TOK_LEXICALSCOPE; |
2328 | pn->pn_op = JSOP_LEAVEBLOCK; |
2329 | pn->pn_pob = blockpob; |
2330 | pn->pn_slot = -1; |
2331 | return pn; |
2332 | } |
2333 | |
2334 | #if JS_HAS_BLOCK_SCOPE |
2335 | |
2336 | static JSParseNode * |
2337 | LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement) |
2338 | { |
2339 | JSParseNode *pn, *pnblock, *pnlet; |
2340 | JSStmtInfo stmtInfo; |
2341 | |
2342 | JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET); |
2343 | |
2344 | /* Create the let binary node. */ |
2345 | pnlet = NewParseNode(cx, ts, PN_BINARY, tc); |
2346 | if (!pnlet) |
2347 | return NULL; |
2348 | |
2349 | MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET); |
2350 | |
2351 | /* This is a let block or expression of the form: let (a, b, c) .... */ |
2352 | pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo); |
2353 | if (!pnblock) |
2354 | return NULL; |
2355 | pn = pnblock; |
2356 | pn->pn_expr = pnlet; |
2357 | |
2358 | pnlet->pn_left = Variables(cx, ts, tc); |
2359 | if (!pnlet->pn_left) |
2360 | return NULL; |
2361 | pnlet->pn_left->pn_extra = PNX_POPVAR; |
2362 | |
2363 | MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET); |
2364 | |
2365 | ts->flags |= TSF_OPERAND; |
2366 | if (statement && !js_MatchToken(cx, ts, TOK_LC)) { |
2367 | /* |
2368 | * If this is really an expression in let statement guise, then we |
2369 | * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop |
2370 | * the return value of the expression. |
2371 | */ |
2372 | pn = NewParseNode(cx, ts, PN_UNARY, tc); |
2373 | if (!pn) |
2374 | return NULL; |
2375 | pn->pn_type = TOK_SEMI; |
2376 | pn->pn_num = -1; |
2377 | pn->pn_kid = pnblock; |
2378 | |
2379 | statement = JS_FALSE; |
2380 | } |
2381 | ts->flags &= ~TSF_OPERAND; |
2382 | |
2383 | if (statement) { |
2384 | pnlet->pn_right = Statements(cx, ts, tc); |
2385 | if (!pnlet->pn_right) |
2386 | return NULL; |
2387 | MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET); |
2388 | } else { |
2389 | /* |
2390 | * Change pnblock's opcode to the variant that propagates the last |
2391 | * result down after popping the block, and clear statement. |
2392 | */ |
2393 | pnblock->pn_op = JSOP_LEAVEBLOCKEXPR; |
2394 | pnlet->pn_right = AssignExpr(cx, ts, tc); |
2395 | if (!pnlet->pn_right) |
2396 | return NULL; |
2397 | } |
2398 | |
2399 | js_PopStatement(tc); |
2400 | return pn; |
2401 | } |
2402 | |
2403 | #endif /* JS_HAS_BLOCK_SCOPE */ |
2404 | |
2405 | static JSParseNode * |
2406 | Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) |
2407 | { |
2408 | JSTokenType tt; |
2409 | JSParseNode *pn, *pn1, *pn2, *pn3, *pn4; |
2410 | JSStmtInfo stmtInfo, *stmt, *stmt2; |
2411 | JSAtom *label; |
2412 | |
2413 | JS_CHECK_RECURSION(cx, return NULL); |
2414 | |
2415 | ts->flags |= TSF_OPERAND; |
2416 | tt = js_GetToken(cx, ts); |
2417 | ts->flags &= ~TSF_OPERAND; |
2418 | |
2419 | #if JS_HAS_GETTER_SETTER |
2420 | if (tt == TOK_NAME) { |
2421 | tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION); |
2422 | if (tt == TOK_ERROR) |
2423 | return NULL; |
2424 | } |
2425 | #endif |
2426 | |
2427 | switch (tt) { |
2428 | case TOK_FUNCTION: |
2429 | #if JS_HAS_XML_SUPPORT |
2430 | ts->flags |= TSF_KEYWORD_IS_NAME; |
2431 | tt = js_PeekToken(cx, ts); |
2432 | ts->flags &= ~TSF_KEYWORD_IS_NAME; |
2433 | if (tt == TOK_DBLCOLON) |
2434 | goto expression; |
2435 | #endif |
2436 | return FunctionStmt(cx, ts, tc); |
2437 | |
2438 | case TOK_IF: |
2439 | /* An IF node has three kids: condition, then, and optional else. */ |
2440 | pn = NewParseNode(cx, ts, PN_TERNARY, tc); |
2441 | if (!pn) |
2442 | return NULL; |
2443 | pn1 = Condition(cx, ts, tc); |
2444 | if (!pn1) |
2445 | return NULL; |
2446 | js_PushStatement(tc, &stmtInfo, STMT_IF, -1); |
2447 | pn2 = Statement(cx, ts, tc); |
2448 | if (!pn2) |
2449 | return NULL; |
2450 | ts->flags |= TSF_OPERAND; |
2451 | if (js_MatchToken(cx, ts, TOK_ELSE)) { |
2452 | ts->flags &= ~TSF_OPERAND; |
2453 | stmtInfo.type = STMT_ELSE; |
2454 | pn3 = Statement(cx, ts, tc); |
2455 | if (!pn3) |
2456 | return NULL; |
2457 | pn->pn_pos.end = pn3->pn_pos.end; |
2458 | } else { |
2459 | ts->flags &= ~TSF_OPERAND; |
2460 | pn3 = NULL; |
2461 | pn->pn_pos.end = pn2->pn_pos.end; |
2462 | } |
2463 | js_PopStatement(tc); |
2464 | pn->pn_kid1 = pn1; |
2465 | pn->pn_kid2 = pn2; |
2466 | pn->pn_kid3 = pn3; |
2467 | return pn; |
2468 | |
2469 | case TOK_SWITCH: |
2470 | { |
2471 | JSParseNode *pn5, *saveBlock; |
2472 | JSBool seenDefault = JS_FALSE; |
2473 | |
2474 | pn = NewParseNode(cx, ts, PN_BINARY, tc); |
2475 | if (!pn) |
2476 | return NULL; |
2477 | MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH); |
2478 | |
2479 | /* pn1 points to the switch's discriminant. */ |
2480 | pn1 = ParenExpr(cx, ts, tc, NULL, NULL); |
2481 | if (!pn1) |
2482 | return NULL; |
2483 | |
2484 | MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH); |
2485 | MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH); |
2486 | |
2487 | /* pn2 is a list of case nodes. The default case has pn_left == NULL */ |
2488 | pn2 = NewParseNode(cx, ts, PN_LIST, tc); |
2489 | if (!pn2) |
2490 | return NULL; |
2491 | saveBlock = tc->blockNode; |
2492 | tc->blockNode = pn2; |
2493 | PN_INIT_LIST(pn2); |
2494 | |
2495 | js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1); |
2496 | |
2497 | while ((tt = js_GetToken(cx, ts)) != TOK_RC) { |
2498 | switch (tt) { |
2499 | case TOK_DEFAULT: |
2500 | if (seenDefault) { |
2501 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
2502 | JSMSG_TOO_MANY_DEFAULTS); |
2503 | return NULL; |
2504 | } |
2505 | seenDefault = JS_TRUE; |
2506 | /* FALL THROUGH */ |
2507 | |
2508 | case TOK_CASE: |
2509 | pn3 = NewParseNode(cx, ts, PN_BINARY, tc); |
2510 | if (!pn3) |
2511 | return NULL; |
2512 | if (tt == TOK_CASE) { |
2513 | pn3->pn_left = Expr(cx, ts, tc); |
2514 | if (!pn3->pn_left) |
2515 | return NULL; |
2516 | } |
2517 | PN_APPEND(pn2, pn3); |
2518 | if (pn2->pn_count == JS_BIT(16)) { |
2519 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
2520 | JSMSG_TOO_MANY_CASES); |
2521 | return NULL; |
2522 | } |
2523 | break; |
2524 | |
2525 | case TOK_ERROR: |
2526 | return NULL; |
2527 | |
2528 | default: |
2529 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
2530 | JSMSG_BAD_SWITCH); |
2531 | return NULL; |
2532 | } |
2533 | MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE); |
2534 | |
2535 | pn4 = NewParseNode(cx, ts, PN_LIST, tc); |
2536 | if (!pn4) |
2537 | return NULL; |
2538 | pn4->pn_type = TOK_LC; |
2539 | PN_INIT_LIST(pn4); |
2540 | ts->flags |= TSF_OPERAND; |
2541 | while ((tt = js_PeekToken(cx, ts)) != TOK_RC && |
2542 | tt != TOK_CASE && tt != TOK_DEFAULT) { |
2543 | ts->flags &= ~TSF_OPERAND; |
2544 | if (tt == TOK_ERROR) |
2545 | return NULL; |
2546 | pn5 = Statement(cx, ts, tc); |
2547 | if (!pn5) |
2548 | return NULL; |
2549 | pn4->pn_pos.end = pn5->pn_pos.end; |
2550 | PN_APPEND(pn4, pn5); |
2551 | ts->flags |= TSF_OPERAND; |
2552 | } |
2553 | ts->flags &= ~TSF_OPERAND; |
2554 | |
2555 | /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */ |
2556 | if (pn4->pn_head) |
2557 | pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin; |
2558 | pn3->pn_pos.end = pn4->pn_pos.end; |
2559 | pn3->pn_right = pn4; |
2560 | } |
2561 | |
2562 | /* |
2563 | * Handle the case where there was a let declaration in any case in |
2564 | * the switch body, but not within an inner block. If it replaced |
2565 | * tc->blockNode with a new block node then we must refresh pn2 and |
2566 | * then restore tc->blockNode. |
2567 | */ |
2568 | if (tc->blockNode != pn2) |
2569 | pn2 = tc->blockNode; |
2570 | tc->blockNode = saveBlock; |
2571 | js_PopStatement(tc); |
2572 | |
2573 | pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; |
2574 | pn->pn_left = pn1; |
2575 | pn->pn_right = pn2; |
2576 | return pn; |
2577 | } |
2578 | |
2579 | case TOK_WHILE: |
2580 | pn = NewParseNode(cx, ts, PN_BINARY, tc); |
2581 | if (!pn) |
2582 | return NULL; |
2583 | js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1); |
2584 | pn2 = Condition(cx, ts, tc); |
2585 | if (!pn2) |
2586 | return NULL; |
2587 | pn->pn_left = pn2; |
2588 | pn2 = Statement(cx, ts, tc); |
2589 | if (!pn2) |
2590 | return NULL; |
2591 | js_PopStatement(tc); |
2592 | pn->pn_pos.end = pn2->pn_pos.end; |
2593 | pn->pn_right = pn2; |
2594 | return pn; |
2595 | |
2596 | case TOK_DO: |
2597 | pn = NewParseNode(cx, ts, PN_BINARY, tc); |
2598 | if (!pn) |
2599 | return NULL; |
2600 | js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1); |
2601 | pn2 = Statement(cx, ts, tc); |
2602 | if (!pn2) |
2603 | return NULL; |
2604 | pn->pn_left = pn2; |
2605 | MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO); |
2606 | pn2 = Condition(cx, ts, tc); |
2607 | if (!pn2) |
2608 | return NULL; |
2609 | js_PopStatement(tc); |
2610 | pn->pn_pos.end = pn2->pn_pos.end; |
2611 | pn->pn_right = pn2; |
2612 | if (JSVERSION_NUMBER(cx) != JSVERSION_ECMA_3) { |
2613 | /* |
2614 | * All legacy and extended versions must do automatic semicolon |
2615 | * insertion after do-while. See the testcase and discussion in |
2616 | * http://bugzilla.mozilla.org/show_bug.cgi?id=238945. |
2617 | */ |
2618 | (void) js_MatchToken(cx, ts, TOK_SEMI); |
2619 | return pn; |
2620 | } |
2621 | break; |
2622 | |
2623 | case TOK_FOR: |
2624 | { |
2625 | JSParseNode *pnseq = NULL; |
2626 | #if JS_HAS_BLOCK_SCOPE |
2627 | JSParseNode *pnlet = NULL; |
2628 | JSStmtInfo blockInfo; |
2629 | #endif |
2630 | |
2631 | /* A FOR node is binary, left is loop control and right is the body. */ |
2632 | pn = NewParseNode(cx, ts, PN_BINARY, tc); |
2633 | if (!pn) |
2634 | return NULL; |
2635 | js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1); |
2636 | |
2637 | pn->pn_op = JSOP_ITER; |
2638 | pn->pn_iflags = 0; |
2639 | if (js_MatchToken(cx, ts, TOK_NAME)) { |
2640 | if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom) |
2641 | pn->pn_iflags = JSITER_FOREACH; |
2642 | else |
2643 | js_UngetToken(ts); |
2644 | } |
2645 | |
2646 | MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); |
2647 | ts->flags |= TSF_OPERAND; |
2648 | tt = js_PeekToken(cx, ts); |
2649 | ts->flags &= ~TSF_OPERAND; |
2650 | if (tt == TOK_SEMI) { |
2651 | if (pn->pn_iflags & JSITER_FOREACH) |
2652 | goto bad_for_each; |
2653 | |
2654 | /* No initializer -- set first kid of left sub-node to null. */ |
2655 | pn1 = NULL; |
2656 | } else { |
2657 | /* |
2658 | * Set pn1 to a var list or an initializing expression. |
2659 | * |
2660 | * Set the TCF_IN_FOR_INIT flag during parsing of the first clause |
2661 | * of the for statement. This flag will be used by the RelExpr |
2662 | * production; if it is set, then the 'in' keyword will not be |
2663 | * recognized as an operator, leaving it available to be parsed as |
2664 | * part of a for/in loop. |
2665 | * |
2666 | * A side effect of this restriction is that (unparenthesized) |
2667 | * expressions involving an 'in' operator are illegal in the init |
2668 | * clause of an ordinary for loop. |
2669 | */ |
2670 | tc->flags |= TCF_IN_FOR_INIT; |
2671 | if (tt == TOK_VAR) { |
2672 | (void) js_GetToken(cx, ts); |
2673 | pn1 = Variables(cx, ts, tc); |
2674 | #if JS_HAS_BLOCK_SCOPE |
2675 | } else if (tt == TOK_LET) { |
2676 | (void) js_GetToken(cx, ts); |
2677 | if (js_PeekToken(cx, ts) == TOK_LP) { |
2678 | pn1 = LetBlock(cx, ts, tc, JS_FALSE); |
2679 | tt = TOK_LEXICALSCOPE; |
2680 | } else { |
2681 | pnlet = PushLexicalScope(cx, ts, tc, &blockInfo); |
2682 | if (!pnlet) |
2683 | return NULL; |
2684 | blockInfo.flags |= SIF_FOR_BLOCK; |
2685 | pn1 = Variables(cx, ts, tc); |
2686 | } |
2687 | #endif |
2688 | } else { |
2689 | pn1 = Expr(cx, ts, tc); |
2690 | if (pn1) { |
2691 | while (pn1->pn_type == TOK_RP) |
2692 | pn1 = pn1->pn_kid; |
2693 | } |
2694 | } |
2695 | tc->flags &= ~TCF_IN_FOR_INIT; |
2696 | if (!pn1) |
2697 | return NULL; |
2698 | } |
2699 | |
2700 | /* |
2701 | * We can be sure that it's a for/in loop if there's still an 'in' |
2702 | * keyword here, even if JavaScript recognizes 'in' as an operator, |
2703 | * as we've excluded 'in' from being parsed in RelExpr by setting |
2704 | * the TCF_IN_FOR_INIT flag in our JSTreeContext. |
2705 | */ |
2706 | if (pn1 && js_MatchToken(cx, ts, TOK_IN)) { |
2707 | pn->pn_iflags |= JSITER_ENUMERATE; |
2708 | stmtInfo.type = STMT_FOR_IN_LOOP; |
2709 | |
2710 | /* Check that the left side of the 'in' is valid. */ |
2711 | JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == tt); |
2712 | if (TOKEN_TYPE_IS_DECL(tt) |
2713 | ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST |
2714 | #if JS_HAS_DESTRUCTURING |
2715 | || (JSVERSION_NUMBER(cx) == JSVERSION_1_7 && |
2716 | pn->pn_op == JSOP_ITER && |
2717 | !(pn->pn_iflags & JSITER_FOREACH) && |
2718 | (pn1->pn_head->pn_type == TOK_RC || |
2719 | (pn1->pn_head->pn_type == TOK_RB && |
2720 | pn1->pn_head->pn_count != 2) || |
2721 | (pn1->pn_head->pn_type == TOK_ASSIGN && |
2722 | (pn1->pn_head->pn_left->pn_type != TOK_RB || |
2723 | pn1->pn_head->pn_left->pn_count != 2)))) |
2724 | #endif |
2725 | ) |
2726 | : (pn1->pn_type != TOK_NAME && |
2727 | pn1->pn_type != TOK_DOT && |
2728 | #if JS_HAS_DESTRUCTURING |
2729 | ((JSVERSION_NUMBER(cx) == JSVERSION_1_7 && |
2730 | pn->pn_op == JSOP_ITER && |
2731 | !(pn->pn_iflags & JSITER_FOREACH)) |
2732 | ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2) |
2733 | : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) && |
2734 | #endif |
2735 | #if JS_HAS_LVALUE_RETURN |
2736 | pn1->pn_type != TOK_LP && |
2737 | #endif |
2738 | #if JS_HAS_XML_SUPPORT |
2739 | (pn1->pn_type != TOK_UNARYOP || |
2740 | pn1->pn_op != JSOP_XMLNAME) && |
2741 | #endif |
2742 | pn1->pn_type != TOK_LB)) { |
2743 | js_ReportCompileErrorNumber(cx, ts, pn1, JSREPORT_ERROR, |
2744 | JSMSG_BAD_FOR_LEFTSIDE); |
2745 | return NULL; |
2746 | } |
2747 | |
2748 | /* pn2 points to the name or destructuring pattern on in's left. */ |
2749 | pn2 = NULL; |
2750 | |
2751 | if (TOKEN_TYPE_IS_DECL(tt)) { |
2752 | /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */ |
2753 | pn1->pn_extra |= PNX_FORINVAR; |
2754 | |
2755 | /* |
2756 | * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let', |
2757 | * 'var', or 'const' to hoist the initializer or the entire |
2758 | * decl out of the loop head. TOK_VAR is the type for both |
2759 | * 'var' and 'const'. |
2760 | */ |
2761 | pn2 = pn1->pn_head; |
2762 | if (pn2->pn_type == TOK_NAME && pn2->pn_expr |
2763 | #if JS_HAS_DESTRUCTURING |
2764 | || pn2->pn_type == TOK_ASSIGN |
2765 | #endif |
2766 | ) { |
2767 | pnseq = NewParseNode(cx, ts, PN_LIST, tc); |
2768 | if (!pnseq) |
2769 | return NULL; |
2770 | pnseq->pn_type = TOK_SEQ; |
2771 | pnseq->pn_pos.begin = pn->pn_pos.begin; |
2772 | if (tt == TOK_LET) { |
2773 | /* |
2774 | * Hoist just the 'i' from 'for (let x = i in o)' to |
2775 | * before the loop, glued together via pnseq. |
2776 | */ |
2777 | pn3 = NewParseNode(cx, ts, PN_UNARY, tc); |
2778 | if (!pn3) |
2779 | return NULL; |
2780 | pn3->pn_type = TOK_SEMI; |
2781 | pn3->pn_op = JSOP_NOP; |
2782 | #if JS_HAS_DESTRUCTURING |
2783 | if (pn2->pn_type == TOK_ASSIGN) { |
2784 | pn4 = pn2->pn_right; |
2785 | pn2 = pn1->pn_head = pn2->pn_left; |
2786 | } else |
2787 | #endif |
2788 | { |
2789 | pn4 = pn2->pn_expr; |
2790 | pn2->pn_expr = NULL; |
2791 | } |
2792 | pn3->pn_pos = pn4->pn_pos; |
2793 | pn3->pn_kid = pn4; |
2794 | PN_INIT_LIST_1(pnseq, pn3); |
2795 | } else { |
2796 | /* |
2797 | * All of 'var x = i' is hoisted above 'for (x in o)', |
2798 | * so clear PNX_FORINVAR. |
2799 | * |
2800 | * Request JSOP_POP here since the var is for a simple |
2801 | * name (it is not a destructuring binding's left-hand |
2802 | * side) and it has an initializer. |
2803 | */ |
2804 | pn1->pn_extra &= ~PNX_FORINVAR; |
2805 | pn1->pn_extra |= PNX_POPVAR; |
2806 | PN_INIT_LIST_1(pnseq, pn1); |
2807 | |
2808 | #if JS_HAS_DESTRUCTURING |
2809 | if (pn2->pn_type == TOK_ASSIGN) { |
2810 | pn1 = CloneParseTree(cx, pn2->pn_left, tc); |
2811 | if (!pn1) |
2812 | return NULL; |
2813 | } else |
2814 | #endif |
2815 | { |
2816 | pn1 = NewParseNode(cx, ts, PN_NAME, tc); |
2817 | if (!pn1) |
2818 | return NULL; |
2819 | pn1->pn_type = TOK_NAME; |
2820 | pn1->pn_op = JSOP_NAME; |
2821 | pn1->pn_pos = pn2->pn_pos; |
2822 | pn1->pn_atom = pn2->pn_atom; |
2823 | pn1->pn_expr = NULL; |
2824 | pn1->pn_slot = -1; |
2825 | pn1->pn_const = pn2->pn_const; |
2826 | } |
2827 | pn2 = pn1; |
2828 | } |
2829 | } |
2830 | } |
2831 | |
2832 | if (!pn2) { |
2833 | pn2 = pn1; |
2834 | #if JS_HAS_LVALUE_RETURN |
2835 | if (pn2->pn_type == TOK_LP && |
2836 | !MakeSetCall(cx, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) { |
2837 | return NULL; |
2838 | } |
2839 | #endif |
2840 | #if JS_HAS_XML_SUPPORT |
2841 | if (pn2->pn_type == TOK_UNARYOP) |
2842 | pn2->pn_op = JSOP_BINDXMLNAME; |
2843 | #endif |
2844 | } |
2845 | |
2846 | switch (pn2->pn_type) { |
2847 | case TOK_NAME: |
2848 | /* Beware 'for (arguments in ...)' with or without a 'var'. */ |
2849 | if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom) |
2850 | tc->flags |= TCF_FUN_HEAVYWEIGHT; |
2851 | break; |
2852 | |
2853 | #if JS_HAS_DESTRUCTURING |
2854 | case TOK_ASSIGN: |
2855 | pn2 = pn2->pn_left; |
2856 | JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC); |
2857 | /* FALL THROUGH */ |
2858 | case TOK_RB: |
2859 | case TOK_RC: |
2860 | /* Check for valid lvalues in var-less destructuring for-in. */ |
2861 | if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc)) |
2862 | return NULL; |
2863 | |
2864 | if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) { |
2865 | /* |
2866 | * Destructuring for-in requires [key, value] enumeration |
2867 | * in JS1.7. |
2868 | */ |
2869 | JS_ASSERT(pn->pn_op == JSOP_ITER); |
2870 | if (!(pn->pn_iflags & JSITER_FOREACH)) |
2871 | pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE; |
2872 | } |
2873 | break; |
2874 | #endif |
2875 | |
2876 | default:; |
2877 | } |
2878 | |
2879 | /* Parse the object expression as the right operand of 'in'. */ |
2880 | pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc); |
2881 | if (!pn2) |
2882 | return NULL; |
2883 | pn->pn_left = pn2; |
2884 | } else { |
2885 | if (pn->pn_iflags & JSITER_FOREACH) |
2886 | goto bad_for_each; |
2887 | pn->pn_op = JSOP_NOP; |
2888 | |
2889 | /* Parse the loop condition or null into pn2. */ |
2890 | MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); |
2891 | ts->flags |= TSF_OPERAND; |
2892 | tt = js_PeekToken(cx, ts); |
2893 | ts->flags &= ~TSF_OPERAND; |
2894 | if (tt == TOK_SEMI) { |
2895 | pn2 = NULL; |
2896 | } else { |
2897 | pn2 = Expr(cx, ts, tc); |
2898 | if (!pn2) |
2899 | return NULL; |
2900 | } |
2901 | |
2902 | /* Parse the update expression or null into pn3. */ |
2903 | MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND); |
2904 | ts->flags |= TSF_OPERAND; |
2905 | tt = js_PeekToken(cx, ts); |
2906 | ts->flags &= ~TSF_OPERAND; |
2907 | if (tt == TOK_RP) { |
2908 | pn3 = NULL; |
2909 | } else { |
2910 | pn3 = Expr(cx, ts, tc); |
2911 | if (!pn3) |
2912 | return NULL; |
2913 | } |
2914 | |
2915 | /* Build the FORHEAD node to use as the left kid of pn. */ |
2916 | pn4 = NewParseNode(cx, ts, PN_TERNARY, tc); |
2917 | if (!pn4) |
2918 | return NULL; |
2919 | pn4->pn_type = TOK_FORHEAD; |
2920 | pn4->pn_op = JSOP_NOP; |
2921 | pn4->pn_kid1 = pn1; |
2922 | pn4->pn_kid2 = pn2; |
2923 | pn4->pn_kid3 = pn3; |
2924 | pn->pn_left = pn4; |
2925 | } |
2926 | |
2927 | MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); |
2928 | |
2929 | /* Parse the loop body into pn->pn_right. */ |
2930 | pn2 = Statement(cx, ts, tc); |
2931 | if (!pn2) |
2932 | return NULL; |
2933 | pn->pn_right = pn2; |
2934 | |
2935 | /* Record the absolute line number for source note emission. */ |
2936 | pn->pn_pos.end = pn2->pn_pos.end; |
2937 | |
2938 | #if JS_HAS_BLOCK_SCOPE |
2939 | if (pnlet) { |
2940 | js_PopStatement(tc); |
2941 | pnlet->pn_expr = pn; |
2942 | pn = pnlet; |
2943 | } |
2944 | #endif |
2945 | if (pnseq) { |
2946 | pnseq->pn_pos.end = pn->pn_pos.end; |
2947 | PN_APPEND(pnseq, pn); |
2948 | pn = pnseq; |
2949 | } |
2950 | js_PopStatement(tc); |
2951 | return pn; |
2952 | |
2953 | bad_for_each: |
2954 | js_ReportCompileErrorNumber(cx, ts, pn, JSREPORT_ERROR, |
2955 | JSMSG_BAD_FOR_EACH_LOOP); |
2956 | return NULL; |
2957 | } |
2958 | |
2959 | case TOK_TRY: { |
2960 | JSParseNode *catchList, *lastCatch; |
2961 | |
2962 | /* |
2963 | * try nodes are ternary. |
2964 | * kid1 is the try Statement |
2965 | * kid2 is the catch node list or null |
2966 | * kid3 is the finally Statement |
2967 | * |
2968 | * catch nodes are ternary. |
2969 | * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC) |
2970 | * kid2 is the catch guard or null if no guard |
2971 | * kid3 is the catch block |
2972 | * |
2973 | * catch lvalue nodes are either: |
2974 | * TOK_NAME for a single identifier |
2975 | * TOK_RB or TOK_RC for a destructuring left-hand side |
2976 | * |
2977 | * finally nodes are TOK_LC Statement lists. |
2978 | */ |
2979 | pn = NewParseNode(cx, ts, PN_TERNARY, tc); |
2980 | if (!pn) |
2981 | return NULL; |
2982 | pn->pn_op = JSOP_NOP; |
2983 | |
2984 | MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); |
2985 | js_PushStatement(tc, &stmtInfo, STMT_TRY, -1); |
2986 | pn->pn_kid1 = Statements(cx, ts, tc); |
2987 | if (!pn->pn_kid1) |
2988 | return NULL; |
2989 | MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY); |
2990 | js_PopStatement(tc); |
2991 | |
2992 | catchList = NULL; |
2993 | tt = js_GetToken(cx, ts); |
2994 | if (tt == TOK_CATCH) { |
2995 | catchList = NewParseNode(cx, ts, PN_LIST, tc); |
2996 | if (!catchList) |
2997 | return NULL; |
2998 | catchList->pn_type = TOK_RESERVED; |
2999 | PN_INIT_LIST(catchList); |
3000 | lastCatch = NULL; |
3001 | |
3002 | do { |
3003 | JSParseNode *pnblock; |
3004 | BindData data; |
3005 | |
3006 | /* Check for another catch after unconditional catch. */ |
3007 | if (lastCatch && !lastCatch->pn_kid2) { |
3008 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
3009 | JSMSG_CATCH_AFTER_GENERAL); |
3010 | return NULL; |
3011 | } |
3012 | |
3013 | /* |
3014 | * Create a lexical scope node around the whole catch clause, |
3015 | * including the head. |
3016 | */ |
3017 | pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo); |
3018 | if (!pnblock) |
3019 | return NULL; |
3020 | stmtInfo.type = STMT_CATCH; |
3021 | |
3022 | /* |
3023 | * Legal catch forms are: |
3024 | * catch (lhs) |
3025 | * catch (lhs if <boolean_expression>) |
3026 | * where lhs is a name or a destructuring left-hand side. |
3027 | * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD) |
3028 | */ |
3029 | pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); |
3030 | if (!pn2) |
3031 | return NULL; |
3032 | pnblock->pn_expr = pn2; |
3033 | MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); |
3034 | |
3035 | /* |
3036 | * Contrary to ECMA Ed. 3, the catch variable is lexically |
3037 | * scoped, not a property of a new Object instance. This is |
3038 | * an intentional change that anticipates ECMA Ed. 4. |
3039 | */ |
3040 | data.pn = NULL; |
3041 | data.op = JSOP_NOP; |
3042 | data.binder = BindLet; |
3043 | data.u.let.overflow = JSMSG_TOO_MANY_CATCH_VARS; |
3044 | |
3045 | tt = js_GetToken(cx, ts); |
3046 | switch (tt) { |
3047 | #if JS_HAS_DESTRUCTURING |
3048 | case TOK_LB: |
3049 | case TOK_LC: |
3050 | pn3 = DestructuringExpr(cx, &data, tc, tt); |
3051 | if (!pn3) |
3052 | return NULL; |
3053 | break; |
3054 | #endif |
3055 | |
3056 | case TOK_NAME: |
3057 | label = CURRENT_TOKEN(ts).t_atom; |
3058 | if (!data.binder(cx, &data, label, tc)) |
3059 | return NULL; |
3060 | |
3061 | pn3 = NewParseNode(cx, ts, PN_NAME, tc); |
3062 | if (!pn3) |
3063 | return NULL; |
3064 | pn3->pn_atom = label; |
3065 | pn3->pn_slot = -1; |
3066 | break; |
3067 | |
3068 | default: |
3069 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
3070 | JSMSG_CATCH_IDENTIFIER); |
3071 | return NULL; |
3072 | } |
3073 | |
3074 | pn2->pn_kid1 = pn3; |
3075 | #if JS_HAS_CATCH_GUARD |
3076 | /* |
3077 | * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)') |
3078 | * to avoid conflicting with the JS2/ECMAv4 type annotation |
3079 | * catchguard syntax. |
3080 | */ |
3081 | if (js_MatchToken(cx, ts, TOK_IF)) { |
3082 | pn2->pn_kid2 = Expr(cx, ts, tc); |
3083 | if (!pn2->pn_kid2) |
3084 | return NULL; |
3085 | } |
3086 | #endif |
3087 | MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH); |
3088 | |
3089 | MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); |
3090 | pn2->pn_kid3 = Statements(cx, ts, tc); |
3091 | if (!pn2->pn_kid3) |
3092 | return NULL; |
3093 | MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH); |
3094 | js_PopStatement(tc); |
3095 | |
3096 | PN_APPEND(catchList, pnblock); |
3097 | lastCatch = pn2; |
3098 | ts->flags |= TSF_OPERAND; |
3099 | tt = js_GetToken(cx, ts); |
3100 | ts->flags &= ~TSF_OPERAND; |
3101 | } while (tt == TOK_CATCH); |
3102 | } |
3103 | pn->pn_kid2 = catchList; |
3104 | |
3105 | if (tt == TOK_FINALLY) { |
3106 | MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY); |
3107 | js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1); |
3108 | pn->pn_kid3 = Statements(cx, ts, tc); |
3109 | if (!pn->pn_kid3) |
3110 | return NULL; |
3111 | MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY); |
3112 | js_PopStatement(tc); |
3113 | } else { |
3114 | js_UngetToken(ts); |
3115 | } |
3116 | if (!catchList && !pn->pn_kid3) { |
3117 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
3118 | JSMSG_CATCH_OR_FINALLY); |
3119 | return NULL; |
3120 | } |
3121 | return pn; |
3122 | } |
3123 | |
3124 | case TOK_THROW: |
3125 | pn = NewParseNode(cx, ts, PN_UNARY, tc); |
3126 | if (!pn) |
3127 | return NULL; |
3128 | |
3129 | /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */ |
3130 | ts->flags |= TSF_OPERAND; |
3131 | tt = js_PeekTokenSameLine(cx, ts); |
3132 | ts->flags &= ~TSF_OPERAND; |
3133 | if (tt == TOK_ERROR) |
3134 | return NULL; |
3135 | if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) { |
3136 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
3137 | JSMSG_SYNTAX_ERROR); |
3138 | return NULL; |
3139 | } |
3140 | |
3141 | pn2 = Expr(cx, ts, tc); |
3142 | if (!pn2) |
3143 | return NULL; |
3144 | pn->pn_pos.end = pn2->pn_pos.end; |
3145 | pn->pn_op = JSOP_THROW; |
3146 | pn->pn_kid = pn2; |
3147 | break; |
3148 | |
3149 | /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */ |
3150 | case TOK_CATCH: |
3151 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
3152 | JSMSG_CATCH_WITHOUT_TRY); |
3153 | return NULL; |
3154 | |
3155 | case TOK_FINALLY: |
3156 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
3157 | JSMSG_FINALLY_WITHOUT_TRY); |
3158 | return NULL; |
3159 | |
3160 | case TOK_BREAK: |
3161 | pn = NewParseNode(cx, ts, PN_NULLARY, tc); |
3162 | if (!pn) |
3163 | return NULL; |
3164 | if (!MatchLabel(cx, ts, pn)) |
3165 | return NULL; |
3166 | stmt = tc->topStmt; |
3167 | label = pn->pn_atom; |
3168 | if (label) { |
3169 | for (; ; stmt = stmt->down) { |
3170 | if (!stmt) { |
3171 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
3172 | JSMSG_LABEL_NOT_FOUND); |
3173 | return NULL; |
3174 | } |
3175 | if (stmt->type == STMT_LABEL && stmt->u.label == label) |
3176 | break; |
3177 | } |
3178 | } else { |
3179 | for (; ; stmt = stmt->down) { |
3180 | if (!stmt) { |
3181 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
3182 | JSMSG_TOUGH_BREAK); |
3183 | return NULL; |
3184 | } |
3185 | if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH) |
3186 | break; |
3187 | } |
3188 | } |
3189 | if (label) |
3190 | pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; |
3191 | break; |
3192 | |
3193 | case TOK_CONTINUE: |
3194 | pn = NewParseNode(cx, ts, PN_NULLARY, tc); |
3195 | if (!pn) |
3196 | return NULL; |
3197 | if (!MatchLabel(cx, ts, pn)) |
3198 | return NULL; |
3199 | stmt = tc->topStmt; |
3200 | label = pn->pn_atom; |
3201 | if (label) { |
3202 | for (stmt2 = NULL; ; stmt = stmt->down) { |
3203 | if (!stmt) { |
3204 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
3205 | JSMSG_LABEL_NOT_FOUND); |
3206 | return NULL; |
3207 | } |
3208 | if (stmt->type == STMT_LABEL) { |
3209 | if (stmt->u.label == label) { |
3210 | if (!stmt2 || !STMT_IS_LOOP(stmt2)) { |
3211 | js_ReportCompileErrorNumber(cx, ts, NULL, |
3212 | JSREPORT_ERROR, |
3213 | JSMSG_BAD_CONTINUE); |
3214 | return NULL; |
3215 | } |
3216 | break; |
3217 | } |
3218 | } else { |
3219 | stmt2 = stmt; |
3220 | } |
3221 | } |
3222 | } else { |
3223 | for (; ; stmt = stmt->down) { |
3224 | if (!stmt) { |
3225 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, |
3226 | JSMSG_BAD_CONTINUE); |
3227 | return NULL; |
3228 | } |
3229 | if (STMT_IS_LOOP(stmt)) |
3230 | break; |
3231 | } |
3232 | } |
3233 | if (label) |
3234 | pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; |
3235 | break; |
3236 | |
3237 | case TOK_WITH: |
3238 | pn = NewParseNode(cx, ts, PN_BINARY, tc); |
3239 | if (!pn) |
3240 | return NULL; |
3241 | MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH); |
3242 | pn2 = ParenExpr(cx, ts, tc, NULL, NULL); |
3243 | if (!pn2) |
3244 | return NULL; |
3245 | MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH); |
3246 | pn->pn_left = pn2; |
3247 | |
3248 | js_PushStatement(tc, &stmtInfo, STMT_WITH, -1); |
3249 | pn2 = Statement(cx, ts, tc); |
3250 | if (!pn2) |
3251 | return NULL; |
3252 | js_PopStatement(tc); |
3253 | |
3254 | pn->pn_pos.end = pn2->pn_pos.end; |
3255 | pn->pn_right = pn2; |
3256 | tc->flags |= TCF_FUN_HEAVYWEIGHT; |
3257 | return pn; |
3258 | |
3259 | case TOK_VAR: |
3260 | pn = Variables(cx, ts, tc); |
3261 | if (!pn) |
3262 | return NULL; |
3263 | |
3264 | /* Tell js_EmitTree to generate a final POP. */ |
3265 | pn->pn_extra |= PNX_POPVAR; |
3266 | break; |
3267 | |
3268 | #if JS_HAS_BLOCK_SCOPE |
3269 | case TOK_LET: |
3270 | { |
3271 | JSObject *obj; |
3272 | JSParsedObjectBox *blockpob; |
3273 | |
3274 | /* Check for a let statement or let expression. */ |
3275 | if (js_PeekToken(cx, ts) == TOK_LP) { |
3276 | pn = LetBlock(cx, ts, tc, JS_TRUE); |
3277 | if (!pn || pn->pn_op == JSOP_LEAVEBLOCK) |
3278 | return pn; |
3279 | |
3280 | /* Let expressions require automatic semicolon insertion. */ |
3281 | JS_ASSERT(pn->pn_type == TOK_SEMI || |
3282 | pn->pn_op == JSOP_LEAVEBLOCKEXPR); |
3283 | break; |
3284 | } |
3285 | |
3286 | /* |
3287 | * This is a let declaration. We must be directly under a block per |
3288 | * the proposed ES4 specs, but not an implicit block created due to |
3289 | * 'for (let ...)'. If we pass this error test, make the enclosing |