/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=99: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS parser. * * This is a recursive-descent parser for the JavaScript language specified by * "The JavaScript 1.5 Language Specification". It uses lexical and semantic * feedback to disambiguate non-LL(1) structures. It generates trees of nodes * induced by the recursive parsing (not precise syntax trees, see jsparse.h). * After tree construction, it rewrites trees to fold constants and evaluate * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to * generate bytecode. * * This parser attempts no error recovery. */ #include #include #include #include "jstypes.h" #include "jsstdint.h" #include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jscntxt.h" #include "jsversion.h" #include "jsemit.h" #include "jsfun.h" #include "jsinterp.h" #include "jsiter.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jsparse.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #include "jsstaticcheck.h" #include "jslibmath.h" #include "jsvector.h" #if JS_HAS_XML_SUPPORT #include "jsxml.h" #endif #if JS_HAS_DESTRUCTURING #include "jsdhash.h" #endif /* * Asserts to verify assumptions behind pn_ macros. */ #define pn_offsetof(m) offsetof(JSParseNode, m) JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses)); JS_STATIC_ASSERT(pn_offsetof(pn_u.name.atom) == pn_offsetof(pn_u.apair.atom)); #undef pn_offsetof /* * JS parsers, from lowest to highest precedence. * * Each parser takes a context, a token stream, and a tree context struct. * Each returns a parse node tree or null on error. */ typedef JSParseNode * JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc); typedef JSParseNode * JSVariablesParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, bool inLetHead); typedef JSParseNode * JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowCallSyntax); typedef JSParseNode * JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSTokenType tt, JSBool afterDot); typedef JSParseNode * JSParenParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSParseNode *pn1, JSBool *genexp); static JSParser FunctionStmt; static JSParser FunctionExpr; static JSParser Statements; static JSParser Statement; static JSVariablesParser Variables; static JSParser Expr; static JSParser AssignExpr; static JSParser CondExpr; static JSParser OrExpr; static JSParser AndExpr; static JSParser BitOrExpr; static JSParser BitXorExpr; static JSParser BitAndExpr; static JSParser EqExpr; static JSParser RelExpr; static JSParser ShiftExpr; static JSParser AddExpr; static JSParser MulExpr; static JSParser UnaryExpr; static JSMemberParser MemberExpr; static JSPrimaryParser PrimaryExpr; static JSParenParser ParenExpr; /* * Insist that the next token be of type tt, or report errno and return null. * NB: this macro uses cx and ts from its lexical environment. */ #define MUST_MATCH_TOKEN(tt, errno) \ JS_BEGIN_MACRO \ if (js_GetToken(cx, ts) != tt) { \ js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \ return NULL; \ } \ JS_END_MACRO #ifdef METER_PARSENODES static uint32 parsenodes = 0; static uint32 maxparsenodes = 0; static uint32 recyclednodes = 0; #endif void JSParseNode::become(JSParseNode *pn2) { JS_ASSERT(!pn_defn); JS_ASSERT(!pn2->pn_defn); JS_ASSERT(!pn_used); if (pn2->pn_used) { JSParseNode **pnup = &pn2->pn_lexdef->dn_uses; while (*pnup != pn2) pnup = &(*pnup)->pn_link; *pnup = this; pn_link = pn2->pn_link; pn_used = true; pn2->pn_link = NULL; pn2->pn_used = false; } /* If this is a function node fix up the pn_funbox->node back-pointer. */ if (PN_TYPE(pn2) == TOK_FUNCTION && pn2->pn_arity == PN_FUNC) pn2->pn_funbox->node = this; pn_type = pn2->pn_type; pn_op = pn2->pn_op; pn_arity = pn2->pn_arity; pn_parens = pn2->pn_parens; pn_u = pn2->pn_u; pn2->clear(); } void JSParseNode::clear() { pn_type = TOK_EOF; pn_op = JSOP_NOP; pn_used = pn_defn = false; pn_arity = PN_NULLARY; pn_parens = false; } bool JSCompiler::init(const jschar *base, size_t length, FILE *fp, const char *filename, uintN lineno) { JSContext *cx = context; tempPoolMark = JS_ARENA_MARK(&cx->tempPool); if (!tokenStream.init(cx, base, length, fp, filename, lineno)) { JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark); return false; } /* Root atoms and objects allocated for the parsed tree. */ JS_KEEP_ATOMS(cx->runtime); JS_PUSH_TEMP_ROOT_COMPILER(cx, this, &tempRoot); return true; } JSCompiler::~JSCompiler() { JSContext *cx = context; if (principals) JSPRINCIPALS_DROP(cx, principals); JS_ASSERT(tempRoot.u.compiler == this); JS_POP_TEMP_ROOT(cx, &tempRoot); JS_UNKEEP_ATOMS(cx->runtime); tokenStream.close(cx); JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark); } void JSCompiler::setPrincipals(JSPrincipals *prin) { JS_ASSERT(!principals); if (prin) JSPRINCIPALS_HOLD(context, prin); principals = prin; } JSObjectBox * JSCompiler::newObjectBox(JSObject *obj) { JS_ASSERT(obj); /* * We use JSContext.tempPool to allocate parsed objects and place them on * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas * containing the entries must be alive until we are done with scanning, * parsing and code generation for the whole script or top-level function. */ JSObjectBox *objbox; JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool); if (!objbox) { js_ReportOutOfScriptQuota(context); return NULL; } objbox->traceLink = traceListHead; traceListHead = objbox; objbox->emitLink = NULL; objbox->object = obj; return objbox; } JSFunctionBox * JSCompiler::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc) { JS_ASSERT(obj); JS_ASSERT(HAS_FUNCTION_CLASS(obj)); /* * We use JSContext.tempPool to allocate parsed objects and place them on * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas * containing the entries must be alive until we are done with scanning, * parsing and code generation for the whole script or top-level function. */ JSFunctionBox *funbox; JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool); if (!funbox) { js_ReportOutOfScriptQuota(context); return NULL; } funbox->traceLink = traceListHead; traceListHead = funbox; funbox->emitLink = NULL; funbox->object = obj; funbox->node = fn; funbox->siblings = tc->functionList; tc->functionList = funbox; ++tc->compiler->functionCount; funbox->kids = NULL; funbox->parent = tc->funbox; funbox->queued = false; funbox->inLoop = false; for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) { if (STMT_IS_LOOP(stmt)) { funbox->inLoop = true; break; } } funbox->level = tc->staticLevel; funbox->tcflags = TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO); return funbox; } void JSCompiler::trace(JSTracer *trc) { JSObjectBox *objbox; JS_ASSERT(tempRoot.u.compiler == this); objbox = traceListHead; while (objbox) { JS_CALL_OBJECT_TRACER(trc, objbox->object, "parser.object"); objbox = objbox->traceLink; } } static void UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc); static void UnlinkFunctionBox(JSParseNode *pn, JSTreeContext *tc) { JSFunctionBox *funbox = pn->pn_funbox; if (funbox) { JS_ASSERT(funbox->node == pn); funbox->node = NULL; JSFunctionBox **funboxp = &tc->functionList; while (*funboxp) { if (*funboxp == funbox) { *funboxp = funbox->siblings; break; } funboxp = &(*funboxp)->siblings; } uint16 oldflags = tc->flags; JSFunctionBox *oldlist = tc->functionList; tc->flags = (uint16) funbox->tcflags; tc->functionList = funbox->kids; UnlinkFunctionBoxes(pn->pn_body, tc); funbox->kids = tc->functionList; tc->flags = oldflags; tc->functionList = oldlist; // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList). pn->pn_funbox = NULL; } } static void UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc) { if (pn) { switch (pn->pn_arity) { case PN_NULLARY: return; case PN_UNARY: UnlinkFunctionBoxes(pn->pn_kid, tc); return; case PN_BINARY: UnlinkFunctionBoxes(pn->pn_left, tc); UnlinkFunctionBoxes(pn->pn_right, tc); return; case PN_TERNARY: UnlinkFunctionBoxes(pn->pn_kid1, tc); UnlinkFunctionBoxes(pn->pn_kid2, tc); UnlinkFunctionBoxes(pn->pn_kid3, tc); return; case PN_LIST: for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) UnlinkFunctionBoxes(pn2, tc); return; case PN_FUNC: UnlinkFunctionBox(pn, tc); return; case PN_NAME: UnlinkFunctionBoxes(pn->maybeExpr(), tc); return; case PN_NAMESET: UnlinkFunctionBoxes(pn->pn_tree, tc); } } } static void RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc); static JSParseNode * RecycleTree(JSParseNode *pn, JSTreeContext *tc) { JSParseNode *next, **head; if (!pn) return NULL; /* Catch back-to-back dup recycles. */ JS_ASSERT(pn != tc->compiler->nodeList); next = pn->pn_next; if (pn->pn_used || pn->pn_defn) { /* * JSAtomLists own definition nodes along with their used-node chains. * Defer recycling such nodes until we unwind to top level to avoid * linkage overhead or (alternatively) unlinking runtime complexity. * Yes, this means dead code can contribute to static analysis results! * * Do recycle kids here, since they are no longer needed. */ pn->pn_next = NULL; RecycleFuncNameKids(pn, tc); } else { UnlinkFunctionBoxes(pn, tc); head = &tc->compiler->nodeList; pn->pn_next = *head; *head = pn; #ifdef METER_PARSENODES recyclednodes++; #endif } return next; } static void RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc) { switch (pn->pn_arity) { case PN_FUNC: UnlinkFunctionBox(pn, tc); /* FALL THROUGH */ case PN_NAME: /* * Only a definition node might have a non-null strong pn_expr link * to recycle, but we test !pn_used to handle PN_FUNC fall through. * Every node with the pn_used flag set has a non-null pn_lexdef * weak reference to its definition node. */ if (!pn->pn_used && pn->pn_expr) { RecycleTree(pn->pn_expr, tc); pn->pn_expr = NULL; } break; default: JS_ASSERT(PN_TYPE(pn) == TOK_FUNCTION); } } static JSParseNode * NewOrRecycledNode(JSTreeContext *tc) { JSParseNode *pn, *pn2; pn = tc->compiler->nodeList; if (!pn) { JSContext *cx = tc->compiler->context; JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool); if (!pn) js_ReportOutOfScriptQuota(cx); } else { tc->compiler->nodeList = pn->pn_next; /* Recycle immediate descendents only, to save work and working set. */ switch (pn->pn_arity) { case PN_FUNC: RecycleTree(pn->pn_body, tc); break; case PN_LIST: pn2 = pn->pn_head; if (pn2) { while (pn2 && !pn2->pn_used && !pn2->pn_defn) pn2 = pn2->pn_next; if (pn2) { pn2 = pn->pn_head; do { pn2 = RecycleTree(pn2, tc); } while (pn2); } else { *pn->pn_tail = tc->compiler->nodeList; tc->compiler->nodeList = pn->pn_head; #ifdef METER_PARSENODES recyclednodes += pn->pn_count; #endif break; } } break; case PN_TERNARY: RecycleTree(pn->pn_kid1, tc); RecycleTree(pn->pn_kid2, tc); RecycleTree(pn->pn_kid3, tc); break; case PN_BINARY: if (pn->pn_left != pn->pn_right) RecycleTree(pn->pn_left, tc); RecycleTree(pn->pn_right, tc); break; case PN_UNARY: RecycleTree(pn->pn_kid, tc); break; case PN_NAME: if (!pn->pn_used) RecycleTree(pn->pn_expr, tc); break; case PN_NULLARY: break; } } if (pn) { #ifdef METER_PARSENODES parsenodes++; if (parsenodes - recyclednodes > maxparsenodes) maxparsenodes = parsenodes - recyclednodes; #endif pn->pn_used = pn->pn_defn = false; memset(&pn->pn_u, 0, sizeof pn->pn_u); pn->pn_next = NULL; } return pn; } static inline void InitParseNode(JSParseNode *pn, JSTokenType type, JSOp op, JSParseNodeArity arity) { pn->pn_type = type; pn->pn_op = op; pn->pn_arity = arity; pn->pn_parens = false; JS_ASSERT(!pn->pn_used); JS_ASSERT(!pn->pn_defn); pn->pn_next = pn->pn_link = NULL; } /* * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's * temporary arena. */ static JSParseNode * NewParseNode(JSParseNodeArity arity, JSTreeContext *tc) { JSParseNode *pn; JSToken *tp; pn = NewOrRecycledNode(tc); if (!pn) return NULL; tp = &CURRENT_TOKEN(&tc->compiler->tokenStream); InitParseNode(pn, tp->type, JSOP_NOP, arity); pn->pn_pos = tp->pos; return pn; } static inline void InitNameNodeCommon(JSParseNode *pn, JSTreeContext *tc) { pn->pn_expr = NULL; pn->pn_cookie = FREE_UPVAR_COOKIE; pn->pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0; if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK) pn->pn_dflags |= PND_BLOCKCHILD; pn->pn_blockid = tc->blockid(); } static JSParseNode * NewNameNode(JSContext *cx, JSTokenStream *ts, JSAtom *atom, JSTreeContext *tc) { JSParseNode *pn; pn = NewParseNode(PN_NAME, tc); if (pn) { pn->pn_atom = atom; InitNameNodeCommon(pn, tc); } return pn; } static JSParseNode * NewBinary(JSTokenType tt, JSOp op, JSParseNode *left, JSParseNode *right, JSTreeContext *tc) { JSParseNode *pn, *pn1, *pn2; if (!left || !right) return NULL; /* * Flatten a left-associative (left-heavy) tree of a given operator into * a list, to reduce js_FoldConstants and js_EmitTree recursion. */ if (PN_TYPE(left) == tt && PN_OP(left) == op && (js_CodeSpec[op].format & JOF_LEFTASSOC)) { if (left->pn_arity != PN_LIST) { pn1 = left->pn_left, pn2 = left->pn_right; left->pn_arity = PN_LIST; left->pn_parens = false; left->initList(pn1); left->append(pn2); if (tt == TOK_PLUS) { if (pn1->pn_type == TOK_STRING) left->pn_xflags |= PNX_STRCAT; else if (pn1->pn_type != TOK_NUMBER) left->pn_xflags |= PNX_CANTFOLD; if (pn2->pn_type == TOK_STRING) left->pn_xflags |= PNX_STRCAT; else if (pn2->pn_type != TOK_NUMBER) left->pn_xflags |= PNX_CANTFOLD; } } left->append(right); left->pn_pos.end = right->pn_pos.end; if (tt == TOK_PLUS) { if (right->pn_type == TOK_STRING) left->pn_xflags |= PNX_STRCAT; else if (right->pn_type != TOK_NUMBER) left->pn_xflags |= PNX_CANTFOLD; } return left; } /* * Fold constant addition immediately, to conserve node space and, what's * more, so js_FoldConstants never sees mixed addition and concatenation * operations with more than one leading non-string operand in a PN_LIST * generated for expressions such as 1 + 2 + "pt" (which should evaluate * to "3pt", not "12pt"). */ if (tt == TOK_PLUS && left->pn_type == TOK_NUMBER && right->pn_type == TOK_NUMBER) { left->pn_dval += right->pn_dval; left->pn_pos.end = right->pn_pos.end; RecycleTree(right, tc); return left; } pn = NewOrRecycledNode(tc); if (!pn) return NULL; InitParseNode(pn, tt, op, PN_BINARY); pn->pn_pos.begin = left->pn_pos.begin; pn->pn_pos.end = right->pn_pos.end; pn->pn_left = left; pn->pn_right = right; return pn; } #if JS_HAS_GETTER_SETTER static JSTokenType CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt) { JSAtom *atom; JSRuntime *rt; JSOp op; const char *name; JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME); atom = CURRENT_TOKEN(ts).t_atom; rt = cx->runtime; if (atom == rt->atomState.getterAtom) op = JSOP_GETTER; else if (atom == rt->atomState.setterAtom) op = JSOP_SETTER; else return TOK_NAME; if (js_PeekTokenSameLine(cx, ts) != tt) return TOK_NAME; (void) js_GetToken(cx, ts); if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) { js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_GETTER_OR_SETTER, (op == JSOP_GETTER) ? js_getter_str : js_setter_str); return TOK_ERROR; } CURRENT_TOKEN(ts).t_op = op; if (JS_HAS_STRICT_OPTION(cx)) { name = js_AtomToPrintableString(cx, atom); if (!name || !js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DEPRECATED_USAGE, name)) { return TOK_ERROR; } } return tt; } #endif static bool GenerateBlockId(JSTreeContext *tc, uint32& blockid) { if (tc->blockidGen == JS_BIT(20)) { JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, "program"); return false; } blockid = tc->blockidGen++; return true; } static bool GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc) { JS_ASSERT(tc->topStmt); JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt)); JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE); if (!GenerateBlockId(tc, tc->topStmt->blockid)) return false; pn->pn_blockid = tc->topStmt->blockid; return true; } /* * Parse a top-level JS script. */ JSParseNode * JSCompiler::parse(JSObject *chain) { /* * Protect atoms from being collected by a GC activation, which might * - nest on this thread due to out of memory (the so-called "last ditch" * GC attempted within js_NewGCThing), or * - run for any reason on another thread if this thread is suspended on * an object lock before it finishes generating bytecode into a script * protected from the GC by a root or a stack frame reference. */ JSTreeContext tc(this); tc.scopeChain = chain; if (!GenerateBlockId(&tc, tc.bodyid)) return NULL; JSParseNode *pn = Statements(context, TS(this), &tc); if (pn) { if (!js_MatchToken(context, TS(this), TOK_EOF)) { js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); pn = NULL; } else { if (!js_FoldConstants(context, pn, &tc)) pn = NULL; } } return pn; } JS_STATIC_ASSERT(FREE_STATIC_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS)); static inline bool SetStaticLevel(JSTreeContext *tc, uintN staticLevel) { /* * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE * (0xffffffff) and other cookies with that level. * * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and * practically speaking it leaves more than enough room for upvars. In fact * we might want to split cookie fields giving fewer bits for skip and more * for slot, but only based on evidence. */ if (staticLevel >= FREE_STATIC_LEVEL) { JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL, JSMSG_TOO_DEEP, js_function_str); return false; } tc->staticLevel = staticLevel; return true; } /* * Compile a top-level script. */ JSScript * JSCompiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame, JSPrincipals *principals, uint32 tcflags, const jschar *chars, size_t length, FILE *file, const char *filename, uintN lineno, JSString *source /* = NULL */) { JSCompiler jsc(cx, principals, callerFrame); JSArenaPool codePool, notePool; JSTokenType tt; JSParseNode *pn; uint32 scriptGlobals; JSScript *script; #ifdef METER_PARSENODES void *sbrk(ptrdiff_t), *before = sbrk(0); #endif JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_STATIC_LEVEL_MASK))); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO); JS_ASSERT_IF(TCF_GET_STATIC_LEVEL(tcflags) != 0, callerFrame); if (!jsc.init(chars, length, file, filename, lineno)) return NULL; JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode), &cx->scriptStackQuota); JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote), &cx->scriptStackQuota); JSCodeGenerator cg(&jsc, &codePool, ¬ePool, jsc.tokenStream.lineno); MUST_FLOW_THROUGH("out"); /* Null script early in case of error, to reduce our code footprint. */ script = NULL; cg.flags |= uint16(tcflags); cg.scopeChain = scopeChain; if (!SetStaticLevel(&cg, TCF_GET_STATIC_LEVEL(tcflags))) goto out; /* * If funbox is non-null after we create the new script, callerFrame->fun * was saved in the 0th object table entry. */ JSObjectBox *funbox; funbox = NULL; if (tcflags & TCF_COMPILE_N_GO) { if (source) { /* * Save eval program source in script->atomMap.vector[0] for the * eval cache (see obj_eval in jsobj.cpp). */ JSAtom *atom = js_AtomizeString(cx, source, 0); if (!atom || !cg.atomList.add(&jsc, atom)) goto out; } if (callerFrame && callerFrame->fun) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ funbox = jsc.newObjectBox(FUN_OBJECT(callerFrame->fun)); if (!funbox) goto out; funbox->emitLink = cg.objectList.lastbox; cg.objectList.lastbox = funbox; cg.objectList.length++; } } /* * Inline Statements to emit as we go to save AST space. We must generate * our script-body blockid since we aren't calling Statements. */ uint32 bodyid; if (!GenerateBlockId(&cg, bodyid)) goto out; cg.bodyid = bodyid; #if JS_HAS_XML_SUPPORT pn = NULL; bool onlyXML; onlyXML = true; #endif CG_SWITCH_TO_PROLOG(&cg); if (js_Emit1(cx, &cg, JSOP_TRACE) < 0) goto out; CG_SWITCH_TO_MAIN(&cg); for (;;) { jsc.tokenStream.flags |= TSF_OPERAND; tt = js_PeekToken(cx, &jsc.tokenStream); jsc.tokenStream.flags &= ~TSF_OPERAND; if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); goto out; } pn = Statement(cx, &jsc.tokenStream, &cg); if (!pn) goto out; JS_ASSERT(!cg.blockNode); if (!js_FoldConstants(cx, pn, &cg)) goto out; if (cg.functionList) { if (!jsc.analyzeFunctions(cg.functionList, cg.flags)) goto out; cg.functionList = NULL; } if (!js_EmitTree(cx, &cg, pn)) goto out; #if JS_HAS_XML_SUPPORT if (PN_TYPE(pn) != TOK_SEMI || !pn->pn_kid || !TREE_TYPE_IS_XML(PN_TYPE(pn->pn_kid))) { onlyXML = false; } #endif RecycleTree(pn, &cg); } #if JS_HAS_XML_SUPPORT /* * Prevent XML data theft via