Parent Directory
|
Revision Log
Add SpiderMonkey from Firefox 3.1b1. The following directories and files were removed: correct/, correct.js liveconnect/ nanojit/ t/ v8/ vprof/ xpconnect/ all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)
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 | /* Inline Statements() to emit as we go to save space. */ | ||
547 | for (;;) { | ||
548 | pc.tokenStream.flags |= TSF_OPERAND; | ||
549 | tt = js_PeekToken(cx, &pc.tokenStream); | ||
550 | pc.tokenStream.flags &= ~TSF_OPERAND; | ||
551 | if (tt <= TOK_EOF) { | ||
552 | if (tt == TOK_EOF) | ||
553 | break; | ||
554 | JS_ASSERT(tt == TOK_ERROR); | ||
555 | script = NULL; | ||
556 | goto out; | ||
557 | } | ||
558 | |||
559 | pn = Statement(cx, &pc.tokenStream, &cg.treeContext); | ||
560 | if (!pn) { | ||
561 | script = NULL; | ||
562 | goto out; | ||
563 | } | ||
564 | JS_ASSERT(!cg.treeContext.blockNode); | ||
565 | |||
566 | if (!js_FoldConstants(cx, pn, &cg.treeContext) || | ||
567 | !js_EmitTree(cx, &cg, pn)) { | ||
568 | script = NULL; | ||
569 | goto out; | ||
570 | } | ||
571 | RecycleTree(pn, &cg.treeContext); | ||
572 | } | ||
573 | |||
574 | /* | ||
575 | * Global variables and regexps shares the index space with locals. Due to | ||
576 | * incremental code generation we need to patch the bytecode to adjust the | ||
577 | * local references to skip the globals. | ||
578 | */ | ||
579 | scriptGlobals = cg.treeContext.ngvars + cg.regexpList.length; | ||
580 | if (scriptGlobals != 0) { | ||
581 | jsbytecode *code, *end; | ||
582 | JSOp op; | ||
583 | const JSCodeSpec *cs; | ||
584 | uintN len, slot; | ||
585 | |||
586 | if (scriptGlobals >= SLOTNO_LIMIT) | ||
587 | goto too_many_slots; | ||
588 | code = CG_BASE(&cg); | ||
589 | for (end = code + CG_OFFSET(&cg); code != end; code += len) { | ||
590 | JS_ASSERT(code < end); | ||
591 | op = (JSOp) *code; | ||
592 | cs = &js_CodeSpec[op]; | ||
593 | len = (cs->length > 0) | ||
594 | ? (uintN) cs->length | ||
595 | : js_GetVariableBytecodeLength(code); | ||
596 | if (JOF_TYPE(cs->format) == JOF_LOCAL || | ||
597 | (JOF_TYPE(cs->format) == JOF_SLOTATOM)) { | ||
598 | /* | ||
599 | * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be | ||
600 | * emitted only for a function. | ||
601 | */ | ||
602 | JS_ASSERT((JOF_TYPE(cs->format) == JOF_SLOTATOM) == | ||
603 | (op == JSOP_GETLOCALPROP)); | ||
604 | slot = GET_SLOTNO(code); | ||
605 | slot += scriptGlobals; | ||
606 | if (slot >= SLOTNO_LIMIT) | ||
607 | goto too_many_slots; | ||
608 | SET_SLOTNO(code, slot); | ||
609 | } | ||
610 | } | ||
611 | } | ||
612 | |||
613 | #ifdef METER_PARSENODES | ||
614 | printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n", | ||
615 | (char *)sbrk(0) - (char *)before, | ||
616 | parsenodes, | ||
617 | maxparsenodes, | ||
618 | parsenodes - recyclednodes); | ||
619 | before = sbrk(0); | ||
620 | #endif | ||
621 | |||
622 | /* | ||
623 | * Nowadays the threaded interpreter needs a stop instruction, so we | ||
624 | * do have to emit that here. | ||
625 | */ | ||
626 | if (js_Emit1(cx, &cg, JSOP_STOP) < 0) { | ||
627 | script = NULL; | ||
628 | goto out; | ||
629 | } | ||
630 | #ifdef METER_PARSENODES | ||
631 | printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n", | ||
632 | (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount); | ||
633 | #endif | ||
634 | #ifdef JS_ARENAMETER | ||
635 | JS_DumpArenaStats(stdout); | ||
636 | #endif | ||
637 | script = js_NewScriptFromCG(cx, &cg); | ||
638 | |||
639 | #ifdef JS_SCOPE_DEPTH_METER | ||
640 | if (script) { | ||
641 | JSObject *obj = scopeChain; | ||
642 | uintN depth = 1; | ||
643 | while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) | ||
644 | ++depth; | ||
645 | JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth); | ||
646 | } | ||
647 | #endif | ||
648 | |||
649 | out: | ||
650 | js_FinishCodeGenerator(cx, &cg); | ||
651 | JS_FinishArenaPool(&codePool); | ||
652 | JS_FinishArenaPool(¬ePool); | ||
653 | js_FinishParseContext(cx, &pc); | ||
654 | return script; | ||
655 | |||
656 | too_many_slots: | ||
657 | js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL, | ||
658 | JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS); | ||
659 | script = NULL; | ||
660 | goto out; | ||
661 | } | ||
662 | |||
663 | /* | ||
664 | * Insist on a final return before control flows out of pn. Try to be a bit | ||
665 | * smart about loops: do {...; return e2;} while(0) at the end of a function | ||
666 | * that contains an early return e1 will get a strict warning. Similarly for | ||
667 | * iloops: while (true){...} is treated as though ... returns. | ||
668 | */ | ||
669 | #define ENDS_IN_OTHER 0 | ||
670 | #define ENDS_IN_RETURN 1 | ||
671 | #define ENDS_IN_BREAK 2 | ||
672 | |||
673 | static int | ||
674 | HasFinalReturn(JSParseNode *pn) | ||
675 | { | ||
676 | JSParseNode *pn2, *pn3; | ||
677 | uintN rv, rv2, hasDefault; | ||
678 | |||
679 | switch (pn->pn_type) { | ||
680 | case TOK_LC: | ||
681 | if (!pn->pn_head) | ||
682 | return ENDS_IN_OTHER; | ||
683 | return HasFinalReturn(PN_LAST(pn)); | ||
684 | |||
685 | case TOK_IF: | ||
686 | if (!pn->pn_kid3) | ||
687 | return ENDS_IN_OTHER; | ||
688 | return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3); | ||
689 | |||
690 | case TOK_WHILE: | ||
691 | pn2 = pn->pn_left; | ||
692 | if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE) | ||
693 | return ENDS_IN_RETURN; | ||
694 | if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval) | ||
695 | return ENDS_IN_RETURN; | ||
696 | return ENDS_IN_OTHER; | ||
697 | |||
698 | case TOK_DO: | ||
699 | pn2 = pn->pn_right; | ||
700 | if (pn2->pn_type == TOK_PRIMARY) { | ||
701 | if (pn2->pn_op == JSOP_FALSE) | ||
702 | return HasFinalReturn(pn->pn_left); | ||
703 | if (pn2->pn_op == JSOP_TRUE) | ||
704 | return ENDS_IN_RETURN; | ||
705 | } | ||
706 | if (pn2->pn_type == TOK_NUMBER) { | ||
707 | if (pn2->pn_dval == 0) | ||
708 | return HasFinalReturn(pn->pn_left); | ||
709 | return ENDS_IN_RETURN; | ||
710 | } | ||
711 | return ENDS_IN_OTHER; | ||
712 | |||
713 | case TOK_FOR: | ||
714 | pn2 = pn->pn_left; | ||
715 | if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2) | ||
716 | return ENDS_IN_RETURN; | ||
717 | return ENDS_IN_OTHER; | ||
718 | |||
719 | case TOK_SWITCH: | ||
720 | rv = ENDS_IN_RETURN; | ||
721 | hasDefault = ENDS_IN_OTHER; | ||
722 | pn2 = pn->pn_right; | ||
723 | if (pn2->pn_type == TOK_LEXICALSCOPE) | ||
724 | pn2 = pn2->pn_expr; | ||
725 | for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) { | ||
726 | if (pn2->pn_type == TOK_DEFAULT) | ||
727 | hasDefault = ENDS_IN_RETURN; | ||
728 | pn3 = pn2->pn_right; | ||
729 | JS_ASSERT(pn3->pn_type == TOK_LC); | ||
730 | if (pn3->pn_head) { | ||
731 | rv2 = HasFinalReturn(PN_LAST(pn3)); | ||
732 | if (rv2 == ENDS_IN_OTHER && pn2->pn_next) | ||
733 | /* Falling through to next case or default. */; | ||
734 | else | ||
735 | rv &= rv2; | ||
736 | } | ||
737 | } | ||
738 | /* If a final switch has no default case, we judge it harshly. */ | ||
739 | rv &= hasDefault; | ||
740 | return rv; | ||
741 | |||
742 | case TOK_BREAK: | ||
743 | return ENDS_IN_BREAK; | ||
744 | |||
745 | case TOK_WITH: | ||
746 | return HasFinalReturn(pn->pn_right); | ||
747 | |||
748 | case TOK_RETURN: | ||
749 | return ENDS_IN_RETURN; | ||
750 | |||
751 | case TOK_COLON: | ||
752 | case TOK_LEXICALSCOPE: | ||
753 | return HasFinalReturn(pn->pn_expr); | ||
754 | |||
755 | case TOK_THROW: | ||
756 | return ENDS_IN_RETURN; | ||
757 | |||
758 | case TOK_TRY: | ||
759 | /* If we have a finally block that returns, we are done. */ | ||
760 | if (pn->pn_kid3) { | ||
761 | rv = HasFinalReturn(pn->pn_kid3); | ||
762 | if (rv == ENDS_IN_RETURN) | ||
763 | return rv; | ||
764 | } | ||
765 | |||
766 | /* Else check the try block and any and all catch statements. */ | ||
767 | rv = HasFinalReturn(pn->pn_kid1); | ||
768 | if (pn->pn_kid2) { | ||
769 | JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST); | ||
770 | for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next) | ||
771 | rv &= HasFinalReturn(pn2); | ||
772 | } | ||
773 | return rv; | ||
774 | |||
775 | case TOK_CATCH: | ||
776 | /* Check this catch block's body. */ | ||
777 | return HasFinalReturn(pn->pn_kid3); | ||
778 | |||
779 | case TOK_LET: | ||
780 | /* Non-binary let statements are let declarations. */ | ||
781 | if (pn->pn_arity != PN_BINARY) | ||
782 | return ENDS_IN_OTHER; | ||
783 | return HasFinalReturn(pn->pn_right); | ||
784 | |||
785 | default: | ||
786 | return ENDS_IN_OTHER; | ||
787 | } | ||
788 | } | ||
789 | |||
790 | static JSBool | ||
791 | ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum, | ||
792 | uintN anonerrnum) | ||
793 | { | ||
794 | const char *name; | ||
795 | |||
796 | JS_ASSERT(tc->flags & TCF_IN_FUNCTION); | ||
797 | if (tc->u.fun->atom) { | ||
798 | name = js_AtomToPrintableString(cx, tc->u.fun->atom); | ||
799 | } else { | ||
800 | errnum = anonerrnum; | ||
801 | name = NULL; | ||
802 | } | ||
803 | return js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, flags, | ||
804 | errnum, name); | ||
805 | } | ||
806 | |||
807 | static JSBool | ||
808 | CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn) | ||
809 | { | ||
810 | JS_ASSERT(tc->flags & TCF_IN_FUNCTION); | ||
811 | return HasFinalReturn(pn) == ENDS_IN_RETURN || | ||
812 | ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT, | ||
813 | JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE); | ||
814 | } | ||
815 | |||
816 | static JSParseNode * | ||
817 | FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) | ||
818 | { | ||
819 | JSStmtInfo stmtInfo; | ||
820 | uintN oldflags, firstLine; | ||
821 | JSParseNode *pn; | ||
822 | |||
823 | JS_ASSERT(tc->flags & TCF_IN_FUNCTION); | ||
824 | js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); | ||
825 | stmtInfo.flags = SIF_BODY_BLOCK; | ||
826 | |||
827 | oldflags = tc->flags; | ||
828 | tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID); | ||
829 | |||
830 | /* | ||
831 | * Save the body's first line, and store it in pn->pn_pos.begin.lineno | ||
832 | * later, because we may have not peeked in ts yet, so Statements won't | ||
833 | * acquire a valid pn->pn_pos.begin from the current token. | ||
834 | */ | ||
835 | firstLine = ts->lineno; | ||
836 | #if JS_HAS_EXPR_CLOSURES | ||
837 | if (CURRENT_TOKEN(ts).type == TOK_LC) { | ||
838 | pn = Statements(cx, ts, tc); | ||
839 | } else { | ||
840 | pn = NewParseNode(cx, ts, PN_UNARY, tc); | ||
841 | if (pn) { | ||
842 | pn->pn_kid = AssignExpr(cx, ts, tc); | ||
843 | if (!pn->pn_kid) { | ||
844 | pn = NULL; | ||
845 | } else { | ||
846 | if (tc->flags & TCF_FUN_IS_GENERATOR) { | ||
847 | ReportBadReturn(cx, tc, JSREPORT_ERROR, | ||
848 | JSMSG_BAD_GENERATOR_RETURN, | ||
849 | JSMSG_BAD_ANON_GENERATOR_RETURN); | ||
850 | pn = NULL; | ||
851 | } else { | ||
852 | pn->pn_type = TOK_RETURN; | ||
853 | pn->pn_op = JSOP_RETURN; | ||
854 | pn->pn_pos.end = pn->pn_kid->pn_pos.end; | ||
855 | } | ||
856 | } | ||
857 | } | ||
858 | } | ||
859 | #else | ||
860 | pn = Statements(cx, ts, tc); | ||
861 | #endif | ||
862 | |||
863 | if (pn) { | ||
864 | js_PopStatement(tc); | ||
865 | pn->pn_pos.begin.lineno = firstLine; | ||
866 | |||
867 | /* Check for falling off the end of a function that returns a value. */ | ||
868 | if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) && | ||
869 | !CheckFinalReturn(cx, tc, pn)) { | ||
870 | pn = NULL; | ||
871 | } | ||
872 | } | ||
873 | |||
874 | tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS)); | ||
875 | return pn; | ||
876 | } | ||
877 | |||
878 | /* | ||
879 | * Compile a JS function body, which might appear as the value of an event | ||
880 | * handler attribute in an HTML <INPUT> tag. | ||
881 | */ | ||
882 | JSBool | ||
883 | js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals, | ||
884 | const jschar *chars, size_t length, | ||
885 | const char *filename, uintN lineno) | ||
886 | { | ||
887 | JSParseContext pc; | ||
888 | JSArenaPool codePool, notePool; | ||
889 | JSCodeGenerator funcg; | ||
890 | JSParseNode *pn; | ||
891 | |||
892 | if (!js_InitParseContext(cx, &pc, principals, NULL, chars, length, NULL, | ||
893 | filename, lineno)) { | ||
894 | return JS_FALSE; | ||
895 | } | ||
896 | |||
897 | /* No early return from this point until js_FinishParseContext call. */ | ||
898 | JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode), | ||
899 | &cx->scriptStackQuota); | ||
900 | JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote), | ||
901 | &cx->scriptStackQuota); | ||
902 | js_InitCodeGenerator(cx, &funcg, &pc, &codePool, ¬ePool, | ||
903 | pc.tokenStream.lineno); | ||
904 | funcg.treeContext.flags |= TCF_IN_FUNCTION; | ||
905 | funcg.treeContext.u.fun = fun; | ||
906 | |||
907 | /* | ||
908 | * Farble the body so that it looks like a block statement to js_EmitTree, | ||
909 | * which is called beneath FunctionBody; see Statements, further below in | ||
910 | * this file. FunctionBody pushes a STMT_BLOCK record around its call to | ||
911 | * Statements, so Statements will not compile each statement as it loops | ||
912 | * to save JSParseNode space -- it will not compile at all, only build a | ||
913 | * JSParseNode tree. | ||
914 | * | ||
915 | * Therefore we must fold constants, allocate try notes, and generate code | ||
916 | * for this function, including a stop opcode at the end. | ||
917 | */ | ||
918 | CURRENT_TOKEN(&pc.tokenStream).type = TOK_LC; | ||
919 | pn = FunctionBody(cx, &pc.tokenStream, &funcg.treeContext); | ||
920 | if (pn) { | ||
921 | if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) { | ||
922 | js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL, | ||
923 | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); | ||
924 | pn = NULL; | ||
925 | } else { | ||
926 | if (!js_FoldConstants(cx, pn, &funcg.treeContext) || | ||
927 | !js_EmitFunctionScript(cx, &funcg, pn)) { | ||
928 | pn = NULL; | ||
929 | } | ||
930 | } | ||
931 | } | ||
932 | |||
933 | /* Restore saved state and release code generation arenas. */ | ||
934 | js_FinishCodeGenerator(cx, &funcg); | ||
935 | JS_FinishArenaPool(&codePool); | ||
936 | JS_FinishArenaPool(¬ePool); | ||
937 | js_FinishParseContext(cx, &pc); | ||
938 | return pn != NULL; | ||
939 | } | ||
940 | |||
941 | /* | ||
942 | * Parameter block types for the several Binder functions. We use a common | ||
943 | * helper function signature in order to share code among destructuring and | ||
944 | * simple variable declaration parsers. In the destructuring case, the binder | ||
945 | * function is called indirectly from the variable declaration parser by way | ||
946 | * of CheckDestructuring and its friends. | ||
947 | */ | ||
948 | typedef struct BindData BindData; | ||
949 | |||
950 | typedef JSBool | ||
951 | (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc); | ||
952 | |||
953 | struct BindData { | ||
954 | JSParseNode *pn; /* error source coordinate */ | ||
955 | JSOp op; /* prolog bytecode or nop */ | ||
956 | Binder binder; /* binder, discriminates u */ | ||
957 | union { | ||
958 | struct { | ||
959 | uintN overflow; | ||
960 | } let; | ||
961 | } u; | ||
962 | }; | ||
963 | |||
964 | static JSBool | ||
965 | BindArg(JSContext *cx, JSAtom *atom, JSTreeContext *tc) | ||
966 | { | ||
967 | const char *name; | ||
968 | |||
969 | /* | ||
970 | * Check for a duplicate parameter name, a "feature" required by ECMA-262. | ||
971 | */ | ||
972 | JS_ASSERT(tc->flags & TCF_IN_FUNCTION); | ||
973 | if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) { | ||
974 | name = js_AtomToPrintableString(cx, atom); | ||
975 | if (!name || | ||
976 | !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, | ||
977 | JSREPORT_WARNING | JSREPORT_STRICT, | ||
978 | JSMSG_DUPLICATE_FORMAL, | ||
979 | name)) { | ||
980 | return JS_FALSE; | ||
981 | } | ||
982 | } | ||
983 | |||
984 | return js_AddLocal(cx, tc->u.fun, atom, JSLOCAL_ARG); | ||
985 | } | ||
986 | |||
987 | static JSBool | ||
988 | BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom, | ||
989 | JSLocalKind localKind) | ||
990 | { | ||
991 | JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); | ||
992 | |||
993 | /* | ||
994 | * Don't bind a variable with the hidden name 'arguments', per ECMA-262. | ||
995 | * Instead 'var arguments' always restates the predefined property of the | ||
996 | * activation objects with unhidden name 'arguments'. Assignment to such | ||
997 | * a variable must be handled specially. | ||
998 | */ | ||
999 | if (atom == cx->runtime->atomState.argumentsAtom) | ||
1000 | return JS_TRUE; | ||
1001 | |||
1002 | return js_AddLocal(cx, fun, atom, localKind); | ||
1003 | } | ||
1004 | |||
1005 | #if JS_HAS_DESTRUCTURING | ||
1006 | /* | ||
1007 | * Forward declaration to maintain top-down presentation. | ||
1008 | */ | ||
1009 | static JSParseNode * | ||
1010 | DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, | ||
1011 | JSTokenType tt); | ||
1012 | |||
1013 | static JSBool | ||
1014 | BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, | ||
1015 | JSTreeContext *tc) | ||
1016 | { | ||
1017 | JSAtomListElement *ale; | ||
1018 | const char *name; | ||
1019 | |||
1020 | JS_ASSERT(tc->flags & TCF_IN_FUNCTION); | ||
1021 | ATOM_LIST_SEARCH(ale, &tc->decls, atom); | ||
1022 | if (!ale) { | ||
1023 | ale = js_IndexAtom(cx, atom, &tc->decls); | ||
1024 | if (!ale) | ||
1025 | return JS_FALSE; | ||
1026 | ALE_SET_JSOP(ale, data->op); | ||
1027 | } | ||
1028 | |||
1029 | if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) { | ||
1030 | name = js_AtomToPrintableString(cx, atom); | ||
1031 | if (!name || | ||
1032 | !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, | ||
1033 | JSREPORT_WARNING | JSREPORT_STRICT, | ||
1034 | JSMSG_DUPLICATE_FORMAL, | ||
1035 | name)) { | ||
1036 | return JS_FALSE; | ||
1037 | } | ||
1038 | } else { | ||
1039 | if (!BindLocalVariable(cx, tc->u.fun, atom, JSLOCAL_VAR)) | ||
1040 | return JS_FALSE; | ||
1041 | } | ||
1042 | return JS_TRUE; | ||
1043 | } | ||
1044 | #endif /* JS_HAS_DESTRUCTURING */ | ||
1045 | |||
1046 | static JSFunction * | ||
1047 | NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom, | ||
1048 | uintN lambda) | ||
1049 | { | ||
1050 | JSObject *parent; | ||
1051 | JSFunction *fun; | ||
1052 | |||
1053 | JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0); | ||
1054 | parent = (tc->flags & TCF_IN_FUNCTION) | ||
1055 | ? FUN_OBJECT(tc->u.fun) | ||
1056 | : tc->u.scopeChain; | ||
1057 | fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED | lambda, | ||
1058 | parent, atom); | ||
1059 | if (fun && !(tc->flags & TCF_COMPILE_N_GO)) { | ||
1060 | STOBJ_CLEAR_PARENT(FUN_OBJECT(fun)); | ||
1061 | STOBJ_CLEAR_PROTO(FUN_OBJECT(fun)); | ||
1062 | } | ||
1063 | return fun; | ||
1064 | } | ||
1065 | |||
1066 | static JSParseNode * | ||
1067 | FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, | ||
1068 | uintN lambda) | ||
1069 | { | ||
1070 | JSOp op, prevop; | ||
1071 | JSParseNode *pn, *body, *result; | ||
1072 | JSTokenType tt; | ||
1073 | JSAtom *funAtom; | ||
1074 | JSParsedObjectBox *funpob; | ||
1075 | JSAtomListElement *ale; | ||
1076 | JSFunction *fun; | ||
1077 | JSTreeContext funtc; | ||
1078 | #if JS_HAS_DESTRUCTURING | ||
1079 | JSParseNode *item, *list = NULL; | ||
1080 | #endif | ||
1081 | |||
1082 | /* Make a TOK_FUNCTION node. */ | ||
1083 | #if JS_HAS_GETTER_SETTER | ||
1084 | op = CURRENT_TOKEN(ts).t_op; | ||
1085 | #endif | ||
1086 | pn = NewParseNode(cx, ts, PN_FUNC, tc); | ||
1087 | if (!pn) | ||
1088 | return NULL; | ||
1089 | #ifdef DEBUG | ||
1090 | pn->pn_index = (uint32) -1; | ||
1091 | #endif | ||
1092 | |||
1093 | /* Scan the optional function name into funAtom. */ | ||
1094 | ts->flags |= TSF_KEYWORD_IS_NAME; | ||
1095 | tt = js_GetToken(cx, ts); | ||
1096 | ts->flags &= ~TSF_KEYWORD_IS_NAME; | ||
1097 | if (tt == TOK_NAME) { | ||
1098 | funAtom = CURRENT_TOKEN(ts).t_atom; | ||
1099 | } else { | ||
1100 | if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) { | ||
1101 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, | ||
1102 | JSMSG_SYNTAX_ERROR); | ||
1103 | return NULL; | ||
1104 | } | ||
1105 | funAtom = NULL; | ||
1106 | js_UngetToken(ts); | ||
1107 | } | ||
1108 | |||
1109 | /* | ||
1110 | * Record names for function statements in tc->decls so we know when to | ||
1111 | * avoid optimizing variable references that might name a function. | ||
1112 | */ | ||
1113 | if (lambda == 0 && funAtom) { | ||
1114 | ATOM_LIST_SEARCH(ale, &tc->decls, funAtom); | ||
1115 | if (ale) { | ||
1116 | prevop = ALE_JSOP(ale); | ||
1117 | if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) { | ||
1118 | const char *name = js_AtomToPrintableString(cx, funAtom); | ||
1119 | if (!name || | ||
1120 | !js_ReportCompileErrorNumber(cx, ts, NULL, | ||
1121 | (prevop != JSOP_DEFCONST) | ||
1122 | ? JSREPORT_WARNING | | ||
1123 | JSREPORT_STRICT | ||
1124 | : JSREPORT_ERROR, | ||
1125 | JSMSG_REDECLARED_VAR, | ||
1126 | (prevop == JSOP_DEFFUN) | ||
1127 | ? js_function_str | ||
1128 | : (prevop == JSOP_DEFCONST) | ||
1129 | ? js_const_str | ||
1130 | : js_var_str, | ||
1131 | name)) { | ||
1132 | return NULL; | ||
1133 | } | ||
1134 | } | ||
1135 | if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR) | ||
1136 | tc->flags |= TCF_FUN_CLOSURE_VS_VAR; | ||
1137 | } else { | ||
1138 | ale = js_IndexAtom(cx, funAtom, &tc->decls); | ||
1139 | if (!ale) | ||
1140 | return NULL; | ||
1141 | } | ||
1142 | ALE_SET_JSOP(ale, JSOP_DEFFUN); | ||
1143 | |||
1144 | /* | ||
1145 | * A function nested at top level inside another's body needs only a | ||
1146 | * local variable to bind its name to its value, and not an activation | ||
1147 | * object property (it might also need the activation property, if the | ||
1148 | * outer function contains with statements, e.g., but the stack slot | ||
1149 | * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a | ||
1150 | * JSOP_GETLOCAL bytecode). | ||
1151 | */ | ||
1152 | if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) { | ||
1153 | JSLocalKind localKind; | ||
1154 | |||
1155 | /* | ||
1156 | * Define a property on the outer function so that BindNameToSlot | ||
1157 | * can properly optimize accesses. Note that we need a variable, | ||
1158 | * not an argument, for the function statement. Thus we add a | ||
1159 | * variable even if the parameter with the given name already | ||
1160 | * exists. | ||
1161 | */ | ||
1162 | localKind = js_LookupLocal(cx, tc->u.fun, funAtom, NULL); | ||
1163 | if (localKind == JSLOCAL_NONE || localKind == JSLOCAL_ARG) { | ||
1164 | if (!js_AddLocal(cx, tc->u.fun, funAtom, JSLOCAL_VAR)) | ||
1165 | return NULL; | ||
1166 | } | ||
1167 | } | ||
1168 | } | ||
1169 | |||
1170 | fun = NewCompilerFunction(cx, tc, funAtom, lambda); | ||
1171 | if (!fun) | ||
1172 | return NULL; | ||
1173 | |||
1174 | #if JS_HAS_GETTER_SETTER | ||
1175 | if (op != JSOP_NOP) | ||
1176 | fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER; | ||
1177 | #endif | ||
1178 | |||
1179 | /* | ||
1180 | * Create wrapping box for fun->object early to protect against a | ||
1181 | * last-ditch GC. | ||
1182 | */ | ||
1183 | funpob = js_NewParsedObjectBox(cx, tc->parseContext, FUN_OBJECT(fun)); | ||
1184 | if (!funpob) | ||
1185 | return NULL; | ||
1186 | |||
1187 | /* Initialize early for possible flags mutation via DestructuringExpr. */ | ||
1188 | TREE_CONTEXT_INIT(&funtc, tc->parseContext); | ||
1189 | funtc.flags |= TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO); | ||
1190 | funtc.u.fun = fun; | ||
1191 | |||
1192 | /* Now parse formal argument list and compute fun->nargs. */ | ||
1193 | MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL); | ||
1194 | if (!js_MatchToken(cx, ts, TOK_RP)) { | ||
1195 | do { | ||
1196 | tt = js_GetToken(cx, ts); | ||
1197 | switch (tt) { | ||
1198 | #if JS_HAS_DESTRUCTURING | ||
1199 | case TOK_LB: | ||
1200 | case TOK_LC: | ||
1201 | { | ||
1202 | BindData data; | ||
1203 | JSParseNode *lhs, *rhs; | ||
1204 | jsint slot; | ||
1205 | |||
1206 | /* | ||
1207 | * A destructuring formal parameter turns into one or more | ||
1208 | * local variables initialized from properties of a single | ||
1209 | * anonymous positional parameter, so here we must tweak our | ||
1210 | * binder and its data. | ||
1211 | */ | ||
1212 | data.pn = NULL; | ||
1213 | data.op = JSOP_DEFVAR; | ||
1214 | data.binder = BindDestructuringArg; | ||
1215 | lhs = DestructuringExpr(cx, &data, &funtc, tt); | ||
1216 | if (!lhs) | ||
1217 | return NULL; | ||
1218 | |||
1219 | /* | ||
1220 | * Adjust fun->nargs to count the single anonymous positional | ||
1221 | * parameter that is to be destructured. | ||
1222 | */ | ||
1223 | slot = fun->nargs; | ||
1224 | if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG)) | ||
1225 | return NULL; | ||
1226 | |||
1227 | /* | ||
1228 | * Synthesize a destructuring assignment from the single | ||
1229 | * anonymous positional parameter into the destructuring | ||
1230 | * left-hand-side expression and accumulate it in list. | ||
1231 | */ | ||
1232 | rhs = NewParseNode(cx, ts, PN_NAME, tc); | ||
1233 | if (!rhs) | ||
1234 | return NULL; | ||
1235 | rhs->pn_type = TOK_NAME; | ||
1236 | rhs->pn_op = JSOP_GETARG; | ||
1237 | rhs->pn_atom = cx->runtime->atomState.emptyAtom; | ||
1238 | rhs->pn_slot = slot; | ||
1239 | |||
1240 | item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc); | ||
1241 | if (!item) | ||
1242 | return NULL; | ||
1243 | if (!list) { | ||
1244 | list = NewParseNode(cx, ts, PN_LIST, tc); | ||
1245 | if (!list) | ||
1246 | return NULL; | ||
1247 | list->pn_type = TOK_COMMA; | ||
1248 | PN_INIT_LIST(list); | ||
1249 | } | ||
1250 | PN_APPEND(list, item); | ||
1251 | break; | ||
1252 | } | ||
1253 | #endif /* JS_HAS_DESTRUCTURING */ | ||
1254 | |||
1255 | case TOK_NAME: | ||
1256 | if (!BindArg(cx, CURRENT_TOKEN(ts).t_atom, &funtc)) | ||
1257 | return NULL; | ||
1258 | break; | ||
1259 | |||
1260 | default: | ||
1261 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, | ||
1262 | JSMSG_MISSING_FORMAL); | ||
1263 | return NULL; | ||
1264 | } | ||
1265 | } while (js_MatchToken(cx, ts, TOK_COMMA)); | ||
1266 | |||
1267 | MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL); | ||
1268 | } | ||
1269 | |||
1270 | #if JS_HAS_EXPR_CLOSURES | ||
1271 | ts->flags |= TSF_OPERAND; | ||
1272 | tt = js_GetToken(cx, ts); | ||
1273 | ts->flags &= ~TSF_OPERAND; | ||
1274 | if (tt != TOK_LC) { | ||
1275 | js_UngetToken(ts); | ||
1276 | fun->flags |= JSFUN_EXPR_CLOSURE; | ||
1277 | } | ||
1278 | #else | ||
1279 | MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY); | ||
1280 | #endif | ||
1281 | pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin; | ||
1282 | |||
1283 | body = FunctionBody(cx, ts, &funtc); | ||
1284 | if (!body) | ||
1285 | return NULL; | ||
1286 | |||
1287 | #if JS_HAS_EXPR_CLOSURES | ||
1288 | if (tt == TOK_LC) | ||
1289 | MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY); | ||
1290 | else if (lambda == 0) | ||
1291 | js_MatchToken(cx, ts, TOK_SEMI); | ||
1292 | #else | ||
1293 | MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY); | ||
1294 | #endif | ||
1295 | pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; | ||
1296 | |||
1297 | #if JS_HAS_DESTRUCTURING | ||
1298 | /* | ||
1299 | * If there were destructuring formal parameters, prepend the initializing | ||
1300 | * comma expression that we synthesized to body. If the body is a lexical | ||
1301 | * scope node, we must make a special TOK_BODY node, to prepend the formal | ||
1302 | * parameter destructuring code without bracing the decompilation of the | ||
1303 | * function body's lexical scope. | ||
1304 | */ | ||
1305 | if (list) { | ||
1306 | if (body->pn_arity != PN_LIST) { | ||
1307 | JSParseNode *block; | ||
1308 | |||
1309 | block = NewParseNode(cx, ts, PN_LIST, tc); | ||
1310 | if (!block) | ||
1311 | return NULL; | ||
1312 | block->pn_type = TOK_BODY; | ||
1313 | block->pn_pos = body->pn_pos; | ||
1314 | PN_INIT_LIST_1(block, body); | ||
1315 | |||
1316 | body = block; | ||
1317 | } | ||
1318 | |||
1319 | item = NewParseNode(cx, ts, PN_UNARY, tc); | ||
1320 | if (!item) | ||
1321 | return NULL; | ||
1322 | |||
1323 | item->pn_type = TOK_SEMI; | ||
1324 | item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin; | ||
1325 | item->pn_kid = list; | ||
1326 | item->pn_next = body->pn_head; | ||
1327 | body->pn_head = item; | ||
1328 | if (body->pn_tail == &body->pn_head) | ||
1329 | body->pn_tail = &item->pn_next; | ||
1330 | ++body->pn_count; | ||
1331 | } | ||
1332 | #endif | ||
1333 | |||
1334 | /* | ||
1335 | * If we collected flags that indicate nested heavyweight functions, or | ||
1336 | * this function contains heavyweight-making statements (references to | ||
1337 | * __parent__ or __proto__; use of with, or eval; and assignment to | ||
1338 | * arguments), flag the function as heavyweight (requiring a call object | ||
1339 | * per invocation). | ||
1340 | */ | ||
1341 | if (funtc.flags & TCF_FUN_HEAVYWEIGHT) { | ||
1342 | fun->flags |= JSFUN_HEAVYWEIGHT; | ||
1343 | tc->flags |= TCF_FUN_HEAVYWEIGHT; | ||
1344 | } else { | ||
1345 | /* | ||
1346 | * If this function is a named statement function not at top-level | ||
1347 | * (i.e. not a top-level function definiton or expression), then | ||
1348 | * our enclosing function, if any, must be heavyweight. | ||
1349 | * | ||
1350 | * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator, | ||
1351 | * so it won't be set here. Assert that it's not. We have to check | ||
1352 | * it later, in js_EmitTree, after js_EmitFunctionScript has traversed | ||
1353 | * the function's body. | ||
1354 | */ | ||
1355 | JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS)); | ||
1356 | if (lambda == 0 && funAtom && !AT_TOP_LEVEL(tc)) | ||
1357 | tc->flags |= TCF_FUN_HEAVYWEIGHT; | ||
1358 | } | ||
1359 | |||
1360 | result = pn; | ||
1361 | if (lambda != 0) { | ||
1362 | /* | ||
1363 | * ECMA ed. 3 standard: function expression, possibly anonymous. | ||
1364 | */ | ||
1365 | op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ; | ||
1366 | } else if (!funAtom) { | ||
1367 | /* | ||
1368 | * If this anonymous function definition is *not* embedded within a | ||
1369 | * larger expression, we treat it as an expression statement, not as | ||
1370 | * a function declaration -- and not as a syntax error (as ECMA-262 | ||
1371 | * Edition 3 would have it). Backward compatibility must trump all, | ||
1372 | * unless JSOPTION_ANONFUNFIX is set. | ||
1373 | */ | ||
1374 | result = NewParseNode(cx, ts, PN_UNARY, tc); | ||
1375 | if (!result) | ||
1376 | return NULL; | ||
1377 | result->pn_type = TOK_SEMI; | ||
1378 | result->pn_pos = pn->pn_pos; | ||
1379 | result->pn_kid = pn; | ||
1380 | op = JSOP_ANONFUNOBJ; | ||
1381 | } else if (!AT_TOP_LEVEL(tc)) { | ||
1382 | /* | ||
1383 | * ECMA ed. 3 extension: a function expression statement not at the | ||
1384 | * top level, e.g., in a compound statement such as the "then" part | ||
1385 | * of an "if" statement, binds a closure only if control reaches that | ||
1386 | * sub-statement. | ||
1387 | */ | ||
1388 | op = JSOP_DEFFUN; | ||
1389 | } else { | ||
1390 | op = JSOP_NOP; | ||
1391 | } | ||
1392 | |||
1393 | pn->pn_funpob = funpob; | ||
1394 | pn->pn_op = op; | ||
1395 | pn->pn_body = body; | ||
1396 | pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS | TCF_COMPILE_N_GO); | ||
1397 | TREE_CONTEXT_FINISH(cx, &funtc); | ||
1398 | return result; | ||
1399 | } | ||
1400 | |||
1401 | static JSParseNode * | ||
1402 | FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) | ||
1403 | { | ||
1404 | return FunctionDef(cx, ts, tc, 0); | ||
1405 | } | ||
1406 | |||
1407 | static JSParseNode * | ||
1408 | FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) | ||
1409 | { | ||
1410 | return FunctionDef(cx, ts, tc, JSFUN_LAMBDA); | ||
1411 | } | ||
1412 | |||
1413 | /* | ||
1414 | * Parse the statements in a block, creating a TOK_LC node that lists the | ||
1415 | * statements' trees. If called from block-parsing code, the caller must | ||
1416 | * match { before and } after. | ||
1417 | */ | ||
1418 | static JSParseNode * | ||
1419 | Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) | ||
1420 | { | ||
1421 | JSParseNode *pn, *pn2, *saveBlock; | ||
1422 | JSTokenType tt; | ||
1423 | |||
1424 | JS_CHECK_RECURSION(cx, return NULL); | ||
1425 | |||
1426 | pn = NewParseNode(cx, ts, PN_LIST, tc); | ||
1427 | if (!pn) | ||
1428 | return NULL; | ||
1429 | saveBlock = tc->blockNode; | ||
1430 | tc->blockNode = pn; | ||
1431 | PN_INIT_LIST(pn); | ||
1432 | |||
1433 | for (;;) { | ||
1434 | ts->flags |= TSF_OPERAND; | ||
1435 | tt = js_PeekToken(cx, ts); | ||
1436 | ts->flags &= ~TSF_OPERAND; | ||
1437 | if (tt <= TOK_EOF || tt == TOK_RC) { | ||
1438 | if (tt == TOK_ERROR) | ||
1439 | return NULL; | ||
1440 | break; | ||
1441 | } | ||
1442 | pn2 = Statement(cx, ts, tc); | ||
1443 | if (!pn2) { | ||
1444 | if (ts->flags & TSF_EOF) | ||
1445 | ts->flags |= TSF_UNEXPECTED_EOF; | ||
1446 | return NULL; | ||
1447 | } | ||
1448 | |||
1449 | if (pn2->pn_type == TOK_FUNCTION) { | ||
1450 | /* | ||
1451 | * PNX_FUNCDEFS notifies the emitter that the block contains top- | ||
1452 | * level function definitions that should be processed before the | ||
1453 | * rest of nodes. | ||
1454 | * | ||
1455 | * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It | ||
1456 | * is relevant only for function definitions not at top-level, | ||
1457 | * which we call function statements. | ||
1458 | */ | ||
1459 | if (AT_TOP_LEVEL(tc)) | ||
1460 | pn->pn_extra |= PNX_FUNCDEFS; | ||
1461 | else | ||
1462 | tc->flags |= TCF_HAS_FUNCTION_STMT; | ||
1463 | } | ||
1464 | PN_APPEND(pn, pn2); | ||
1465 | } | ||
1466 | |||
1467 | /* | ||
1468 | * Handle the case where there was a let declaration under this block. If | ||
1469 | * it replaced tc->blockNode with a new block node then we must refresh pn | ||
1470 | * and then restore tc->blockNode. | ||
1471 | */ | ||
1472 | if (tc->blockNode != pn) | ||
1473 | pn = tc->blockNode; | ||
1474 | tc->blockNode = saveBlock; | ||
1475 | |||
1476 | pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; | ||
1477 | return pn; | ||
1478 | } | ||
1479 | |||
1480 | static JSParseNode * | ||
1481 | Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) | ||
1482 | { | ||
1483 | JSParseNode *pn; | ||
1484 | |||
1485 | MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND); | ||
1486 | pn = ParenExpr(cx, ts, tc, NULL, NULL); | ||
1487 | if (!pn) | ||
1488 | return NULL; | ||
1489 | MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); | ||
1490 | |||
1491 | /* | ||
1492 | * Check for (a = b) and warn about possible (a == b) mistype iff b's | ||
1493 | * operator has greater precedence than ==. | ||
1494 | */ | ||
1495 | if (pn->pn_type == TOK_ASSIGN && | ||
1496 | pn->pn_op == JSOP_NOP && | ||
1497 | pn->pn_right->pn_type > TOK_EQOP) | ||
1498 | { | ||
1499 | if (!js_ReportCompileErrorNumber(cx, ts, NULL, | ||
1500 | JSREPORT_WARNING | JSREPORT_STRICT, | ||
1501 | JSMSG_EQUAL_AS_ASSIGN, | ||
1502 | "")) { | ||
1503 | return NULL; | ||
1504 | } | ||
1505 | } | ||
1506 | return pn; | ||
1507 | } | ||
1508 | |||
1509 | static JSBool | ||
1510 | MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) | ||
1511 | { | ||
1512 | JSAtom *label; | ||
1513 | JSTokenType tt; | ||
1514 | |||
1515 | tt = js_PeekTokenSameLine(cx, ts); | ||
1516 | if (tt == TOK_ERROR) | ||
1517 | return JS_FALSE; | ||
1518 | if (tt == TOK_NAME) { | ||
1519 | (void) js_GetToken(cx, ts); | ||
1520 | label = CURRENT_TOKEN(ts).t_atom; | ||
1521 | } else { | ||
1522 | label = NULL; | ||
1523 | } | ||
1524 | pn->pn_atom = label; | ||
1525 | return JS_TRUE; | ||
1526 | } | ||
1527 | |||
1528 | static JSBool | ||
1529 | BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) | ||
1530 | { | ||
1531 | JSObject *blockObj; | ||
1532 | JSScopeProperty *sprop; | ||
1533 | JSAtomListElement *ale; | ||
1534 | uintN n; | ||
1535 | |||
1536 | blockObj = tc->blockChain; | ||
1537 | sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom)); | ||
1538 | ATOM_LIST_SEARCH(ale, &tc->decls, atom); | ||
1539 | if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) { | ||
1540 | const char *name; | ||
1541 | |||
1542 | if (sprop) { | ||
1543 | JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); | ||
1544 | JS_ASSERT((uint16)sprop->shortid < OBJ_BLOCK_COUNT(cx, blockObj)); | ||
1545 | } | ||
1546 | |||
1547 | name = js_AtomToPrintableString(cx, atom); | ||
1548 | if (name) { | ||
1549 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, | ||
1550 | JSREPORT_ERROR, JSMSG_REDECLARED_VAR, | ||
1551 | (ale && ALE_JSOP(ale) == JSOP_DEFCONST) | ||
1552 | ? js_const_str | ||
1553 | : "variable", | ||
1554 | name); | ||
1555 | } | ||
1556 | return JS_FALSE; | ||
1557 | } | ||
1558 | |||
1559 | n = OBJ_BLOCK_COUNT(cx, blockObj); | ||
1560 | if (n == JS_BIT(16)) { | ||
1561 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, | ||
1562 | JSREPORT_ERROR, data->u.let.overflow); | ||
1563 | return JS_FALSE; | ||
1564 | } | ||
1565 | |||
1566 | /* Use JSPROP_ENUMERATE to aid the disassembler. */ | ||
1567 | return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom), | ||
1568 | JSVAL_VOID, NULL, NULL, | ||
1569 | JSPROP_ENUMERATE | | ||
1570 | JSPROP_PERMANENT | | ||
1571 | JSPROP_SHARED, | ||
1572 | SPROP_HAS_SHORTID, (int16) n, NULL); | ||
1573 | } | ||
1574 | |||
1575 | static JSBool | ||
1576 | BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) | ||
1577 | { | ||
1578 | JSStmtInfo *stmt; | ||
1579 | JSAtomListElement *ale; | ||
1580 | JSOp op, prevop; | ||
1581 | const char *name; | ||
1582 | JSLocalKind localKind; | ||
1583 | |||
1584 | stmt = js_LexicalLookup(tc, atom, NULL); | ||
1585 | ATOM_LIST_SEARCH(ale, &tc->decls, atom); | ||
1586 | op = data->op; | ||
1587 | if ((stmt && stmt->type != STMT_WITH) || ale) { | ||
1588 | prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR; | ||
1589 | if (JS_HAS_STRICT_OPTION(cx) | ||
1590 | ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR | ||
1591 | : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) { | ||
1592 | name = js_AtomToPrintableString(cx, atom); | ||
1593 | if (!name || | ||
1594 | !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, | ||
1595 | (op != JSOP_DEFCONST && | ||
1596 | prevop != JSOP_DEFCONST) | ||
1597 | ? JSREPORT_WARNING | | ||
1598 | JSREPORT_STRICT | ||
1599 | : JSREPORT_ERROR, | ||
1600 | JSMSG_REDECLARED_VAR, | ||
1601 | (prevop == JSOP_DEFFUN) | ||
1602 | ? js_function_str | ||
1603 | : (prevop == JSOP_DEFCONST) | ||
1604 | ? js_const_str | ||
1605 | : js_var_str, | ||
1606 | name)) { | ||
1607 | return JS_FALSE; | ||
1608 | } | ||
1609 | } | ||
1610 | if (op == JSOP_DEFVAR && prevop == JSOP_DEFFUN) | ||
1611 | tc->flags |= TCF_FUN_CLOSURE_VS_VAR; | ||
1612 | } | ||
1613 | if (!ale) { | ||
1614 | ale = js_IndexAtom(cx, atom, &tc->decls); | ||
1615 | if (!ale) | ||
1616 | return JS_FALSE; | ||
1617 | } | ||
1618 | ALE_SET_JSOP(ale, op); | ||
1619 | |||
1620 | if (!(tc->flags & TCF_IN_FUNCTION)) { | ||
1621 | /* | ||
1622 | * Don't lookup global variables or variables in an active frame at | ||
1623 | * compile time. | ||
1624 | */ | ||
1625 | return JS_TRUE; | ||
1626 | } | ||
1627 | |||
1628 | localKind = js_LookupLocal(cx, tc->u.fun, atom, NULL); | ||
1629 | if (localKind == JSLOCAL_NONE) { | ||
1630 | /* | ||
1631 | * Property not found in current variable scope: we have not seen this | ||
1632 | * variable before. Define a new local variable by adding a property | ||
1633 | * to the function's scope, allocating one slot in the function's vars | ||
1634 | * frame. Any locals declared in with statement bodies are handled at | ||
1635 | * runtime, by script prolog JSOP_DEFVAR opcodes generated for | ||
1636 | * slot-less vars. | ||
1637 | */ | ||
1638 | localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR; | ||
1639 | if (!js_InWithStatement(tc) && | ||
1640 | !BindLocalVariable(cx, tc->u.fun, atom, localKind)) { | ||
1641 | return JS_FALSE; | ||
1642 | } | ||
1643 | } else if (localKind == JSLOCAL_ARG) { | ||
1644 | name = js_AtomToPrintableString(cx, atom); | ||
1645 | if (!name) | ||
1646 | return JS_FALSE; | ||
1647 | |||
1648 | if (op == JSOP_DEFCONST) { | ||
1649 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, | ||
1650 | JSREPORT_ERROR, JSMSG_REDECLARED_PARAM, | ||
1651 | name); | ||
1652 | return JS_FALSE; | ||
1653 | } | ||
1654 | if (!js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, | ||
1655 | JSREPORT_WARNING | JSREPORT_STRICT, | ||
1656 | JSMSG_VAR_HIDES_ARG, name)) { | ||
1657 | return JS_FALSE; | ||
1658 | } | ||
1659 | } else { | ||
1660 | /* Not an argument, must be a redeclared local var. */ | ||
1661 | JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); | ||
1662 | } | ||
1663 | return JS_TRUE; | ||
1664 | } | ||
1665 | |||
1666 | #if JS_HAS_DESTRUCTURING | ||
1667 | |||
1668 | static JSBool | ||
1669 | BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn, | ||
1670 | JSTreeContext *tc) | ||
1671 | { | ||
1672 | JSAtom *atom; | ||
1673 | |||
1674 | /* | ||
1675 | * Destructuring is a form of assignment, so just as for an initialized | ||
1676 | * simple variable, we must check for assignment to 'arguments' and flag | ||
1677 | * the enclosing function (if any) as heavyweight. | ||
1678 | */ | ||
1679 | JS_ASSERT(pn->pn_type == TOK_NAME); | ||
1680 | atom = pn->pn_atom; | ||
1681 | if (atom == cx->runtime->atomState.argumentsAtom) | ||
1682 | tc->flags |= TCF_FUN_HEAVYWEIGHT; | ||
1683 | |||
1684 | data->pn = pn; | ||
1685 | if (!data->binder(cx, data, atom, tc)) | ||
1686 | return JS_FALSE; | ||
1687 | data->pn = NULL; | ||
1688 | |||
1689 | /* | ||
1690 | * Select the appropriate name-setting opcode, which may be specialized | ||
1691 | * further for local variable and argument slot optimizations. At this | ||
1692 | * point, we can't select the optimal final opcode, yet we must preserve | ||
1693 | * the CONST bit and convey "set", not "get". | ||
1694 | */ | ||
1695 | if (data->op == JSOP_DEFCONST) { | ||
1696 | pn->pn_op = JSOP_SETCONST; | ||
1697 | pn->pn_const = JS_TRUE; | ||
1698 | } else { | ||
1699 | pn->pn_op = JSOP_SETNAME; | ||
1700 | pn->pn_const = JS_FALSE; | ||
1701 | } | ||
1702 | return JS_TRUE; | ||
1703 | } | ||
1704 | |||
1705 | static JSBool | ||
1706 | MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg) | ||
1707 | { | ||
1708 | JSParseNode *pn2; | ||
1709 | |||
1710 | JS_ASSERT(pn->pn_arity == PN_LIST); | ||
1711 | JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL); | ||
1712 | pn2 = pn->pn_head; | ||
1713 | if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_flags & TCF_GENEXP_LAMBDA)) { | ||
1714 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn, | ||
1715 | JSREPORT_ERROR, msg); | ||
1716 | return JS_FALSE; | ||
1717 | } | ||
1718 | pn->pn_op = JSOP_SETCALL; | ||
1719 | return JS_TRUE; | ||
1720 | } | ||
1721 | |||
1722 | /* | ||
1723 | * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any | ||
1724 | * LHS expression except a destructuring initialiser, and R is on the stack. | ||
1725 | * Because R is already evaluated, the usual LHS-specialized bytecodes won't | ||
1726 | * work. After pushing R[P] we need to evaluate Q's "reference base" QB and | ||
1727 | * then push its property name QN. At this point the stack looks like | ||
1728 | * | ||
1729 | * [... R, R[P], QB, QN] | ||
1730 | * | ||
1731 | * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes | ||
1732 | * its operands with left-hand side above right-hand side: | ||
1733 | * | ||
1734 | * [rval, lval, xval] | ||
1735 | * | ||
1736 | * and pops all three values, setting lval[xval] = rval. But we cannot select | ||
1737 | * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var, | ||
1738 | * which can be optimized further. So we select JSOP_SETNAME. | ||
1739 | */ | ||
1740 | static JSBool | ||
1741 | BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) | ||
1742 | { | ||
1743 | while (pn->pn_type == TOK_RP) | ||
1744 | pn = pn->pn_kid; | ||
1745 | |||
1746 | switch (pn->pn_type) { | ||
1747 | case TOK_NAME: | ||
1748 | if (pn->pn_atom == cx->runtime->atomState.argumentsAtom) | ||
1749 | tc->flags |= TCF_FUN_HEAVYWEIGHT; | ||
1750 | /* FALL THROUGH */ | ||
1751 | case TOK_DOT: | ||
1752 | case TOK_LB: | ||
1753 | pn->pn_op = JSOP_SETNAME; | ||
1754 | break; | ||
1755 | |||
1756 | #if JS_HAS_LVALUE_RETURN | ||
1757 | case TOK_LP: | ||
1758 | if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) | ||
1759 | return JS_FALSE; | ||
1760 | break; | ||
1761 | #endif | ||
1762 | |||
1763 | #if JS_HAS_XML_SUPPORT | ||
1764 | case TOK_UNARYOP: | ||
1765 | if (pn->pn_op == JSOP_XMLNAME) { | ||
1766 | pn->pn_op = JSOP_BINDXMLNAME; | ||
1767 | break; | ||
1768 | } | ||
1769 | /* FALL THROUGH */ | ||
1770 | #endif | ||
1771 | |||
1772 | default: | ||
1773 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn, | ||
1774 | JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS); | ||
1775 | return JS_FALSE; | ||
1776 | } | ||
1777 | |||
1778 | return JS_TRUE; | ||
1779 | } | ||
1780 | |||
1781 | typedef struct FindPropValData { | ||
1782 | uint32 numvars; /* # of destructuring vars in left side */ | ||
1783 | uint32 maxstep; /* max # of steps searching right side */ | ||
1784 | JSDHashTable table; /* hash table for O(1) right side search */ | ||
1785 | } FindPropValData; | ||
1786 | |||
1787 | typedef struct FindPropValEntry { | ||
1788 | JSDHashEntryHdr hdr; | ||
1789 | JSParseNode *pnkey; | ||
1790 | JSParseNode *pnval; | ||
1791 | } FindPropValEntry; | ||
1792 | |||
1793 | #define ASSERT_VALID_PROPERTY_KEY(pnkey) \ | ||
1794 | JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \ | ||
1795 | ((pnkey)->pn_type == TOK_NUMBER || \ | ||
1796 | (pnkey)->pn_type == TOK_STRING || \ | ||
1797 | (pnkey)->pn_type == TOK_NAME)) | ||
1798 | |||
1799 | static JSDHashNumber | ||
1800 | HashFindPropValKey(JSDHashTable *table, const void *key) | ||
1801 | { | ||
1802 | const JSParseNode *pnkey = (const JSParseNode *)key; | ||
1803 | |||
1804 | ASSERT_VALID_PROPERTY_KEY(pnkey); | ||
1805 | return (pnkey->pn_type == TOK_NUMBER) | ||
1806 | ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^ | ||
1807 | JSDOUBLE_LO32(pnkey->pn_dval)) | ||
1808 | : ATOM_HASH(pnkey->pn_atom); | ||
1809 | } | ||
1810 | |||
1811 | static JSBool | ||
1812 | MatchFindPropValEntry(JSDHashTable *table, | ||
1813 | const JSDHashEntryHdr *entry, | ||
1814 | const void *key) | ||
1815 | { | ||
1816 | const FindPropValEntry *fpve = (const FindPropValEntry *)entry; | ||
1817 | const JSParseNode *pnkey = (const JSParseNode *)key; | ||
1818 | |||
1819 | ASSERT_VALID_PROPERTY_KEY(pnkey); | ||
1820 | return pnkey->pn_type == fpve->pnkey->pn_type && | ||
1821 | ((pnkey->pn_type == TOK_NUMBER) | ||
1822 | ? pnkey->pn_dval == fpve->pnkey->pn_dval | ||
1823 | : pnkey->pn_atom == fpve->pnkey->pn_atom); | ||
1824 | } | ||
1825 | |||
1826 | static const JSDHashTableOps FindPropValOps = { | ||
1827 | JS_DHashAllocTable, | ||
1828 | JS_DHashFreeTable, | ||
1829 | HashFindPropValKey, | ||
1830 | MatchFindPropValEntry, | ||
1831 | JS_DHashMoveEntryStub, | ||
1832 | JS_DHashClearEntryStub, | ||
1833 | JS_DHashFinalizeStub, | ||
1834 | NULL | ||
1835 | }; | ||
1836 | |||
1837 | #define STEP_HASH_THRESHOLD 10 | ||
1838 | #define BIG_DESTRUCTURING 5 | ||
1839 | #define BIG_OBJECT_INIT 20 | ||
1840 | |||
1841 | static JSParseNode * | ||
1842 | FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data) | ||
1843 | { | ||
1844 | FindPropValEntry *entry; | ||
1845 | JSParseNode *pnhit, *pnhead, *pnprop, *pnkey; | ||
1846 | uint32 step; | ||
1847 | |||
1848 | /* If we have a hash table, use it as the sole source of truth. */ | ||
1849 | if (data->table.ops) { | ||
1850 | entry = (FindPropValEntry *) | ||
1851 | JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP); | ||
1852 | return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL; | ||
1853 | } | ||
1854 | |||
1855 | /* If pn is not an object initialiser node, we can't do anything here. */ | ||
1856 | if (pn->pn_type != TOK_RC) | ||
1857 | return NULL; | ||
1858 | |||
1859 | /* | ||
1860 | * We must search all the way through pn's list, to handle the case of an | ||
1861 | * id duplicated for two or more property initialisers. | ||
1862 | */ | ||
1863 | pnhit = NULL; | ||
1864 | step = 0; | ||
1865 | ASSERT_VALID_PROPERTY_KEY(pnid); | ||
1866 | pnhead = pn->pn_head; | ||
1867 | if (pnhead && pnhead->pn_type == TOK_DEFSHARP) | ||
1868 | pnhead = pnhead->pn_next; | ||
1869 | if (pnid->pn_type == TOK_NUMBER) { | ||
1870 | for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) { | ||
1871 | JS_ASSERT(pnprop->pn_type == TOK_COLON); | ||
1872 | if (pnprop->pn_op == JSOP_NOP) { | ||
1873 | pnkey = pnprop->pn_left; | ||
1874 | ASSERT_VALID_PROPERTY_KEY(pnkey); | ||
1875 | if (pnkey->pn_type == TOK_NUMBER && | ||
1876 | pnkey->pn_dval == pnid->pn_dval) { | ||
1877 | pnhit = pnprop; | ||
1878 | } | ||
1879 | ++step; | ||
1880 | } | ||
1881 | } | ||
1882 | } else { | ||
1883 | for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) { | ||
1884 | JS_ASSERT(pnprop->pn_type == TOK_COLON); | ||
1885 | if (pnprop->pn_op == JSOP_NOP) { | ||
1886 | pnkey = pnprop->pn_left; | ||
1887 | ASSERT_VALID_PROPERTY_KEY(pnkey); | ||
1888 | if (pnkey->pn_type == pnid->pn_type && | ||
1889 | pnkey->pn_atom == pnid->pn_atom) { | ||
1890 | pnhit = pnprop; | ||
1891 | } | ||
1892 | ++step; | ||
1893 | } | ||
1894 | } | ||
1895 | } | ||
1896 | if (!pnhit) | ||
1897 | return NULL; | ||
1898 | |||
1899 | /* Hit via full search -- see whether it's time to create the hash table. */ | ||
1900 | JS_ASSERT(!data->table.ops); | ||
1901 | if (step > data->maxstep) { | ||
1902 | data->maxstep = step; | ||
1903 | if (step >= STEP_HASH_THRESHOLD && | ||
1904 | data->numvars >= BIG_DESTRUCTURING && | ||
1905 | pn->pn_count >= BIG_OBJECT_INIT && | ||
1906 | JS_DHashTableInit(&data->table, &FindPropValOps, pn, | ||
1907 | sizeof(FindPropValEntry), | ||
1908 | JS_DHASH_DEFAULT_CAPACITY(pn->pn_count))) | ||
1909 | { | ||
1910 | for (pn = pnhead; pn; pn = pn->pn_next) { | ||
1911 | JS_ASSERT(pnprop->pn_type == TOK_COLON); | ||
1912 | ASSERT_VALID_PROPERTY_KEY(pn->pn_left); | ||
1913 | entry = (FindPropValEntry *) | ||
1914 | JS_DHashTableOperate(&data->table, pn->pn_left, | ||
1915 | JS_DHASH_ADD); | ||
1916 | entry->pnval = pn->pn_right; | ||
1917 | } | ||
1918 | } | ||
1919 | } | ||
1920 | return pnhit->pn_right; | ||
1921 | } | ||
1922 | |||
1923 | /* | ||
1924 | * If data is null, the caller is AssignExpr and instead of binding variables, | ||
1925 | * we specialize lvalues in the propery value positions of the left-hand side. | ||
1926 | * If right is null, just check for well-formed lvalues. | ||
1927 | */ | ||
1928 | static JSBool | ||
1929 | CheckDestructuring(JSContext *cx, BindData *data, | ||
1930 | JSParseNode *left, JSParseNode *right, | ||
1931 | JSTreeContext *tc) | ||
1932 | { | ||
1933 | JSBool ok; | ||
1934 | FindPropValData fpvd; | ||
1935 | JSParseNode *lhs, *rhs, *pn, *pn2; | ||
1936 | |||
1937 | if (left->pn_type == TOK_ARRAYCOMP) { | ||
1938 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), left, | ||
1939 | JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE); | ||
1940 | return JS_FALSE; | ||
1941 | } | ||
1942 | |||
1943 | #if JS_HAS_DESTRUCTURING_SHORTHAND | ||
1944 | if (right && right->pn_arity == PN_LIST && (right->pn_extra & PNX_SHORTHAND)) { | ||
1945 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), right, | ||
1946 | JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT); | ||
1947 | return JS_FALSE; | ||
1948 | } | ||
1949 | #endif | ||
1950 | |||
1951 | fpvd.table.ops = NULL; | ||
1952 | lhs = left->pn_head; | ||
1953 | if (lhs && lhs->pn_type == TOK_DEFSHARP) { | ||
1954 | pn = lhs; | ||
1955 | goto no_var_name; | ||
1956 | } | ||
1957 | |||
1958 | if (left->pn_type == TOK_RB) { | ||
1959 | rhs = (right && right->pn_type == left->pn_type) | ||
1960 | ? right->pn_head | ||
1961 | : NULL; | ||
1962 | |||
1963 | while (lhs) { | ||
1964 | pn = lhs, pn2 = rhs; | ||
1965 | if (!data) { | ||
1966 | /* Skip parenthesization if not in a variable declaration. */ | ||
1967 | while (pn->pn_type == TOK_RP) | ||
1968 | pn = pn->pn_kid; | ||
1969 | if (pn2) { | ||
1970 | while (pn2->pn_type == TOK_RP) | ||
1971 | pn2 = pn2->pn_kid; | ||
1972 | } | ||
1973 | } | ||
1974 | |||
1975 | /* Nullary comma is an elision; binary comma is an expression.*/ | ||
1976 | if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) { | ||
1977 | if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { | ||
1978 | ok = CheckDestructuring(cx, data, pn, pn2, tc); | ||
1979 | } else { | ||
1980 | if (data) { | ||
1981 | if (pn->pn_type != TOK_NAME) | ||
1982 | goto no_var_name; | ||
1983 | |||
1984 | ok = BindDestructuringVar(cx, data, pn, tc); | ||
1985 | } else { | ||
1986 | ok = BindDestructuringLHS(cx, pn, tc); | ||
1987 | } | ||
1988 | } | ||
1989 | if (!ok) | ||
1990 | goto out; | ||
1991 | } | ||
1992 | |||
1993 | lhs = lhs->pn_next; | ||
1994 | if (rhs) | ||
1995 | rhs = rhs->pn_next; | ||
1996 | } | ||
1997 | } else { | ||
1998 | JS_ASSERT(left->pn_type == TOK_RC); | ||
1999 | fpvd.numvars = left->pn_count; | ||
2000 | fpvd.maxstep = 0; | ||
2001 | rhs = NULL; | ||
2002 | |||
2003 | while (lhs) { | ||
2004 | JS_ASSERT(lhs->pn_type == TOK_COLON); | ||
2005 | pn = lhs->pn_right; | ||
2006 | if (!data) { | ||
2007 | /* Skip parenthesization if not in a variable declaration. */ | ||
2008 | while (pn->pn_type == TOK_RP) | ||
2009 | pn = pn->pn_kid; | ||
2010 | } | ||
2011 | |||
2012 | if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { | ||
2013 | if (right) { | ||
2014 | rhs = FindPropertyValue(right, lhs->pn_left, &fpvd); | ||
2015 | if (rhs && !data) { | ||
2016 | while (rhs->pn_type == TOK_RP) | ||
2017 | rhs = rhs->pn_kid; | ||
2018 | } | ||
2019 | } | ||
2020 | |||
2021 | ok = CheckDestructuring(cx, data, pn, rhs, tc); | ||
2022 | } else if (data) { | ||
2023 | if (pn->pn_type != TOK_NAME) | ||
2024 | goto no_var_name; | ||
2025 | |||
2026 | ok = BindDestructuringVar(cx, data, pn, tc); | ||
2027 | } else { | ||
2028 | ok = BindDestructuringLHS(cx, pn, tc); | ||
2029 | } | ||
2030 | if (!ok) | ||
2031 | goto out; | ||
2032 | |||
2033 | lhs = lhs->pn_next; | ||
2034 | } | ||
2035 | } | ||
2036 | |||
2037 | /* | ||
2038 | * The catch/finally handler implementation in the interpreter assumes | ||
2039 | * that any operation that introduces a new scope (like a "let" or "with" | ||
2040 | * block) increases the stack depth. This way, it is possible to restore | ||
2041 | * the scope chain based on stack depth of the handler alone. "let" with | ||
2042 | * an empty destructuring pattern like in | ||
2043 | * | ||
2044 | * let [] = 1; | ||
2045 | * | ||
2046 | * would violate this assumption as the there would be no let locals to | ||
2047 | * store on the stack. To satisfy it we add an empty property to such | ||
2048 | * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of | ||
2049 | * slots, would be always positive. | ||
2050 | * | ||
2051 | * Note that we add such a property even if the block has locals due to | ||
2052 | * later let declarations in it. We optimize for code simplicity here, | ||
2053 | * not the fastest runtime performance with empty [] or {}. | ||
2054 | */ | ||
2055 | if (data && | ||
2056 | data->binder == BindLet && | ||
2057 | OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) { | ||
2058 | ok = js_DefineNativeProperty(cx, tc->blockChain, | ||
2059 | ATOM_TO_JSID(cx->runtime-> | ||
2060 | atomState.emptyAtom), | ||
2061 | JSVAL_VOID, NULL, NULL, | ||
2062 | JSPROP_ENUMERATE | | ||
2063 | JSPROP_PERMANENT | | ||
2064 | JSPROP_SHARED, | ||
2065 | SPROP_HAS_SHORTID, 0, NULL); | ||
2066 | if (!ok) | ||
2067 | goto out; | ||
2068 | } | ||
2069 | |||
2070 | ok = JS_TRUE; | ||
2071 | |||
2072 | out: | ||
2073 | if (fpvd.table.ops) | ||
2074 | JS_DHashTableFinish(&fpvd.table); | ||
2075 | return ok; | ||
2076 | |||
2077 | no_var_name: | ||
2078 | js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn, JSREPORT_ERROR, | ||
2079 | JSMSG_NO_VARIABLE_NAME); | ||
2080 | ok = JS_FALSE; | ||
2081 | goto out; | ||
2082 | } | ||
2083 | |||
2084 | static JSParseNode * | ||
2085 | DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, | ||
2086 | JSTokenType tt) | ||
2087 | { | ||
2088 | JSParseNode *pn; | ||
2089 | |||
2090 | pn = PrimaryExpr(cx, TS(tc->parseContext), tc, tt, JS_FALSE); | ||
2091 | if (!pn) | ||
2092 | return NULL; | ||
2093 | if (!CheckDestructuring(cx, data, pn, NULL, tc)) | ||
2094 | return NULL; | ||
2095 | return pn; | ||
2096 | } | ||
2097 | |||
2098 | #endif /* JS_HAS_DESTRUCTURING */ | ||
2099 | |||
2100 | extern const char js_with_statement_str[]; | ||
2101 | |||
2102 | static JSParseNode * | ||
2103 | ContainsStmt(JSParseNode *pn, JSTokenType tt) | ||
2104 | { | ||
2105 | JSParseNode *pn2, *pnt; | ||
2106 | |||
2107 | if (!pn) | ||
2108 | return NULL; | ||
2109 | if (pn->pn_type == tt) | ||
2110 | return pn; | ||
2111 | switch (pn->pn_arity) { | ||
2112 | case PN_LIST: | ||
2113 | for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { | ||
2114 | pnt = ContainsStmt(pn2, tt); | ||
2115 | if (pnt) | ||
2116 | return pnt; | ||
2117 | } | ||
2118 | break; | ||
2119 | case PN_TERNARY: | ||
2120 | pnt = ContainsStmt(pn->pn_kid1, tt); | ||
2121 | if (pnt) | ||
2122 | return pnt; | ||
2123 | pnt = ContainsStmt(pn->pn_kid2, tt); | ||
2124 | if (pnt) | ||
2125 | return pnt; | ||
2126 | return ContainsStmt(pn->pn_kid3, tt); | ||
2127 | case PN_BINARY: | ||
2128 | /* | ||
2129 | * Limit recursion if pn is a binary expression, which can't contain a | ||
2130 | * var statement. | ||
2131 | */ | ||
2132 | if (pn->pn_op != JSOP_NOP) | ||
2133 | return NULL; | ||
2134 | pnt = ContainsStmt(pn->pn_left, tt); | ||
2135 | if (pnt) | ||
2136 | return pnt; | ||
2137 | return ContainsStmt(pn->pn_right, tt); | ||
2138 | case PN_UNARY: | ||
2139 | if (pn->pn_op != JSOP_NOP) | ||
2140 | return NULL; | ||
2141 | return ContainsStmt(pn->pn_kid, tt); | ||
2142 | case PN_NAME: | ||
2143 | return ContainsStmt(pn->pn_expr, tt); | ||
2144 | default:; | ||
2145 | } | ||
2146 | return NULL; | ||
2147 | } | ||
2148 | |||
2149 | static JSParseNode * | ||
2150 | ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, | ||
2151 | JSParser operandParser) | ||
2152 | { | ||
2153 | JSTokenType tt, tt2; | ||
2154 | JSParseNode *pn, *pn2; | ||
2155 | |||
2156 | tt = CURRENT_TOKEN(ts).type; | ||
2157 | if (tt == TOK_RETURN && !(tc->flags & TCF_IN_FUNCTION)) { | ||
2158 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, | ||
2159 | JSMSG_BAD_RETURN_OR_YIELD, js_return_str); | ||
2160 | return NULL; | ||
2161 | } | ||
2162 | |||
2163 | pn = NewParseNode(cx, ts, PN_UNARY, tc); | ||
2164 | if (!pn) | ||
2165 | return NULL; | ||
2166 | |||
2167 | #if JS_HAS_GENERATORS | ||
2168 | if (tt == TOK_YIELD) | ||
2169 | tc->flags |= TCF_FUN_IS_GENERATOR; | ||
2170 | #endif | ||
2171 | |||
2172 | /* This is ugly, but we don't want to require a semicolon. */ | ||
2173 | ts->flags |= TSF_OPERAND; | ||
2174 | tt2 = js_PeekTokenSameLine(cx, ts); | ||
2175 | ts->flags &= ~TSF_OPERAND; | ||
2176 | if (tt2 == TOK_ERROR) | ||
2177 | return NULL; | ||
2178 | |||
2179 | if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC | ||
2180 | #if JS_HAS_GENERATORS | ||
2181 | && (tt != TOK_YIELD || | ||
2182 | (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP && | ||
2183 | tt2 != TOK_COLON && tt2 != TOK_COMMA)) | ||
2184 | #endif | ||
2185 | ) { | ||
2186 | pn2 = operandParser(cx, ts, tc); | ||
2187 | if (!pn2) | ||
2188 | return NULL; | ||
2189 | #if JS_HAS_GENERATORS | ||
2190 | if (tt == TOK_RETURN) | ||
2191 | #endif | ||
2192 | tc->flags |= TCF_RETURN_EXPR; | ||
2193 | pn->pn_pos.end = pn2->pn_pos.end; | ||
2194 | pn->pn_kid = pn2; | ||
2195 | } else { | ||
2196 | #if JS_HAS_GENERATORS | ||
2197 | if (tt == TOK_RETURN) | ||
2198 | #endif | ||
2199 | tc->flags |= TCF_RETURN_VOID; | ||
2200 | } | ||
2201 | |||
2202 | if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) { | ||
2203 | /* As in Python (see PEP-255), disallow return v; in generators. */ | ||
2204 | ReportBadReturn(cx, tc, JSREPORT_ERROR, | ||
2205 | JSMSG_BAD_GENERATOR_RETURN, | ||
2206 | JSMSG_BAD_ANON_GENERATOR_RETURN); | ||
2207 | return NULL; | ||
2208 | } | ||
2209 | |||
2210 | if (JS_HAS_STRICT_OPTION(cx) && | ||
2211 | (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 && | ||
2212 | !ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT, | ||
2213 | JSMSG_NO_RETURN_VALUE, | ||
2214 | JSMSG_ANON_NO_RETURN_VALUE)) { | ||
2215 | return NULL; | ||
2216 | } | ||
2217 | |||
2218 | return pn; | ||
2219 | } | ||
2220 | |||
2221 | static JSParseNode * | ||
2222 | PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, | ||
2223 | JSStmtInfo *stmtInfo) | ||
2224 | { | ||
2225 | JSParseNode *pn; | ||
2226 | JSObject *obj; | ||
2227 | JSParsedObjectBox *blockpob; | ||
2228 | |||
2229 | pn = NewParseNode(cx, ts, PN_NAME, tc); | ||
2230 | if (!pn) | ||
2231 | return NULL; | ||
2232 | |||
2233 | obj = js_NewBlockObject(cx); | ||
2234 | if (!obj) | ||
2235 | return NULL; | ||
2236 | |||
2237 | blockpob = js_NewParsedObjectBox(cx, tc->parseContext, obj); | ||
2238 | if (!blockpob) | ||
2239 | return NULL; | ||
2240 | |||
2241 | js_PushBlockScope(tc, stmtInfo, obj, -1); | ||
2242 | pn->pn_type = TOK_LEXICALSCOPE; | ||
2243 | pn->pn_op = JSOP_LEAVEBLOCK; | ||
2244 | pn->pn_pob = blockpob; | ||
2245 | pn->pn_slot = -1; | ||
2246 | return pn; | ||
2247 | } | ||
2248 | |||
2249 | #if JS_HAS_BLOCK_SCOPE | ||
2250 | |||
2251 | static JSParseNode * | ||
2252 | LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement) | ||
2253 | { | ||
2254 | JSParseNode *pn, *pnblock, *pnlet; | ||
2255 | JSStmtInfo stmtInfo; | ||
2256 | |||
2257 | JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET); | ||
2258 | |||
2259 | /* Create the let binary node. */ | ||
2260 | pnlet = NewParseNode(cx, ts, PN_BINARY, tc); | ||
2261 | if (!pnlet) | ||
2262 | return NULL; | ||
2263 | |||
2264 | MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET); | ||
2265 | |||
2266 | /* This is a let block or expression of the form: let (a, b, c) .... */ | ||
2267 | pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo); | ||
2268 | if (!pnblock) | ||
2269 | return NULL; | ||
2270 | pn = pnblock; | ||
2271 | pn->pn_expr = pnlet; | ||
2272 | |||
2273 | pnlet->pn_left = Variables(cx, ts, tc); | ||
2274 | if (!pnlet->pn_left) | ||
2275 | return NULL; | ||
2276 | pnlet->pn_left->pn_extra = PNX_POPVAR; | ||
2277 | |||
2278 | MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET); | ||
2279 | |||
2280 | ts->flags |= TSF_OPERAND; | ||
2281 | if (statement && !js_MatchToken(cx, ts, TOK_LC)) { | ||
2282 | /* | ||
2283 | * If this is really an expression in let statement guise, then we | ||
2284 | * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop | ||
2285 | * the return value of the expression. | ||
2286 | */ | ||
2287 | pn = NewParseNode(cx, ts, PN_UNARY, tc); | ||
2288 | if (!pn) | ||
2289 | return NULL; | ||
2290 | pn->pn_type = TOK_SEMI; | ||
2291 | pn->pn_num = -1; | ||
2292 | pn->pn_kid = pnblock; | ||
2293 | |||
2294 | statement = JS_FALSE; | ||
2295 | } | ||
2296 | ts->flags &= ~TSF_OPERAND; | ||
2297 | |||
2298 | if (statement) { | ||
2299 | pnlet->pn_right = Statements(cx, ts, tc); | ||
2300 | if (!pnlet->pn_right) | ||
2301 | return NULL; | ||
2302 | MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET); | ||
2303 | } else { | ||
2304 | /* | ||
2305 | * Change pnblock's opcode to the variant that propagates the last | ||
2306 | * result down after popping the block, and clear statement. | ||
2307 | */ | ||
2308 | pnblock->pn_op = JSOP_LEAVEBLOCKEXPR; | ||
2309 | pnlet->pn_right = AssignExpr(cx, ts, tc); | ||
2310 | if (!pnlet->pn_right) | ||
2311 | return NULL; | ||
2312 | } | ||
2313 | |||
2314 | js_PopStatement(tc); | ||
2315 | return pn; | ||
2316 | } | ||
2317 | |||
2318 | #endif /* JS_HAS_BLOCK_SCOPE */ | ||
2319 | |||
2320 | static JSParseNode * | ||
2321 | Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) | ||
2322 | { | ||
2323 | JSTokenType tt; | ||
2324 | JSParseNode *pn, *pn1, *pn2, *pn3, *pn4; | ||
2325 | JSStmtInfo stmtInfo, *stmt, *stmt2; | ||
2326 | JSAtom *label; | ||
2327 | |||
2328 | JS_CHECK_RECURSION(cx, return NULL); | ||
2329 | |||
2330 | ts->flags |= TSF_OPERAND; | ||
2331 | tt = js_GetToken(cx, ts); | ||
2332 | ts->flags &= ~TSF_OPERAND; | ||
2333 | |||
2334 | #if JS_HAS_GETTER_SETTER | ||
2335 | if (tt == TOK_NAME) { | ||
2336 | tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION); | ||
2337 | if (tt == TOK_ERROR) | ||
2338 | return NULL; | ||
2339 | } | ||
2340 | #endif | ||
2341 | |||
2342 | switch (tt) { | ||
2343 | case TOK_FUNCTION: | ||
2344 | #if JS_HAS_XML_SUPPORT | ||
2345 | ts->flags |= TSF_KEYWORD_IS_NAME; | ||
2346 | tt = js_PeekToken(cx, ts); | ||
2347 | ts->flags &= ~TSF_KEYWORD_IS_NAME; | ||
2348 | if (tt == TOK_DBLCOLON) | ||
2349 | goto expression; | ||
2350 | #endif | ||
2351 | return FunctionStmt(cx, ts, tc); | ||
2352 | |||
2353 | case TOK_IF: | ||
2354 | /* An IF node has three kids: condition, then, and optional else. */ | ||
2355 | pn = NewParseNode(cx, ts, PN_TERNARY, tc); | ||
2356 | if (!pn) | ||
2357 | return NULL; | ||
2358 | pn1 = Condition(cx, ts, tc); | ||
2359 | if (!pn1) | ||
2360 | return NULL; | ||
2361 | js_PushStatement(tc, &stmtInfo, STMT_IF, -1); | ||
2362 | pn2 = Statement(cx, ts, tc); | ||
2363 | if (!pn2) | ||
2364 | return NULL; | ||
2365 | ts->flags |= TSF_OPERAND; | ||
2366 | if (js_MatchToken(cx, ts, TOK_ELSE)) { | ||
2367 | ts->flags &= ~TSF_OPERAND; | ||
2368 | stmtInfo.type = STMT_ELSE; | ||
2369 | pn3 = Statement(cx, ts, tc); | ||
2370 | if (!pn3) | ||
2371 | return NULL; | ||
2372 | pn->pn_pos.end = pn3->pn_pos.end; | ||
2373 | } else { | ||
2374 | ts->flags &= ~TSF_OPERAND; | ||
2375 | pn3 = NULL; | ||
2376 | pn->pn_pos.end = pn2->pn_pos.end; | ||
2377 | } | ||
2378 | js_PopStatement(tc); | ||
2379 | pn->pn_kid1 = pn1; | ||
2380 | pn->pn_kid2 = pn2; | ||
2381 | pn->pn_kid3 = pn3; | ||
2382 | return pn; | ||
2383 | |||
2384 | case TOK_SWITCH: | ||
2385 | { | ||
2386 | JSParseNode *pn5, *saveBlock; | ||
2387 | JSBool seenDefault = JS_FALSE; | ||
2388 | |||
2389 | pn = NewParseNode(cx, ts, PN_BINARY, tc); | ||
2390 | if (!pn) | ||
2391 | return NULL; | ||
2392 | MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH); | ||
2393 | |||
2394 | /* pn1 points to the switch's discriminant. */ | ||
2395 | pn1 = ParenExpr(cx, ts, tc, NULL, NULL); | ||
2396 | if (!pn1) | ||
2397 | return NULL; | ||
2398 | |||
2399 | MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH); | ||
2400 | MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH); | ||
2401 | |||
2402 | /* pn2 is a list of case nodes. The default case has pn_left == NULL */ | ||
2403 | pn2 = NewParseNode(cx, ts, PN_LIST, tc); | ||
2404 | if (!pn2) | ||
2405 | return NULL; | ||
2406 | saveBlock = tc->blockNode; | ||
2407 | tc->blockNode = pn2; | ||
2408 | PN_INIT_LIST(pn2); | ||
2409 | |||
2410 | js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1); | ||
2411 | |||
2412 | while ((tt = js_GetToken(cx, ts)) != TOK_RC) { | ||
2413 | switch (tt) { | ||
2414 | case TOK_DEFAULT: | ||
2415 | if (seenDefault) { | ||
2416 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, | ||
2417 | JSMSG_TOO_MANY_DEFAULTS); | ||
2418 | return NULL; | ||
2419 | } | ||
2420 | seenDefault = JS_TRUE; | ||
2421 | /* FALL THROUGH */ | ||
2422 | |||
2423 | case TOK_CASE: | ||
2424 | pn3 = NewParseNode(cx, ts, PN_BINARY, tc); | ||
2425 | if (!pn3) | ||
2426 | return NULL; | ||
2427 | if (tt == TOK_CASE) { | ||
2428 | pn3->pn_left = Expr(cx, ts, tc); | ||
2429 | if (!pn3->pn_left) | ||
2430 | return NULL; | ||
2431 | } | ||
2432 | PN_APPEND(pn2, pn3); | ||
2433 | if (pn2->pn_count == JS_BIT(16)) { | ||
2434 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, | ||
2435 | JSMSG_TOO_MANY_CASES); | ||
2436 | return NULL; | ||
2437 | } | ||
2438 | break; | ||
2439 | |||
2440 | case TOK_ERROR: | ||
2441 | return NULL; | ||
2442 | |||
2443 | default: | ||
2444 | js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, | ||
2445 | JSMSG_BAD_SWITCH); | ||
2446 | return NULL; | ||
2447 | } | ||
2448 | MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE); | ||
2449 | |||
2450 | pn4 = NewParseNode(cx, ts, PN_LIST, tc); | ||
2451 | if (!pn4) | ||
2452 | return NULL; | ||
2453 | pn4->pn_type = TOK_LC; | ||
2454 | PN_INIT_LIST(pn4); | ||
2455 | ts->flags |= TSF_OPERAND; | ||
2456 | while ((tt = js_PeekToken(cx, ts)) != TOK_RC && | ||
2457 | tt != TOK_CASE && tt != TOK_DEFAULT) { | ||
2458 | ts->flags &= ~TSF_OPERAND; | ||
2459 | if (tt == TOK_ERROR) | ||
2460 | return NULL; | ||
2461 | pn5 = Statement(cx, ts, tc); | ||
2462 | if (!pn5) | ||
2463 | return NULL; | ||
2464 | pn4->pn_pos.end = pn5->pn_pos.end; | ||
2465 | PN_APPEND(pn4, pn5); | ||
2466 | ts->flags |= TSF_OPERAND; | ||
2467 | } | ||
2468 | ts->flags &= ~TSF_OPERAND; | ||
2469 | |||
2470 | /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */ | ||
2471 | if (pn4->pn_head) | ||
2472 | pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin; | ||
2473 | pn3->pn_pos.end = pn4->pn_pos.end; | ||
2474 | pn3->pn_right = pn4; | ||
2475 | } | ||
2476 | |||
2477 | /* | ||
2478 | * Handle the case where there was a let declaration in any case in | ||
2479 | * the switch body, but not within an inner block. If it replaced | ||
2480 | * tc->blockNode with a new block node then we must refresh pn2 and | ||
2481 | * then restore tc->blockNode. | ||
2482 | */ | ||
2483 | if (tc->blockNode != pn2) | ||
2484 | pn2 = tc->blockNode; | ||
2485 | tc->blockNode = saveBlock; | ||
2486 | js_PopStatement(tc); | ||
2487 | |||
2488 | pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; | ||
2489 | pn->pn_left = pn1; | ||
2490 | pn->pn_right = pn2; | ||
2491 | return pn; | ||
2492 | } | ||
2493 | |||
2494 | case TOK_WHILE: | ||
2495 | pn = NewParseNode(cx, ts, PN_BINARY, tc); | ||
2496 | if (!pn) | ||
2497 | return NULL; | ||
2498 | js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1); | ||
2499 | pn2 = Condition(cx, ts, tc); | ||
2500 | if (!pn2) | ||
2501 | return NULL; | ||
2502 | pn->pn_left = pn2; | ||
2503 | pn2 = Statement(cx, ts, tc); | ||
2504 | if (!pn2) | ||
2505 | return NULL; | ||
2506 | js_PopStatement(tc); | ||
2507 | pn->pn_pos.end = pn2->pn_pos.end; | ||
2508 | pn->pn_right = pn2; | ||
2509 | return pn; | ||
2510 | |||
2511 | case TOK_DO: | ||
2512 | pn = NewParseNode(cx, ts, PN_BINARY, tc); | ||
2513 | if (!pn) | ||
2514 | return NULL; | ||
2515 | js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1); | ||
2516 | pn2 = Statement(cx, ts, tc); | ||
2517 | if (!pn2) | ||
2518 | return NULL; | ||
2519 | pn->pn_left = pn2; | ||
2520 | MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO); | ||
2521 | pn2 = Condition(cx, ts, tc); | ||
2522 | if (!pn2) | ||
2523 | return NULL; | ||
2524 | js_PopStatement(tc); | ||
2525 | pn->pn_pos.end = pn2->pn_pos.end; | ||
2526 | pn->pn_right = pn2; | ||
2527 | if (JSVERSION_NUMBER(cx) != JSVERSION_ECMA_3) { | ||
2528 | /* | ||
2529 | * All legacy and extended versions must do automatic semicolon | ||
2530 | * insertion after do-while. See the testcase and discussion in | ||
2531 | * http://bugzilla.mozilla.org/show_bug.cgi?id=238945. | ||
2532 | */ | ||
2533 | (void) js_MatchToken(cx, ts, TOK_SEMI); | ||
2534 | return pn; | ||
2535 | } | ||
2536 | break; | ||
2537 | |||
2538 | case TOK_FOR: | ||
2539 | { | ||
2540 | #if JS_HAS_BLOCK_SCOPE | ||
2541 | JSParseNode *pnlet; | ||
2542 | JSStmtInfo blockInfo; | ||
2543 | |||
2544 | pnlet = NULL; | ||
2545 | #endif | ||
2546 | |||
2547 | /* A FOR node is binary, left is loop control and right is the body. */ | ||
2548 | pn = NewParseNode(cx, ts, PN_BINARY, tc); | ||
2549 | if (!pn) | ||
2550 | return NULL; | ||
2551 | js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1); | ||
2552 | |||
2553 | pn->pn_op = JSOP_ITER; | ||
2554 | pn->pn_iflags = 0; | ||
2555 | if (js_MatchToken(cx, ts, TOK_NAME)) { | ||
2556 | if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom) | ||
2557 | pn->pn_iflags = JSITER_FOREACH; | ||
2558 | else | ||
2559 | js_UngetToken(ts); | ||
2560 | } | ||
2561 | |||
2562 | MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); | ||
2563 | ts->flags |= TSF_OPERAND; | ||
2564 | tt = js_PeekToken(cx, ts); | ||
2565 | ts->flags &= ~TSF_OPERAND; | ||
2566 | if (tt == TOK_SEMI) { | ||
2567 | if (pn->pn_iflags & JSITER_FOREACH) | ||
2568 | goto bad_for_each; | ||
2569 | |||
2570 | /* No initializer -- set first kid of left sub-node to null. */ | ||
2571 | pn1 = NULL; | ||
2572 | } else { | ||
2573 | /* | ||
2574 | * Set pn1 to a var list or an initializing expression. | ||
2575 | * | ||
2576 | * Set the TCF_IN_FOR_INIT flag during parsing of the first clause | ||
2577 | * of the for statement. This flag will be used by the RelExpr | ||
2578 | * production; if it is set, then the 'in' keyword will not be | ||
2579 | * recognized as an operator, leaving it available to be parsed as | ||
2580 | * part of a for/in loop. | ||
2581 | * | ||
2582 |