Parent Directory
|
Revision Log
Use SpiderMonkey from Firefox 3.1b2.
1 | siliconforks | 332 | /* -*- 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 | siliconforks | 399 | 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 | siliconforks | 332 | /* 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 | siliconforks | 399 | * scope node, we must make a special TOK_SEQ node, to prepend the formal |
1314 | siliconforks | 332 | * 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 | siliconforks | 399 | block->pn_type = TOK_SEQ; |
1325 | siliconforks | 332 | 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 | siliconforks | 399 | 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 | siliconforks | 332 | #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 | siliconforks | 399 | // 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 | siliconforks | 332 | #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); | ||