/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsparse.cpp
C++ | 1987 lines | 1451 code | 193 blank | 343 comment | 407 complexity | cb9396026941b3113ddbde18f4e6ddd8 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * vim: set ts=8 sw=4 et tw=99: 3 * 4 * ***** BEGIN LICENSE BLOCK ***** 5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 6 * 7 * The contents of this file are subject to the Mozilla Public License Version 8 * 1.1 (the "License"); you may not use this file except in compliance with 9 * the License. You may obtain a copy of the License at 10 * http://www.mozilla.org/MPL/ 11 * 12 * Software distributed under the License is distributed on an "AS IS" basis, 13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 * for the specific language governing rights and limitations under the 15 * License. 16 * 17 * The Original Code is Mozilla Communicator client code, released 18 * March 31, 1998. 19 * 20 * The Initial Developer of the Original Code is 21 * Netscape Communications Corporation. 22 * Portions created by the Initial Developer are Copyright (C) 1998 23 * the Initial Developer. All Rights Reserved. 24 * 25 * Contributor(s): 26 * 27 * Alternatively, the contents of this file may be used under the terms of 28 * either of the GNU General Public License Version 2 or later (the "GPL"), 29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 30 * in which case the provisions of the GPL or the LGPL are applicable instead 31 * of those above. If you wish to allow use of your version of this file only 32 * under the terms of either the GPL or the LGPL, and not to allow others to 33 * use your version of this file under the terms of the MPL, indicate your 34 * decision by deleting the provisions above and replace them with the notice 35 * and other provisions required by the GPL or the LGPL. If you do not delete 36 * the provisions above, a recipient may use your version of this file under 37 * the terms of any one of the MPL, the GPL or the LGPL. 38 * 39 * ***** END LICENSE BLOCK ***** */ 40 41/* 42 * JS parser. 43 * 44 * This is a recursive-descent parser for the JavaScript language specified by 45 * "The JavaScript 1.5 Language Specification". It uses lexical and semantic 46 * feedback to disambiguate non-LL(1) structures. It generates trees of nodes 47 * induced by the recursive parsing (not precise syntax trees, see jsparse.h). 48 * After tree construction, it rewrites trees to fold constants and evaluate 49 * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to 50 * generate bytecode. 51 * 52 * This parser attempts no error recovery. 53 */ 54#include "jsstddef.h" 55#include <stdlib.h> 56#include <string.h> 57#include <math.h> 58#include "jstypes.h" 59#include "jsarena.h" /* Added by JSIFY */ 60#include "jsutil.h" /* Added by JSIFY */ 61#include "jsapi.h" 62#include "jsarray.h" 63#include "jsatom.h" 64#include "jscntxt.h" 65#include "jsversion.h" 66#include "jsemit.h" 67#include "jsfun.h" 68#include "jsinterp.h" 69#include "jsiter.h" 70#include "jslock.h" 71#include "jsnum.h" 72#include "jsobj.h" 73#include "jsopcode.h" 74#include "jsparse.h" 75#include "jsscan.h" 76#include "jsscope.h" 77#include "jsscript.h" 78#include "jsstr.h" 79#include "jsstaticcheck.h" 80 81#if JS_HAS_XML_SUPPORT 82#include "jsxml.h" 83#endif 84 85#if JS_HAS_DESTRUCTURING 86#include "jsdhash.h" 87#endif 88 89/* 90 * Asserts to verify assumptions behind pn_ macros. 91 */ 92JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.atom) == 93 offsetof(JSParseNode, pn_u.apair.atom)); 94JS_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 104typedef JSParseNode * 105JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc); 106 107typedef JSParseNode * 108JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, 109 JSBool allowCallSyntax); 110 111typedef JSParseNode * 112JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, 113 JSTokenType tt, JSBool afterDot); 114 115typedef JSParseNode * 116JSParenParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, 117 JSParseNode *pn1, JSBool *genexp); 118 119static JSParser FunctionStmt; 120static JSParser FunctionExpr; 121static JSParser Statements; 122static JSParser Statement; 123static JSParser Variables; 124static JSParser Expr; 125static JSParser AssignExpr; 126static JSParser CondExpr; 127static JSParser OrExpr; 128static JSParser AndExpr; 129static JSParser BitOrExpr; 130static JSParser BitXorExpr; 131static JSParser BitAndExpr; 132static JSParser EqExpr; 133static JSParser RelExpr; 134static JSParser ShiftExpr; 135static JSParser AddExpr; 136static JSParser MulExpr; 137static JSParser UnaryExpr; 138static JSMemberParser MemberExpr; 139static JSPrimaryParser PrimaryExpr; 140static 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 155static uint32 parsenodes = 0; 156static uint32 maxparsenodes = 0; 157static uint32 recyclednodes = 0; 158#endif 159 160JSBool 161js_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 186void 187js_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 198void 199js_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 208JSParsedObjectBox * 209js_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 233void 234js_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 246static JSParseNode * 247RecycleTree(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 265static JSParseNode * 266NewOrRecycledNode(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 */ 328static JSParseNode * 329NewParseNode(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 346static JSParseNode * 347NewBinary(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 420static JSTokenType 421CheckGetterOrSetter(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 */ 466JSParseNode * 467js_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 */ 502extern JSScript * 503js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame, 504 JSPrincipals *principals, uint32 tcflags, 505 const jschar *chars, size_t length, 506 FILE *file, const char *filename, uintN lineno) 507{ 508 JSParseContext pc; 509 JSArenaPool codePool, notePool; 510 JSCodeGenerator cg; 511 JSTokenType tt; 512 JSParseNode *pn; 513 uint32 scriptGlobals; 514 JSScript *script; 515#ifdef METER_PARSENODES 516 void *sbrk(ptrdiff_t), *before = sbrk(0); 517#endif 518 519 JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | 520 TCF_STATIC_DEPTH_MASK))); 521 522 /* 523 * The scripted callerFrame can only be given for compile-and-go scripts 524 * and non-zero static depth requires callerFrame. 525 */ 526 JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO); 527 JS_ASSERT_IF(TCF_GET_STATIC_DEPTH(tcflags) != 0, callerFrame); 528 529 if (!js_InitParseContext(cx, &pc, principals, callerFrame, chars, length, 530 file, filename, lineno)) { 531 return NULL; 532 } 533 534 JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode), 535 &cx->scriptStackQuota); 536 JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote), 537 &cx->scriptStackQuota); 538 js_InitCodeGenerator(cx, &cg, &pc, &codePool, ¬ePool, 539 pc.tokenStream.lineno); 540 541 MUST_FLOW_THROUGH("out"); 542 cg.treeContext.flags |= (uint16) tcflags; 543 cg.treeContext.u.scopeChain = scopeChain; 544 cg.staticDepth = TCF_GET_STATIC_DEPTH(tcflags); 545 546 if ((tcflags & TCF_COMPILE_N_GO) && callerFrame && callerFrame->fun) { 547 /* 548 * An eval script in a caller frame needs to have its enclosing function 549 * captured in case it uses an upvar reference, and someone wishes to 550 * decompile it while running. 551 */ 552 JSParsedObjectBox *pob = js_NewParsedObjectBox(cx, &pc, callerFrame->callee); 553 pob->emitLink = cg.objectList.lastPob; 554 cg.objectList.lastPob = pob; 555 cg.objectList.length++; 556 } 557 558 /* Inline Statements() to emit as we go to save space. */ 559 for (;;) { 560 pc.tokenStream.flags |= TSF_OPERAND; 561 tt = js_PeekToken(cx, &pc.tokenStream); 562 pc.tokenStream.flags &= ~TSF_OPERAND; 563 if (tt <= TOK_EOF) { 564 if (tt == TOK_EOF) 565 break; 566 JS_ASSERT(tt == TOK_ERROR); 567 script = NULL; 568 goto out; 569 } 570 571 pn = Statement(cx, &pc.tokenStream, &cg.treeContext); 572 if (!pn) { 573 script = NULL; 574 goto out; 575 } 576 JS_ASSERT(!cg.treeContext.blockNode); 577 578 if (!js_FoldConstants(cx, pn, &cg.treeContext) || 579 !js_EmitTree(cx, &cg, pn)) { 580 script = NULL; 581 goto out; 582 } 583 RecycleTree(pn, &cg.treeContext); 584 } 585 586 /* 587 * Global variables and regexps shares the index space with locals. Due to 588 * incremental code generation we need to patch the bytecode to adjust the 589 * local references to skip the globals. 590 */ 591 scriptGlobals = cg.treeContext.ngvars + cg.regexpList.length; 592 if (scriptGlobals != 0) { 593 jsbytecode *code, *end; 594 JSOp op; 595 const JSCodeSpec *cs; 596 uintN len, slot; 597 598 if (scriptGlobals >= SLOTNO_LIMIT) 599 goto too_many_slots; 600 code = CG_BASE(&cg); 601 for (end = code + CG_OFFSET(&cg); code != end; code += len) { 602 JS_ASSERT(code < end); 603 op = (JSOp) *code; 604 cs = &js_CodeSpec[op]; 605 len = (cs->length > 0) 606 ? (uintN) cs->length 607 : js_GetVariableBytecodeLength(code); 608 if (JOF_TYPE(cs->format) == JOF_LOCAL || 609 (JOF_TYPE(cs->format) == JOF_SLOTATOM)) { 610 /* 611 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be 612 * emitted only for a function. 613 */ 614 JS_ASSERT((JOF_TYPE(cs->format) == JOF_SLOTATOM) == 615 (op == JSOP_GETLOCALPROP)); 616 slot = GET_SLOTNO(code); 617 slot += scriptGlobals; 618 if (slot >= SLOTNO_LIMIT) 619 goto too_many_slots; 620 SET_SLOTNO(code, slot); 621 } 622 } 623 } 624 625#ifdef METER_PARSENODES 626 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n", 627 (char *)sbrk(0) - (char *)before, 628 parsenodes, 629 maxparsenodes, 630 parsenodes - recyclednodes); 631 before = sbrk(0); 632#endif 633 634 /* 635 * Nowadays the threaded interpreter needs a stop instruction, so we 636 * do have to emit that here. 637 */ 638 if (js_Emit1(cx, &cg, JSOP_STOP) < 0) { 639 script = NULL; 640 goto out; 641 } 642#ifdef METER_PARSENODES 643 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n", 644 (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount); 645#endif 646#ifdef JS_ARENAMETER 647 JS_DumpArenaStats(stdout); 648#endif 649 script = js_NewScriptFromCG(cx, &cg); 650 651#ifdef JS_SCOPE_DEPTH_METER 652 if (script) { 653 JSObject *obj = scopeChain; 654 uintN depth = 1; 655 while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) 656 ++depth; 657 JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth); 658 } 659#endif 660 661 out: 662 js_FinishCodeGenerator(cx, &cg); 663 JS_FinishArenaPool(&codePool); 664 JS_FinishArenaPool(¬ePool); 665 js_FinishParseContext(cx, &pc); 666 return script; 667 668 too_many_slots: 669 js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL, 670 JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS); 671 script = NULL; 672 goto out; 673} 674 675/* 676 * Insist on a final return before control flows out of pn. Try to be a bit 677 * smart about loops: do {...; return e2;} while(0) at the end of a function 678 * that contains an early return e1 will get a strict warning. Similarly for 679 * iloops: while (true){...} is treated as though ... returns. 680 */ 681#define ENDS_IN_OTHER 0 682#define ENDS_IN_RETURN 1 683#define ENDS_IN_BREAK 2 684 685static int 686HasFinalReturn(JSParseNode *pn) 687{ 688 JSParseNode *pn2, *pn3; 689 uintN rv, rv2, hasDefault; 690 691 switch (pn->pn_type) { 692 case TOK_LC: 693 if (!pn->pn_head) 694 return ENDS_IN_OTHER; 695 return HasFinalReturn(PN_LAST(pn)); 696 697 case TOK_IF: 698 if (!pn->pn_kid3) 699 return ENDS_IN_OTHER; 700 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3); 701 702 case TOK_WHILE: 703 pn2 = pn->pn_left; 704 if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE) 705 return ENDS_IN_RETURN; 706 if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval) 707 return ENDS_IN_RETURN; 708 return ENDS_IN_OTHER; 709 710 case TOK_DO: 711 pn2 = pn->pn_right; 712 if (pn2->pn_type == TOK_PRIMARY) { 713 if (pn2->pn_op == JSOP_FALSE) 714 return HasFinalReturn(pn->pn_left); 715 if (pn2->pn_op == JSOP_TRUE) 716 return ENDS_IN_RETURN; 717 } 718 if (pn2->pn_type == TOK_NUMBER) { 719 if (pn2->pn_dval == 0) 720 return HasFinalReturn(pn->pn_left); 721 return ENDS_IN_RETURN; 722 } 723 return ENDS_IN_OTHER; 724 725 case TOK_FOR: 726 pn2 = pn->pn_left; 727 if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2) 728 return ENDS_IN_RETURN; 729 return ENDS_IN_OTHER; 730 731 case TOK_SWITCH: 732 rv = ENDS_IN_RETURN; 733 hasDefault = ENDS_IN_OTHER; 734 pn2 = pn->pn_right; 735 if (pn2->pn_type == TOK_LEXICALSCOPE) 736 pn2 = pn2->pn_expr; 737 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) { 738 if (pn2->pn_type == TOK_DEFAULT) 739 hasDefault = ENDS_IN_RETURN; 740 pn3 = pn2->pn_right; 741 JS_ASSERT(pn3->pn_type == TOK_LC); 742 if (pn3->pn_head) { 743 rv2 = HasFinalReturn(PN_LAST(pn3)); 744 if (rv2 == ENDS_IN_OTHER && pn2->pn_next) 745 /* Falling through to next case or default. */; 746 else 747 rv &= rv2; 748 } 749 } 750 /* If a final switch has no default case, we judge it harshly. */ 751 rv &= hasDefault; 752 return rv; 753 754 case TOK_BREAK: 755 return ENDS_IN_BREAK; 756 757 case TOK_WITH: 758 return HasFinalReturn(pn->pn_right); 759 760 case TOK_RETURN: 761 return ENDS_IN_RETURN; 762 763 case TOK_COLON: 764 case TOK_LEXICALSCOPE: 765 return HasFinalReturn(pn->pn_expr); 766 767 case TOK_THROW: 768 return ENDS_IN_RETURN; 769 770 case TOK_TRY: 771 /* If we have a finally block that returns, we are done. */ 772 if (pn->pn_kid3) { 773 rv = HasFinalReturn(pn->pn_kid3); 774 if (rv == ENDS_IN_RETURN) 775 return rv; 776 } 777 778 /* Else check the try block and any and all catch statements. */ 779 rv = HasFinalReturn(pn->pn_kid1); 780 if (pn->pn_kid2) { 781 JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST); 782 for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next) 783 rv &= HasFinalReturn(pn2); 784 } 785 return rv; 786 787 case TOK_CATCH: 788 /* Check this catch block's body. */ 789 return HasFinalReturn(pn->pn_kid3); 790 791 case TOK_LET: 792 /* Non-binary let statements are let declarations. */ 793 if (pn->pn_arity != PN_BINARY) 794 return ENDS_IN_OTHER; 795 return HasFinalReturn(pn->pn_right); 796 797 default: 798 return ENDS_IN_OTHER; 799 } 800} 801 802static JSBool 803ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum, 804 uintN anonerrnum) 805{ 806 const char *name; 807 808 JS_ASSERT(tc->flags & TCF_IN_FUNCTION); 809 if (tc->u.fun->atom) { 810 name = js_AtomToPrintableString(cx, tc->u.fun->atom); 811 } else { 812 errnum = anonerrnum; 813 name = NULL; 814 } 815 return js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, flags, 816 errnum, name); 817} 818 819static JSBool 820CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn) 821{ 822 JS_ASSERT(tc->flags & TCF_IN_FUNCTION); 823 return HasFinalReturn(pn) == ENDS_IN_RETURN || 824 ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT, 825 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE); 826} 827 828static JSParseNode * 829FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 830{ 831 JSStmtInfo stmtInfo; 832 uintN oldflags, firstLine; 833 JSParseNode *pn; 834 835 JS_ASSERT(tc->flags & TCF_IN_FUNCTION); 836 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); 837 stmtInfo.flags = SIF_BODY_BLOCK; 838 839 oldflags = tc->flags; 840 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID); 841 842 /* 843 * Save the body's first line, and store it in pn->pn_pos.begin.lineno 844 * later, because we may have not peeked in ts yet, so Statements won't 845 * acquire a valid pn->pn_pos.begin from the current token. 846 */ 847 firstLine = ts->lineno; 848#if JS_HAS_EXPR_CLOSURES 849 if (CURRENT_TOKEN(ts).type == TOK_LC) { 850 pn = Statements(cx, ts, tc); 851 } else { 852 pn = NewParseNode(cx, ts, PN_UNARY, tc); 853 if (pn) { 854 pn->pn_kid = AssignExpr(cx, ts, tc); 855 if (!pn->pn_kid) { 856 pn = NULL; 857 } else { 858 if (tc->flags & TCF_FUN_IS_GENERATOR) { 859 ReportBadReturn(cx, tc, JSREPORT_ERROR, 860 JSMSG_BAD_GENERATOR_RETURN, 861 JSMSG_BAD_ANON_GENERATOR_RETURN); 862 pn = NULL; 863 } else { 864 pn->pn_type = TOK_RETURN; 865 pn->pn_op = JSOP_RETURN; 866 pn->pn_pos.end = pn->pn_kid->pn_pos.end; 867 } 868 } 869 } 870 } 871#else 872 pn = Statements(cx, ts, tc); 873#endif 874 875 if (pn) { 876 js_PopStatement(tc); 877 pn->pn_pos.begin.lineno = firstLine; 878 879 /* Check for falling off the end of a function that returns a value. */ 880 if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) && 881 !CheckFinalReturn(cx, tc, pn)) { 882 pn = NULL; 883 } 884 } 885 886 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS)); 887 return pn; 888} 889 890/* 891 * Compile a JS function body, which might appear as the value of an event 892 * handler attribute in an HTML <INPUT> tag. 893 */ 894JSBool 895js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals, 896 const jschar *chars, size_t length, 897 const char *filename, uintN lineno) 898{ 899 JSParseContext pc; 900 JSArenaPool codePool, notePool; 901 JSCodeGenerator funcg; 902 JSParseNode *pn; 903 904 if (!js_InitParseContext(cx, &pc, principals, NULL, chars, length, NULL, 905 filename, lineno)) { 906 return JS_FALSE; 907 } 908 909 /* No early return from this point until js_FinishParseContext call. */ 910 JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode), 911 &cx->scriptStackQuota); 912 JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote), 913 &cx->scriptStackQuota); 914 js_InitCodeGenerator(cx, &funcg, &pc, &codePool, ¬ePool, 915 pc.tokenStream.lineno); 916 funcg.treeContext.flags |= TCF_IN_FUNCTION; 917 funcg.treeContext.u.fun = fun; 918 919 /* 920 * Farble the body so that it looks like a block statement to js_EmitTree, 921 * which is called beneath FunctionBody; see Statements, further below in 922 * this file. FunctionBody pushes a STMT_BLOCK record around its call to 923 * Statements, so Statements will not compile each statement as it loops 924 * to save JSParseNode space -- it will not compile at all, only build a 925 * JSParseNode tree. 926 * 927 * Therefore we must fold constants, allocate try notes, and generate code 928 * for this function, including a stop opcode at the end. 929 */ 930 CURRENT_TOKEN(&pc.tokenStream).type = TOK_LC; 931 pn = FunctionBody(cx, &pc.tokenStream, &funcg.treeContext); 932 if (pn) { 933 if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) { 934 js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL, 935 JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); 936 pn = NULL; 937 } else { 938 if (!js_FoldConstants(cx, pn, &funcg.treeContext) || 939 !js_EmitFunctionScript(cx, &funcg, pn)) { 940 pn = NULL; 941 } 942 } 943 } 944 945 /* Restore saved state and release code generation arenas. */ 946 js_FinishCodeGenerator(cx, &funcg); 947 JS_FinishArenaPool(&codePool); 948 JS_FinishArenaPool(¬ePool); 949 js_FinishParseContext(cx, &pc); 950 return pn != NULL; 951} 952 953/* 954 * Parameter block types for the several Binder functions. We use a common 955 * helper function signature in order to share code among destructuring and 956 * simple variable declaration parsers. In the destructuring case, the binder 957 * function is called indirectly from the variable declaration parser by way 958 * of CheckDestructuring and its friends. 959 */ 960typedef struct BindData BindData; 961 962typedef JSBool 963(*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc); 964 965struct BindData { 966 JSParseNode *pn; /* error source coordinate */ 967 JSOp op; /* prolog bytecode or nop */ 968 Binder binder; /* binder, discriminates u */ 969 union { 970 struct { 971 uintN overflow; 972 } let; 973 } u; 974}; 975 976static JSBool 977BindArg(JSContext *cx, JSAtom *atom, JSTreeContext *tc) 978{ 979 const char *name; 980 981 /* 982 * Check for a duplicate parameter name, a "feature" required by ECMA-262. 983 */ 984 JS_ASSERT(tc->flags & TCF_IN_FUNCTION); 985 if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) { 986 name = js_AtomToPrintableString(cx, atom); 987 if (!name || 988 !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, 989 JSREPORT_WARNING | JSREPORT_STRICT, 990 JSMSG_DUPLICATE_FORMAL, 991 name)) { 992 return JS_FALSE; 993 } 994 } 995 996 return js_AddLocal(cx, tc->u.fun, atom, JSLOCAL_ARG); 997} 998 999static JSBool 1000BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom, 1001 JSLocalKind localKind) 1002{ 1003 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); 1004 1005 /* 1006 * Don't bind a variable with the hidden name 'arguments', per ECMA-262. 1007 * Instead 'var arguments' always restates the predefined property of the 1008 * activation objects with unhidden name 'arguments'. Assignment to such 1009 * a variable must be handled specially. 1010 */ 1011 if (atom == cx->runtime->atomState.argumentsAtom) 1012 return JS_TRUE; 1013 1014 return js_AddLocal(cx, fun, atom, localKind); 1015} 1016 1017#if JS_HAS_DESTRUCTURING 1018/* 1019 * Forward declaration to maintain top-down presentation. 1020 */ 1021static JSParseNode * 1022DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, 1023 JSTokenType tt); 1024 1025static JSBool 1026BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, 1027 JSTreeContext *tc) 1028{ 1029 JSAtomListElement *ale; 1030 const char *name; 1031 1032 JS_ASSERT(tc->flags & TCF_IN_FUNCTION); 1033 ATOM_LIST_SEARCH(ale, &tc->decls, atom); 1034 if (!ale) { 1035 ale = js_IndexAtom(cx, atom, &tc->decls); 1036 if (!ale) 1037 return JS_FALSE; 1038 ALE_SET_JSOP(ale, data->op); 1039 } 1040 1041 if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) { 1042 name = js_AtomToPrintableString(cx, atom); 1043 if (!name || 1044 !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, 1045 JSREPORT_WARNING | JSREPORT_STRICT, 1046 JSMSG_DUPLICATE_FORMAL, 1047 name)) { 1048 return JS_FALSE; 1049 } 1050 } else { 1051 if (!BindLocalVariable(cx, tc->u.fun, atom, JSLOCAL_VAR)) 1052 return JS_FALSE; 1053 } 1054 return JS_TRUE; 1055} 1056#endif /* JS_HAS_DESTRUCTURING */ 1057 1058static JSFunction * 1059NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom, 1060 uintN lambda) 1061{ 1062 JSObject *parent; 1063 JSFunction *fun; 1064 1065 JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0); 1066 parent = (tc->flags & TCF_IN_FUNCTION) 1067 ? FUN_OBJECT(tc->u.fun) 1068 : tc->u.scopeChain; 1069 fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED | lambda, 1070 parent, atom); 1071 if (fun && !(tc->flags & TCF_COMPILE_N_GO)) { 1072 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun)); 1073 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun)); 1074 } 1075 return fun; 1076} 1077 1078static JSParseNode * 1079FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, 1080 uintN lambda) 1081{ 1082 JSOp op, prevop; 1083 JSParseNode *pn, *body, *result; 1084 JSTokenType tt; 1085 JSAtom *funAtom; 1086 JSParsedObjectBox *funpob; 1087 JSAtomListElement *ale; 1088 JSFunction *fun; 1089 JSTreeContext funtc; 1090#if JS_HAS_DESTRUCTURING 1091 JSParseNode *item, *list = NULL; 1092#endif 1093 1094 /* Make a TOK_FUNCTION node. */ 1095#if JS_HAS_GETTER_SETTER 1096 op = CURRENT_TOKEN(ts).t_op; 1097#endif 1098 pn = NewParseNode(cx, ts, PN_FUNC, tc); 1099 if (!pn) 1100 return NULL; 1101#ifdef DEBUG 1102 pn->pn_index = (uint32) -1; 1103#endif 1104 1105 /* Scan the optional function name into funAtom. */ 1106 ts->flags |= TSF_KEYWORD_IS_NAME; 1107 tt = js_GetToken(cx, ts); 1108 ts->flags &= ~TSF_KEYWORD_IS_NAME; 1109 if (tt == TOK_NAME) { 1110 funAtom = CURRENT_TOKEN(ts).t_atom; 1111 } else { 1112 if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) { 1113 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, 1114 JSMSG_SYNTAX_ERROR); 1115 return NULL; 1116 } 1117 funAtom = NULL; 1118 js_UngetToken(ts); 1119 } 1120 1121 /* 1122 * Record names for function statements in tc->decls so we know when to 1123 * avoid optimizing variable references that might name a function. 1124 */ 1125 if (lambda == 0 && funAtom) { 1126 ATOM_LIST_SEARCH(ale, &tc->decls, funAtom); 1127 if (ale) { 1128 prevop = ALE_JSOP(ale); 1129 if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) { 1130 const char *name = js_AtomToPrintableString(cx, funAtom); 1131 if (!name || 1132 !js_ReportCompileErrorNumber(cx, ts, NULL, 1133 (prevop != JSOP_DEFCONST) 1134 ? JSREPORT_WARNING | 1135 JSREPORT_STRICT 1136 : JSREPORT_ERROR, 1137 JSMSG_REDECLARED_VAR, 1138 (prevop == JSOP_DEFFUN) 1139 ? js_function_str 1140 : (prevop == JSOP_DEFCONST) 1141 ? js_const_str 1142 : js_var_str, 1143 name)) { 1144 return NULL; 1145 } 1146 } 1147 if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR) 1148 tc->flags |= TCF_FUN_CLOSURE_VS_VAR; 1149 } else { 1150 ale = js_IndexAtom(cx, funAtom, &tc->decls); 1151 if (!ale) 1152 return NULL; 1153 } 1154 ALE_SET_JSOP(ale, JSOP_DEFFUN); 1155 1156 /* 1157 * A function nested at top level inside another's body needs only a 1158 * local variable to bind its name to its value, and not an activation 1159 * object property (it might also need the activation property, if the 1160 * outer function contains with statements, e.g., but the stack slot 1161 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a 1162 * JSOP_GETLOCAL bytecode). 1163 */ 1164 if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) { 1165 JSLocalKind localKind; 1166 1167 /* 1168 * Define a property on the outer function so that BindNameToSlot 1169 * can properly optimize accesses. Note that we need a variable, 1170 * not an argument, for the function statement. Thus we add a 1171 * variable even if the parameter with the given name already 1172 * exists. 1173 */ 1174 localKind = js_LookupLocal(cx, tc->u.fun, funAtom, NULL); 1175 if (localKind == JSLOCAL_NONE || localKind == JSLOCAL_ARG) { 1176 if (!js_AddLocal(cx, tc->u.fun, funAtom, JSLOCAL_VAR)) 1177 return NULL; 1178 } 1179 } 1180 } 1181 1182 fun = NewCompilerFunction(cx, tc, funAtom, lambda); 1183 if (!fun) 1184 return NULL; 1185 1186#if JS_HAS_GETTER_SETTER 1187 if (op != JSOP_NOP) 1188 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER; 1189#endif 1190 1191 /* 1192 * Create wrapping box for fun->object early to protect against a 1193 * last-ditch GC. 1194 */ 1195 funpob = js_NewParsedObjectBox(cx, tc->parseContext, FUN_OBJECT(fun)); 1196 if (!funpob) 1197 return NULL; 1198 1199 /* Initialize early for possible flags mutation via DestructuringExpr. */ 1200 TREE_CONTEXT_INIT(&funtc, tc->parseContext); 1201 funtc.flags |= TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO); 1202 funtc.u.fun = fun; 1203 1204 /* Now parse formal argument list and compute fun->nargs. */ 1205 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL); 1206 if (!js_MatchToken(cx, ts, TOK_RP)) { 1207 do { 1208 tt = js_GetToken(cx, ts); 1209 switch (tt) { 1210#if JS_HAS_DESTRUCTURING 1211 case TOK_LB: 1212 case TOK_LC: 1213 { 1214 BindData data; 1215 JSParseNode *lhs, *rhs; 1216 jsint slot; 1217 1218 /* 1219 * A destructuring formal parameter turns into one or more 1220 * local variables initialized from properties of a single 1221 * anonymous positional parameter, so here we must tweak our 1222 * binder and its data. 1223 */ 1224 data.pn = NULL; 1225 data.op = JSOP_DEFVAR; 1226 data.binder = BindDestructuringArg; 1227 lhs = DestructuringExpr(cx, &data, &funtc, tt); 1228 if (!lhs) 1229 return NULL; 1230 1231 /* 1232 * Adjust fun->nargs to count the single anonymous positional 1233 * parameter that is to be destructured. 1234 */ 1235 slot = fun->nargs; 1236 if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG)) 1237 return NULL; 1238 1239 /* 1240 * Synthesize a destructuring assignment from the single 1241 * anonymous positional parameter into the destructuring 1242 * left-hand-side expression and accumulate it in list. 1243 */ 1244 rhs = NewParseNode(cx, ts, PN_NAME, tc); 1245 if (!rhs) 1246 return NULL; 1247 rhs->pn_type = TOK_NAME; 1248 rhs->pn_op = JSOP_GETARG; 1249 rhs->pn_atom = cx->runtime->atomState.emptyAtom; 1250 rhs->pn_slot = slot; 1251 1252 item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc); 1253 if (!item) 1254 return NULL; 1255 if (!list) { 1256 list = NewParseNode(cx, ts, PN_LIST, tc); 1257 if (!list) 1258 return NULL; 1259 list->pn_type = TOK_COMMA; 1260 PN_INIT_LIST(list); 1261 } 1262 PN_APPEND(list, item); 1263 break; 1264 } 1265#endif /* JS_HAS_DESTRUCTURING */ 1266 1267 case TOK_NAME: 1268 if (!BindArg(cx, CURRENT_TOKEN(ts).t_atom, &funtc)) 1269 return NULL; 1270 break; 1271 1272 default: 1273 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, 1274 JSMSG_MISSING_FORMAL); 1275 return NULL; 1276 } 1277 } while (js_MatchToken(cx, ts, TOK_COMMA)); 1278 1279 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL); 1280 } 1281 1282#if JS_HAS_EXPR_CLOSURES 1283 ts->flags |= TSF_OPERAND; 1284 tt = js_GetToken(cx, ts); 1285 ts->flags &= ~TSF_OPERAND; 1286 if (tt != TOK_LC) { 1287 js_UngetToken(ts); 1288 fun->flags |= JSFUN_EXPR_CLOSURE; 1289 } 1290#else 1291 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY); 1292#endif 1293 pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin; 1294 1295 body = FunctionBody(cx, ts, &funtc); 1296 if (!body) 1297 return NULL; 1298 1299#if JS_HAS_EXPR_CLOSURES 1300 if (tt == TOK_LC) 1301 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY); 1302 else if (lambda == 0) 1303 js_MatchToken(cx, ts, TOK_SEMI); 1304#else 1305 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY); 1306#endif 1307 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; 1308 1309#if JS_HAS_DESTRUCTURING 1310 /* 1311 * If there were destructuring formal parameters, prepend the initializing 1312 * comma expression that we synthesized to body. If the body is a lexical 1313 * scope node, we must make a special TOK_SEQ node, to prepend the formal 1314 * parameter destructuring code without bracing the decompilation of the 1315 * function body's lexical scope. 1316 */ 1317 if (list) { 1318 if (body->pn_arity != PN_LIST) { 1319 JSParseNode *block; 1320 1321 block = NewParseNode(cx, ts, PN_LIST, tc); 1322 if (!block) 1323 return NULL; 1324 block->pn_type = TOK_SEQ; 1325 block->pn_pos = body->pn_pos; 1326 PN_INIT_LIST_1(block, body); 1327 1328 body = block; 1329 } 1330 1331 item = NewParseNode(cx, ts, PN_UNARY, tc); 1332 if (!item) 1333 return NULL; 1334 1335 item->pn_type = TOK_SEMI; 1336 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin; 1337 item->pn_kid = list; 1338 item->pn_next = body->pn_head; 1339 body->pn_head = item; 1340 if (body->pn_tail == &body->pn_head) 1341 body->pn_tail = &item->pn_next; 1342 ++body->pn_count; 1343 } 1344#endif 1345 1346 /* 1347 * If we collected flags that indicate nested heavyweight functions, or 1348 * this function contains heavyweight-making statements (references to 1349 * __parent__ or __proto__; use of with, or eval; and assignment to 1350 * arguments), flag the function as heavyweight (requiring a call object 1351 * per invocation). 1352 */ 1353 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) { 1354 fun->flags |= JSFUN_HEAVYWEIGHT; 1355 tc->flags |= TCF_FUN_HEAVYWEIGHT; 1356 } else { 1357 /* 1358 * If this function is a named statement function not at top-level 1359 * (i.e. not a top-level function definiton or expression), then 1360 * our enclosing function, if any, must be heavyweight. 1361 * 1362 * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator, 1363 * so it won't be set here. Assert that it's not. We have to check 1364 * it later, in js_EmitTree, after js_EmitFunctionScript has traversed 1365 * the function's body. 1366 */ 1367 JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS)); 1368 if (lambda == 0 && funAtom && !AT_TOP_LEVEL(tc)) 1369 tc->flags |= TCF_FUN_HEAVYWEIGHT; 1370 } 1371 1372 result = pn; 1373 if (lambda != 0) { 1374 /* 1375 * ECMA ed. 3 standard: function expression, possibly anonymous. 1376 */ 1377 op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ; 1378 } else if (!funAtom) { 1379 /* 1380 * If this anonymous function definition is *not* embedded within a 1381 * larger expression, we treat it as an expression statement, not as 1382 * a function declaration -- and not as a syntax error (as ECMA-262 1383 * Edition 3 would have it). Backward compatibility must trump all, 1384 * unless JSOPTION_ANONFUNFIX is set. 1385 */ 1386 result = NewParseNode(cx, ts, PN_UNARY, tc); 1387 if (!result) 1388 return NULL; 1389 result->pn_type = TOK_SEMI; 1390 result->pn_pos = pn->pn_pos; 1391 result->pn_kid = pn; 1392 op = JSOP_ANONFUNOBJ; 1393 } else if (!AT_TOP_LEVEL(tc)) { 1394 /* 1395 * ECMA ed. 3 extension: a function expression statement not at the 1396 * top level, e.g., in a compound statement such as the "then" part 1397 * of an "if" statement, binds a closure only if control reaches that 1398 * sub-statement. 1399 */ 1400 op = JSOP_DEFFUN; 1401 } else { 1402 op = JSOP_NOP; 1403 } 1404 1405 pn->pn_funpob = funpob; 1406 pn->pn_op = op; 1407 pn->pn_body = body; 1408 pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS | TCF_COMPILE_N_GO); 1409 TREE_CONTEXT_FINISH(cx, &funtc); 1410 return result; 1411} 1412 1413static JSParseNode * 1414FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 1415{ 1416 return FunctionDef(cx, ts, tc, 0); 1417} 1418 1419static JSParseNode * 1420FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 1421{ 1422 return FunctionDef(cx, ts, tc, JSFUN_LAMBDA); 1423} 1424 1425/* 1426 * Parse the statements in a block, creating a TOK_LC node that lists the 1427 * statements' trees. If called from block-parsing code, the caller must 1428 * match { before and } after. 1429 */ 1430static JSParseNode * 1431Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 1432{ 1433 JSParseNode *pn, *pn2, *saveBlock; 1434 JSTokenType tt; 1435 1436 JS_CHECK_RECURSION(cx, return NULL); 1437 1438 pn = NewParseNode(cx, ts, PN_LIST, tc); 1439 if (!pn) 1440 return NULL; 1441 saveBlock = tc->blockNode; 1442 tc->blockNode = pn; 1443 PN_INIT_LIST(pn); 1444 1445 for (;;) { 1446 ts->flags |= TSF_OPERAND; 1447 tt = js_PeekToken(cx, ts); 1448 ts->flags &= ~TSF_OPERAND; 1449 if (tt <= TOK_EOF || tt == TOK_RC) { 1450 if (tt == TOK_ERROR) 1451 return NULL; 1452 break; 1453 } 1454 pn2 = Statement(cx, ts, tc); 1455 if (!pn2) { 1456 if (ts->flags & TSF_EOF) 1457 ts->flags |= TSF_UNEXPECTED_EOF; 1458 return NULL; 1459 } 1460 1461 if (pn2->pn_type == TOK_FUNCTION) { 1462 /* 1463 * PNX_FUNCDEFS notifies the emitter that the block contains top- 1464 * level function definitions that should be processed before the 1465 * rest of nodes. 1466 * 1467 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It 1468 * is relevant only for function definitions not at top-level, 1469 * which we call function statements. 1470 */ 1471 if (AT_TOP_LEVEL(tc)) 1472 pn->pn_extra |= PNX_FUNCDEFS; 1473 else 1474 tc->flags |= TCF_HAS_FUNCTION_STMT; 1475 } 1476 PN_APPEND(pn, pn2); 1477 } 1478 1479 /* 1480 * Handle the case where there was a let declaration under this block. If 1481 * it replaced tc->blockNode with a new block node then we must refresh pn 1482 * and then restore tc->blockNode. 1483 */ 1484 if (tc->blockNode != pn) 1485 pn = tc->blockNode; 1486 tc->blockNode = saveBlock; 1487 1488 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; 1489 return pn; 1490} 1491 1492static JSParseNode * 1493Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 1494{ 1495 JSParseNode *pn; 1496 1497 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND); 1498 pn = ParenExpr(cx, ts, tc, NULL, NULL); 1499 if (!pn) 1500 return NULL; 1501 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); 1502 1503 /* 1504 * Check for (a = b) and warn about possible (a == b) mistype iff b's 1505 * operator has greater precedence than ==. 1506 */ 1507 if (pn->pn_type == TOK_ASSIGN && 1508 pn->pn_op == JSOP_NOP && 1509 pn->pn_right->pn_type > TOK_EQOP) 1510 { 1511 if (!js_ReportCompileErrorNumber(cx, ts, NULL, 1512 JSREPORT_WARNING | JSREPORT_STRICT, 1513 JSMSG_EQUAL_AS_ASSIGN, 1514 "")) { 1515 return NULL; 1516 } 1517 } 1518 return pn; 1519} 1520 1521static JSBool 1522MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) 1523{ 1524 JSAtom *label; 1525 JSTokenType tt; 1526 1527 tt = js_PeekTokenSameLine(cx, ts); 1528 if (tt == TOK_ERROR) 1529 return JS_FALSE; 1530 if (tt == TOK_NAME) { 1531 (void) js_GetToken(cx, ts); 1532 label = CURRENT_TOKEN(ts).t_atom; 1533 } else { 1534 label = NULL; 1535 } 1536 pn->pn_atom = label; 1537 return JS_TRUE; 1538} 1539 1540static JSBool 1541BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) 1542{ 1543 JSObje…
Large files files are truncated, but you can click here to view the full file