/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
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- /* -*- 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 "jsstddef.h"
- #include <stdlib.h>
- #include <string.h>
- #include <math.h>
- #include "jstypes.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"
- #if JS_HAS_XML_SUPPORT
- #include "jsxml.h"
- #endif
- #if JS_HAS_DESTRUCTURING
- #include "jsdhash.h"
- #endif
- /*
- * Asserts to verify assumptions behind pn_ macros.
- */
- JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.atom) ==
- offsetof(JSParseNode, pn_u.apair.atom));
- JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.slot) ==
- offsetof(JSParseNode, pn_u.lexical.slot));
- /*
- * 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 *
- 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 JSParser 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
- JSBool
- js_InitParseContext(JSContext *cx, JSParseContext *pc, JSPrincipals *principals,
- JSStackFrame *callerFrame,
- const jschar *base, size_t length,
- FILE *fp, const char *filename, uintN lineno)
- {
- JS_ASSERT_IF(callerFrame, callerFrame->script);
- pc->tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
- if (!js_InitTokenStream(cx, TS(pc), base, length, fp, filename, lineno)) {
- JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark);
- return JS_FALSE;
- }
- if (principals)
- JSPRINCIPALS_HOLD(cx, principals);
- pc->principals = principals;
- pc->callerFrame = callerFrame;
- pc->nodeList = NULL;
- pc->traceListHead = NULL;
- /* Root atoms and objects allocated for the parsed tree. */
- JS_KEEP_ATOMS(cx->runtime);
- JS_PUSH_TEMP_ROOT_PARSE_CONTEXT(cx, pc, &pc->tempRoot);
- return JS_TRUE;
- }
- void
- js_FinishParseContext(JSContext *cx, JSParseContext *pc)
- {
- if (pc->principals)
- JSPRINCIPALS_DROP(cx, pc->principals);
- JS_ASSERT(pc->tempRoot.u.parseContext == pc);
- JS_POP_TEMP_ROOT(cx, &pc->tempRoot);
- JS_UNKEEP_ATOMS(cx->runtime);
- js_CloseTokenStream(cx, TS(pc));
- JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark);
- }
- void
- js_InitCompilePrincipals(JSContext *cx, JSParseContext *pc,
- JSPrincipals *principals)
- {
- JS_ASSERT(!pc->principals);
- if (principals)
- JSPRINCIPALS_HOLD(cx, principals);
- pc->principals = principals;
- }
- JSParsedObjectBox *
- js_NewParsedObjectBox(JSContext *cx, JSParseContext *pc, JSObject *obj)
- {
- JSParsedObjectBox *pob;
- /*
- * 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.
- */
- JS_ASSERT(obj);
- JS_ARENA_ALLOCATE_TYPE(pob, JSParsedObjectBox, &cx->tempPool);
- if (!pob) {
- js_ReportOutOfScriptQuota(cx);
- return NULL;
- }
- pob->traceLink = pc->traceListHead;
- pob->emitLink = NULL;
- pob->object = obj;
- pc->traceListHead = pob;
- return pob;
- }
- void
- js_TraceParseContext(JSTracer *trc, JSParseContext *pc)
- {
- JSParsedObjectBox *pob;
- JS_ASSERT(pc->tempRoot.u.parseContext == pc);
- pob = pc->traceListHead;
- while (pob) {
- JS_CALL_OBJECT_TRACER(trc, pob->object, "parser.object");
- pob = pob->traceLink;
- }
- }
- static JSParseNode *
- RecycleTree(JSParseNode *pn, JSTreeContext *tc)
- {
- JSParseNode *next;
- if (!pn)
- return NULL;
- /* Catch back-to-back dup recycles. */
- JS_ASSERT(pn != tc->parseContext->nodeList);
- next = pn->pn_next;
- pn->pn_next = tc->parseContext->nodeList;
- tc->parseContext->nodeList = pn;
- #ifdef METER_PARSENODES
- recyclednodes++;
- #endif
- return next;
- }
- static JSParseNode *
- NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
- {
- JSParseNode *pn;
- pn = tc->parseContext->nodeList;
- if (!pn) {
- JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
- if (!pn)
- js_ReportOutOfScriptQuota(cx);
- } else {
- tc->parseContext->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:
- if (pn->pn_head) {
- /* XXX check for dup recycles in the list */
- *pn->pn_tail = tc->parseContext->nodeList;
- tc->parseContext->nodeList = pn->pn_head;
- #ifdef METER_PARSENODES
- recyclednodes += pn->pn_count;
- #endif
- }
- 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:
- RecycleTree(pn->pn_expr, tc);
- break;
- case PN_NULLARY:
- break;
- }
- }
- if (pn) {
- #ifdef METER_PARSENODES
- parsenodes++;
- if (parsenodes - recyclednodes > maxparsenodes)
- maxparsenodes = parsenodes - recyclednodes;
- #endif
- memset(&pn->pn_u, 0, sizeof pn->pn_u);
- pn->pn_next = NULL;
- }
- return pn;
- }
- /*
- * Allocate a JSParseNode from cx's temporary arena.
- */
- static JSParseNode *
- NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity,
- JSTreeContext *tc)
- {
- JSParseNode *pn;
- JSToken *tp;
- pn = NewOrRecycledNode(cx, tc);
- if (!pn)
- return NULL;
- tp = &CURRENT_TOKEN(ts);
- pn->pn_type = tp->type;
- pn->pn_pos = tp->pos;
- pn->pn_op = JSOP_NOP;
- pn->pn_arity = arity;
- return pn;
- }
- static JSParseNode *
- NewBinary(JSContext *cx, 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 (left->pn_type == tt &&
- left->pn_op == 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;
- PN_INIT_LIST_1(left, pn1);
- PN_APPEND(left, pn2);
- if (tt == TOK_PLUS) {
- if (pn1->pn_type == TOK_STRING)
- left->pn_extra |= PNX_STRCAT;
- else if (pn1->pn_type != TOK_NUMBER)
- left->pn_extra |= PNX_CANTFOLD;
- if (pn2->pn_type == TOK_STRING)
- left->pn_extra |= PNX_STRCAT;
- else if (pn2->pn_type != TOK_NUMBER)
- left->pn_extra |= PNX_CANTFOLD;
- }
- }
- PN_APPEND(left, right);
- left->pn_pos.end = right->pn_pos.end;
- if (tt == TOK_PLUS) {
- if (right->pn_type == TOK_STRING)
- left->pn_extra |= PNX_STRCAT;
- else if (right->pn_type != TOK_NUMBER)
- left->pn_extra |= 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(cx, tc);
- if (!pn)
- return NULL;
- pn->pn_type = tt;
- pn->pn_pos.begin = left->pn_pos.begin;
- pn->pn_pos.end = right->pn_pos.end;
- pn->pn_op = op;
- pn->pn_arity = PN_BINARY;
- 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
- /*
- * Parse a top-level JS script.
- */
- JSParseNode *
- js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc)
- {
- JSTreeContext tc;
- JSParseNode *pn;
- /*
- * 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.
- */
- TREE_CONTEXT_INIT(&tc, pc);
- tc.u.scopeChain = chain;
- pn = Statements(cx, TS(pc), &tc);
- if (pn) {
- if (!js_MatchToken(cx, TS(pc), TOK_EOF)) {
- js_ReportCompileErrorNumber(cx, TS(pc), NULL, JSREPORT_ERROR,
- JSMSG_SYNTAX_ERROR);
- pn = NULL;
- } else {
- pn->pn_type = TOK_LC;
- if (!js_FoldConstants(cx, pn, &tc))
- pn = NULL;
- }
- }
- TREE_CONTEXT_FINISH(cx, &tc);
- return pn;
- }
- /*
- * Compile a top-level script.
- */
- extern JSScript *
- js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
- JSPrincipals *principals, uint32 tcflags,
- const jschar *chars, size_t length,
- FILE *file, const char *filename, uintN lineno)
- {
- JSParseContext pc;
- JSArenaPool codePool, notePool;
- JSCodeGenerator cg;
- 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_DEPTH_MASK)));
- /*
- * The scripted callerFrame can only be given for compile-and-go scripts
- * and non-zero static depth requires callerFrame.
- */
- JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
- JS_ASSERT_IF(TCF_GET_STATIC_DEPTH(tcflags) != 0, callerFrame);
- if (!js_InitParseContext(cx, &pc, principals, callerFrame, 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);
- js_InitCodeGenerator(cx, &cg, &pc, &codePool, ¬ePool,
- pc.tokenStream.lineno);
- MUST_FLOW_THROUGH("out");
- cg.treeContext.flags |= (uint16) tcflags;
- cg.treeContext.u.scopeChain = scopeChain;
- cg.staticDepth = TCF_GET_STATIC_DEPTH(tcflags);
- if ((tcflags & TCF_COMPILE_N_GO) && callerFrame && callerFrame->fun) {
- /*
- * An eval script in a caller frame needs to have its enclosing function
- * captured in case it uses an upvar reference, and someone wishes to
- * decompile it while running.
- */
- JSParsedObjectBox *pob = js_NewParsedObjectBox(cx, &pc, callerFrame->callee);
- pob->emitLink = cg.objectList.lastPob;
- cg.objectList.lastPob = pob;
- cg.objectList.length++;
- }
- /* Inline Statements() to emit as we go to save space. */
- for (;;) {
- pc.tokenStream.flags |= TSF_OPERAND;
- tt = js_PeekToken(cx, &pc.tokenStream);
- pc.tokenStream.flags &= ~TSF_OPERAND;
- if (tt <= TOK_EOF) {
- if (tt == TOK_EOF)
- break;
- JS_ASSERT(tt == TOK_ERROR);
- script = NULL;
- goto out;
- }
- pn = Statement(cx, &pc.tokenStream, &cg.treeContext);
- if (!pn) {
- script = NULL;
- goto out;
- }
- JS_ASSERT(!cg.treeContext.blockNode);
- if (!js_FoldConstants(cx, pn, &cg.treeContext) ||
- !js_EmitTree(cx, &cg, pn)) {
- script = NULL;
- goto out;
- }
- RecycleTree(pn, &cg.treeContext);
- }
- /*
- * Global variables and regexps shares the index space with locals. Due to
- * incremental code generation we need to patch the bytecode to adjust the
- * local references to skip the globals.
- */
- scriptGlobals = cg.treeContext.ngvars + cg.regexpList.length;
- if (scriptGlobals != 0) {
- jsbytecode *code, *end;
- JSOp op;
- const JSCodeSpec *cs;
- uintN len, slot;
- if (scriptGlobals >= SLOTNO_LIMIT)
- goto too_many_slots;
- code = CG_BASE(&cg);
- for (end = code + CG_OFFSET(&cg); code != end; code += len) {
- JS_ASSERT(code < end);
- op = (JSOp) *code;
- cs = &js_CodeSpec[op];
- len = (cs->length > 0)
- ? (uintN) cs->length
- : js_GetVariableBytecodeLength(code);
- if (JOF_TYPE(cs->format) == JOF_LOCAL ||
- (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
- /*
- * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
- * emitted only for a function.
- */
- JS_ASSERT((JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
- (op == JSOP_GETLOCALPROP));
- slot = GET_SLOTNO(code);
- slot += scriptGlobals;
- if (slot >= SLOTNO_LIMIT)
- goto too_many_slots;
- SET_SLOTNO(code, slot);
- }
- }
- }
- #ifdef METER_PARSENODES
- printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
- (char *)sbrk(0) - (char *)before,
- parsenodes,
- maxparsenodes,
- parsenodes - recyclednodes);
- before = sbrk(0);
- #endif
- /*
- * Nowadays the threaded interpreter needs a stop instruction, so we
- * do have to emit that here.
- */
- if (js_Emit1(cx, &cg, JSOP_STOP) < 0) {
- script = NULL;
- goto out;
- }
- #ifdef METER_PARSENODES
- printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
- (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
- #endif
- #ifdef JS_ARENAMETER
- JS_DumpArenaStats(stdout);
- #endif
- script = js_NewScriptFromCG(cx, &cg);
- #ifdef JS_SCOPE_DEPTH_METER
- if (script) {
- JSObject *obj = scopeChain;
- uintN depth = 1;
- while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
- ++depth;
- JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
- }
- #endif
- out:
- js_FinishCodeGenerator(cx, &cg);
- JS_FinishArenaPool(&codePool);
- JS_FinishArenaPool(¬ePool);
- js_FinishParseContext(cx, &pc);
- return script;
- too_many_slots:
- js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
- JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
- script = NULL;
- goto out;
- }
- /*
- * Insist on a final return before control flows out of pn. Try to be a bit
- * smart about loops: do {...; return e2;} while(0) at the end of a function
- * that contains an early return e1 will get a strict warning. Similarly for
- * iloops: while (true){...} is treated as though ... returns.
- */
- #define ENDS_IN_OTHER 0
- #define ENDS_IN_RETURN 1
- #define ENDS_IN_BREAK 2
- static int
- HasFinalReturn(JSParseNode *pn)
- {
- JSParseNode *pn2, *pn3;
- uintN rv, rv2, hasDefault;
- switch (pn->pn_type) {
- case TOK_LC:
- if (!pn->pn_head)
- return ENDS_IN_OTHER;
- return HasFinalReturn(PN_LAST(pn));
- case TOK_IF:
- if (!pn->pn_kid3)
- return ENDS_IN_OTHER;
- return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
- case TOK_WHILE:
- pn2 = pn->pn_left;
- if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
- return ENDS_IN_RETURN;
- if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
- return ENDS_IN_RETURN;
- return ENDS_IN_OTHER;
- case TOK_DO:
- pn2 = pn->pn_right;
- if (pn2->pn_type == TOK_PRIMARY) {
- if (pn2->pn_op == JSOP_FALSE)
- return HasFinalReturn(pn->pn_left);
- if (pn2->pn_op == JSOP_TRUE)
- return ENDS_IN_RETURN;
- }
- if (pn2->pn_type == TOK_NUMBER) {
- if (pn2->pn_dval == 0)
- return HasFinalReturn(pn->pn_left);
- return ENDS_IN_RETURN;
- }
- return ENDS_IN_OTHER;
- case TOK_FOR:
- pn2 = pn->pn_left;
- if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
- return ENDS_IN_RETURN;
- return ENDS_IN_OTHER;
- case TOK_SWITCH:
- rv = ENDS_IN_RETURN;
- hasDefault = ENDS_IN_OTHER;
- pn2 = pn->pn_right;
- if (pn2->pn_type == TOK_LEXICALSCOPE)
- pn2 = pn2->pn_expr;
- for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
- if (pn2->pn_type == TOK_DEFAULT)
- hasDefault = ENDS_IN_RETURN;
- pn3 = pn2->pn_right;
- JS_ASSERT(pn3->pn_type == TOK_LC);
- if (pn3->pn_head) {
- rv2 = HasFinalReturn(PN_LAST(pn3));
- if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
- /* Falling through to next case or default. */;
- else
- rv &= rv2;
- }
- }
- /* If a final switch has no default case, we judge it harshly. */
- rv &= hasDefault;
- return rv;
- case TOK_BREAK:
- return ENDS_IN_BREAK;
- case TOK_WITH:
- return HasFinalReturn(pn->pn_right);
- case TOK_RETURN:
- return ENDS_IN_RETURN;
- case TOK_COLON:
- case TOK_LEXICALSCOPE:
- return HasFinalReturn(pn->pn_expr);
- case TOK_THROW:
- return ENDS_IN_RETURN;
- case TOK_TRY:
- /* If we have a finally block that returns, we are done. */
- if (pn->pn_kid3) {
- rv = HasFinalReturn(pn->pn_kid3);
- if (rv == ENDS_IN_RETURN)
- return rv;
- }
- /* Else check the try block and any and all catch statements. */
- rv = HasFinalReturn(pn->pn_kid1);
- if (pn->pn_kid2) {
- JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
- for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
- rv &= HasFinalReturn(pn2);
- }
- return rv;
- case TOK_CATCH:
- /* Check this catch block's body. */
- return HasFinalReturn(pn->pn_kid3);
- case TOK_LET:
- /* Non-binary let statements are let declarations. */
- if (pn->pn_arity != PN_BINARY)
- return ENDS_IN_OTHER;
- return HasFinalReturn(pn->pn_right);
- default:
- return ENDS_IN_OTHER;
- }
- }
- static JSBool
- ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
- uintN anonerrnum)
- {
- const char *name;
- JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
- if (tc->u.fun->atom) {
- name = js_AtomToPrintableString(cx, tc->u.fun->atom);
- } else {
- errnum = anonerrnum;
- name = NULL;
- }
- return js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, flags,
- errnum, name);
- }
- static JSBool
- CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
- {
- JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
- return HasFinalReturn(pn) == ENDS_IN_RETURN ||
- ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
- JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
- }
- static JSParseNode *
- FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
- {
- JSStmtInfo stmtInfo;
- uintN oldflags, firstLine;
- JSParseNode *pn;
- JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
- js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
- stmtInfo.flags = SIF_BODY_BLOCK;
- oldflags = tc->flags;
- tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
- /*
- * Save the body's first line, and store it in pn->pn_pos.begin.lineno
- * later, because we may have not peeked in ts yet, so Statements won't
- * acquire a valid pn->pn_pos.begin from the current token.
- */
- firstLine = ts->lineno;
- #if JS_HAS_EXPR_CLOSURES
- if (CURRENT_TOKEN(ts).type == TOK_LC) {
- pn = Statements(cx, ts, tc);
- } else {
- pn = NewParseNode(cx, ts, PN_UNARY, tc);
- if (pn) {
- pn->pn_kid = AssignExpr(cx, ts, tc);
- if (!pn->pn_kid) {
- pn = NULL;
- } else {
- if (tc->flags & TCF_FUN_IS_GENERATOR) {
- ReportBadReturn(cx, tc, JSREPORT_ERROR,
- JSMSG_BAD_GENERATOR_RETURN,
- JSMSG_BAD_ANON_GENERATOR_RETURN);
- pn = NULL;
- } else {
- pn->pn_type = TOK_RETURN;
- pn->pn_op = JSOP_RETURN;
- pn->pn_pos.end = pn->pn_kid->pn_pos.end;
- }
- }
- }
- }
- #else
- pn = Statements(cx, ts, tc);
- #endif
- if (pn) {
- js_PopStatement(tc);
- pn->pn_pos.begin.lineno = firstLine;
- /* Check for falling off the end of a function that returns a value. */
- if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) &&
- !CheckFinalReturn(cx, tc, pn)) {
- pn = NULL;
- }
- }
- tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));
- return pn;
- }
- /*
- * Compile a JS function body, which might appear as the value of an event
- * handler attribute in an HTML <INPUT> tag.
- */
- JSBool
- js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
- const jschar *chars, size_t length,
- const char *filename, uintN lineno)
- {
- JSParseContext pc;
- JSArenaPool codePool, notePool;
- JSCodeGenerator funcg;
- JSParseNode *pn;
- if (!js_InitParseContext(cx, &pc, principals, NULL, chars, length, NULL,
- filename, lineno)) {
- return JS_FALSE;
- }
- /* No early return from this point until js_FinishParseContext call. */
- JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
- &cx->scriptStackQuota);
- JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote),
- &cx->scriptStackQuota);
- js_InitCodeGenerator(cx, &funcg, &pc, &codePool, ¬ePool,
- pc.tokenStream.lineno);
- funcg.treeContext.flags |= TCF_IN_FUNCTION;
- funcg.treeContext.u.fun = fun;
- /*
- * Farble the body so that it looks like a block statement to js_EmitTree,
- * which is called beneath FunctionBody; see Statements, further below in
- * this file. FunctionBody pushes a STMT_BLOCK record around its call to
- * Statements, so Statements will not compile each statement as it loops
- * to save JSParseNode space -- it will not compile at all, only build a
- * JSParseNode tree.
- *
- * Therefore we must fold constants, allocate try notes, and generate code
- * for this function, including a stop opcode at the end.
- */
- CURRENT_TOKEN(&pc.tokenStream).type = TOK_LC;
- pn = FunctionBody(cx, &pc.tokenStream, &funcg.treeContext);
- if (pn) {
- if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) {
- js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
- JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
- pn = NULL;
- } else {
- if (!js_FoldConstants(cx, pn, &funcg.treeContext) ||
- !js_EmitFunctionScript(cx, &funcg, pn)) {
- pn = NULL;
- }
- }
- }
- /* Restore saved state and release code generation arenas. */
- js_FinishCodeGenerator(cx, &funcg);
- JS_FinishArenaPool(&codePool);
- JS_FinishArenaPool(¬ePool);
- js_FinishParseContext(cx, &pc);
- return pn != NULL;
- }
- /*
- * Parameter block types for the several Binder functions. We use a common
- * helper function signature in order to share code among destructuring and
- * simple variable declaration parsers. In the destructuring case, the binder
- * function is called indirectly from the variable declaration parser by way
- * of CheckDestructuring and its friends.
- */
- typedef struct BindData BindData;
- typedef JSBool
- (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
- struct BindData {
- JSParseNode *pn; /* error source coordinate */
- JSOp op; /* prolog bytecode or nop */
- Binder binder; /* binder, discriminates u */
- union {
- struct {
- uintN overflow;
- } let;
- } u;
- };
- static JSBool
- BindArg(JSContext *cx, JSAtom *atom, JSTreeContext *tc)
- {
- const char *name;
- /*
- * Check for a duplicate parameter name, a "feature" required by ECMA-262.
- */
- JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
- if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {
- name = js_AtomToPrintableString(cx, atom);
- if (!name ||
- !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL,
- JSREPORT_WARNING | JSREPORT_STRICT,
- JSMSG_DUPLICATE_FORMAL,
- name)) {
- return JS_FALSE;
- }
- }
- return js_AddLocal(cx, tc->u.fun, atom, JSLOCAL_ARG);
- }
- static JSBool
- BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
- JSLocalKind localKind)
- {
- JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
- /*
- * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
- * Instead 'var arguments' always restates the predefined property of the
- * activation objects with unhidden name 'arguments'. Assignment to such
- * a variable must be handled specially.
- */
- if (atom == cx->runtime->atomState.argumentsAtom)
- return JS_TRUE;
- return js_AddLocal(cx, fun, atom, localKind);
- }
- #if JS_HAS_DESTRUCTURING
- /*
- * Forward declaration to maintain top-down presentation.
- */
- static JSParseNode *
- DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
- JSTokenType tt);
- static JSBool
- BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
- JSTreeContext *tc)
- {
- JSAtomListElement *ale;
- const char *name;
- JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
- ATOM_LIST_SEARCH(ale, &tc->decls, atom);
- if (!ale) {
- ale = js_IndexAtom(cx, atom, &tc->decls);
- if (!ale)
- return JS_FALSE;
- ALE_SET_JSOP(ale, data->op);
- }
- if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {
- name = js_AtomToPrintableString(cx, atom);
- if (!name ||
- !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
- JSREPORT_WARNING | JSREPORT_STRICT,
- JSMSG_DUPLICATE_FORMAL,
- name)) {
- return JS_FALSE;
- }
- } else {
- if (!BindLocalVariable(cx, tc->u.fun, atom, JSLOCAL_VAR))
- return JS_FALSE;
- }
- return JS_TRUE;
- }
- #endif /* JS_HAS_DESTRUCTURING */
- static JSFunction *
- NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom,
- uintN lambda)
- {
- JSObject *parent;
- JSFunction *fun;
- JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
- parent = (tc->flags & TCF_IN_FUNCTION)
- ? FUN_OBJECT(tc->u.fun)
- : tc->u.scopeChain;
- fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
- parent, atom);
- if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
- STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
- STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
- }
- return fun;
- }
- static JSParseNode *
- FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
- uintN lambda)
- {
- JSOp op, prevop;
- JSParseNode *pn, *body, *result;
- JSTokenType tt;
- JSAtom *funAtom;
- JSParsedObjectBox *funpob;
- JSAtomListElement *ale;
- JSFunction *fun;
- JSTreeContext funtc;
- #if JS_HAS_DESTRUCTURING
- JSParseNode *item, *list = NULL;
- #endif
- /* Make a TOK_FUNCTION node. */
- #if JS_HAS_GETTER_SETTER
- op = CURRENT_TOKEN(ts).t_op;
- #endif
- pn = NewParseNode(cx, ts, PN_FUNC, tc);
- if (!pn)
- return NULL;
- #ifdef DEBUG
- pn->pn_index = (uint32) -1;
- #endif
- /* Scan the optional function name into funAtom. */
- ts->flags |= TSF_KEYWORD_IS_NAME;
- tt = js_GetToken(cx, ts);
- ts->flags &= ~TSF_KEYWORD_IS_NAME;
- if (tt == TOK_NAME) {
- funAtom = CURRENT_TOKEN(ts).t_atom;
- } else {
- if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) {
- js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
- JSMSG_SYNTAX_ERROR);
- return NULL;
- }
- funAtom = NULL;
- js_UngetToken(ts);
- }
- /*
- * Record names for function statements in tc->decls so we know when to
- * avoid optimizing variable references that might name a function.
- */
- if (lambda == 0 && funAtom) {
- ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
- if (ale) {
- prevop = ALE_JSOP(ale);
- if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
- const char *name = js_AtomToPrintableString(cx, funAtom);
- if (!name ||
- !js_ReportCompileErrorNumber(cx, ts, NULL,
- (prevop != JSOP_DEFCONST)
- ? JSREPORT_WARNING |
- JSREPORT_STRICT
- : JSREPORT_ERROR,
- JSMSG_REDECLARED_VAR,
- (prevop == JSOP_DEFFUN)
- ? js_function_str
- : (prevop == JSOP_DEFCONST)
- ? js_const_str
- : js_var_str,
- name)) {
- return NULL;
- }
- }
- if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR)
- tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
- } else {
- ale = js_IndexAtom(cx, funAtom, &tc->decls);
- if (!ale)
- return NULL;
- }
- ALE_SET_JSOP(ale, JSOP_DEFFUN);
- /*
- * A function nested at top level inside another's body needs only a
- * local variable to bind its name to its value, and not an activation
- * object property (it might also need the activation property, if the
- * outer function contains with statements, e.g., but the stack slot
- * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
- * JSOP_GETLOCAL bytecode).
- */
- if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) {
- JSLocalKind localKind;
- /*
- * Define a property on the outer function so that BindNameToSlot
- * can properly optimize accesses. Note that we need a variable,
- * not an argument, for the function statement. Thus we add a
- * variable even if the parameter with the given name already
- * exists.
- */
- localKind = js_LookupLocal(cx, tc->u.fun, funAtom, NULL);
- if (localKind == JSLOCAL_NONE || localKind == JSLOCAL_ARG) {
- if (!js_AddLocal(cx, tc->u.fun, funAtom, JSLOCAL_VAR))
- return NULL;
- }
- }
- }
- fun = NewCompilerFunction(cx, tc, funAtom, lambda);
- if (!fun)
- return NULL;
- #if JS_HAS_GETTER_SETTER
- if (op != JSOP_NOP)
- fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
- #endif
- /*
- * Create wrapping box for fun->object early to protect against a
- * last-ditch GC.
- */
- funpob = js_NewParsedObjectBox(cx, tc->parseContext, FUN_OBJECT(fun));
- if (!funpob)
- return NULL;
- /* Initialize early for possible flags mutation via DestructuringExpr. */
- TREE_CONTEXT_INIT(&funtc, tc->parseContext);
- funtc.flags |= TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO);
- funtc.u.fun = fun;
- /* Now parse formal argument list and compute fun->nargs. */
- MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
- if (!js_MatchToken(cx, ts, TOK_RP)) {
- do {
- tt = js_GetToken(cx, ts);
- switch (tt) {
- #if JS_HAS_DESTRUCTURING
- case TOK_LB:
- case TOK_LC:
- {
- BindData data;
- JSParseNode *lhs, *rhs;
- jsint slot;
- /*
- * A destructuring formal parameter turns into one or more
- * local variables initialized from properties of a single
- * anonymous positional parameter, so here we must tweak our
- * binder and its data.
- */
- data.pn = NULL;
- data.op = JSOP_DEFVAR;
- data.binder = BindDestructuringArg;
- lhs = DestructuringExpr(cx, &data, &funtc, tt);
- if (!lhs)
- return NULL;
- /*
- * Adjust fun->nargs to count the single anonymous positional
- * parameter that is to be destructured.
- */
- slot = fun->nargs;
- if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG))
- return NULL;
- /*
- * Synthesize a destructuring assignment from the single
- * anonymous positional parameter into the destructuring
- * left-hand-side expression and accumulate it in list.
- */
- rhs = NewParseNode(cx, ts, PN_NAME, tc);
- if (!rhs)
- return NULL;
- rhs->pn_type = TOK_NAME;
- rhs->pn_op = JSOP_GETARG;
- rhs->pn_atom = cx->runtime->atomState.emptyAtom;
- rhs->pn_slot = slot;
- item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc);
- if (!item)
- return NULL;
- if (!list) {
- list = NewParseNode(cx, ts, PN_LIST, tc);
- if (!list)
- return NULL;
- list->pn_type = TOK_COMMA;
- PN_INIT_LIST(list);
- }
- PN_APPEND(list, item);
- break;
- }
- #endif /* JS_HAS_DESTRUCTURING */
- case TOK_NAME:
- if (!BindArg(cx, CURRENT_TOKEN(ts).t_atom, &funtc))
- return NULL;
- break;
- default:
- js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
- JSMSG_MISSING_FORMAL);
- return NULL;
- }
- } while (js_MatchToken(cx, ts, TOK_COMMA));
- MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
- }
- #if JS_HAS_EXPR_CLOSURES
- ts->flags |= TSF_OPERAND;
- tt = js_GetToken(cx, ts);
- ts->flags &= ~TSF_OPERAND;
- if (tt != TOK_LC) {
- js_UngetToken(ts);
- fun->flags |= JSFUN_EXPR_CLOSURE;
- }
- #else
- MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
- #endif
- pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
- body = FunctionBody(cx, ts, &funtc);
- if (!body)
- return NULL;
- #if JS_HAS_EXPR_CLOSURES
- if (tt == TOK_LC)
- MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
- else if (lambda == 0)
- js_MatchToken(cx, ts, TOK_SEMI);
- #else
- MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
- #endif
- pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
- #if JS_HAS_DESTRUCTURING
- /*
- * If there were destructuring formal parameters, prepend the initializing
- * comma expression that we synthesized to body. If the body is a lexical
- * scope node, we must make a special TOK_SEQ node, to prepend the formal
- * parameter destructuring code without bracing the decompilation of the
- * function body's lexical scope.
- */
- if (list) {
- if (body->pn_arity != PN_LIST) {
- JSParseNode *block;
- block = NewParseNode(cx, ts, PN_LIST, tc);
- if (!block)
- return NULL;
- block->pn_type = TOK_SEQ;
- block->pn_pos = body->pn_pos;
- PN_INIT_LIST_1(block, body);
- body = block;
- }
- item = NewParseNode(cx, ts, PN_UNARY, tc);
- if (!item)
- return NULL;
- item->pn_type = TOK_SEMI;
- item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
- item->pn_kid = list;
- item->pn_next = body->pn_head;
- body->pn_head = item;
- if (body->pn_tail == &body->pn_head)
- body->pn_tail = &item->pn_next;
- ++body->pn_count;
- }
- #endif
- /*
- * If we collected flags that indicate nested heavyweight functions, or
- * this function contains heavyweight-making statements (references to
- * __parent__ or __proto__; use of with, or eval; and assignment to
- * arguments), flag the function as heavyweight (requiring a call object
- * per invocation).
- */
- if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
- fun->flags |= JSFUN_HEAVYWEIGHT;
- tc->flags |= TCF_FUN_HEAVYWEIGHT;
- } else {
- /*
- * If this function is a named statement function not at top-level
- * (i.e. not a top-level function definiton or expression), then
- * our enclosing function, if any, must be heavyweight.
- *
- * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator,
- * so it won't be set here. Assert that it's not. We have to check
- * it later, in js_EmitTree, after js_EmitFunctionScript has traversed
- * the function's body.
- */
- JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS));
- if (lambda == 0 && funAtom && !AT_TOP_LEVEL(tc))
- tc->flags |= TCF_FUN_HEAVYWEIGHT;
- }
- result = pn;
- if (lambda != 0) {
- /*
- * ECMA ed. 3 standard: function expression, possibly anonymous.
- */
- op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
- } else if (!funAtom) {
- /*
- * If this anonymous function definition is *not* embedded within a
- * larger expression, we treat it as an expression statement, not as
- * a function declaration -- and not as a syntax error (as ECMA-262
- * Edition 3 would have it). Backward compatibility must trump all,
- * unless JSOPTION_ANONFUNFIX is set.
- */
- result = NewParseNode(cx, ts, PN_UNARY, tc);
- if (!result)
- return NULL;
- result->pn_type = TOK_SEMI;
- result->pn_pos = pn->pn_pos;
- result->pn_kid = pn;
- op = JSOP_ANONFUNOBJ;
- } else if (!AT_TOP_LEVEL(tc)) {
- /*
- * ECMA ed. 3 extension: a function expression statement not at the
- * top level, e.g., in a compound statement such as the "then" part
- * of an "if" statement, binds a closure only if control reaches that
- * sub-statement.
- */
- op = JSOP_DEFFUN;
- } else {
- op = JSOP_NOP;
- }
- pn->pn_funpob = funpob;
- pn->pn_op = op;
- pn->pn_body = body;
- pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS | TCF_COMPILE_N_GO);
- TREE_CONTEXT_FINISH(cx, &funtc);
- return result;
- }
- static JSParseNode *
- FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
- {
- return FunctionDef(cx, ts, tc, 0);
- }
- static JSParseNode *
- FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
- {
- return FunctionDef(cx, ts, tc, JSFUN_LAMBDA);
- }
- /*
- * Parse the statements in a block, creating a TOK_LC node that lists the
- * statements' trees. If called from block-parsing code, the caller must
- * match { before and } after.
- */
- static JSParseNode *
- Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
- {
- JSParseNode *pn, *pn2, *saveBlock;
- JSTokenType tt;
- JS_CHECK_RECURSION(cx, return NULL);
- pn = NewParseNode(cx, ts, PN_LIST, tc);
- if (!pn)
- return NULL;
- saveBlock = tc->blockNode;
- tc->blockNode = pn;
- PN_INIT_LIST(pn);
- for (;;) {
- ts->flags |= TSF_OPERAND;
- tt = js_PeekToken(cx, ts);
- ts->flags &= ~TSF_OPERAND;
- if (tt <= TOK_EOF || tt == TOK_RC) {
- if (tt == TOK_ERROR)
- return NULL;
- break;
- }
- pn2 = Statement(cx, ts, tc);
- if (!pn2) {
- if (ts->flags & TSF_EOF)
- ts->flags |= TSF_UNEXPECTED_EOF;
- return NULL;
- }
- if (pn2->pn_type == TOK_FUNCTION) {
- /*
- * PNX_FUNCDEFS notifies the emitter that the block contains top-
- * level function definitions that should be processed before the
- * rest of nodes.
- *
- * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
- * is relevant only for function definitions not at top-level,
- * which we call function statements.
- */
- if (AT_TOP_LEVEL(tc))
- pn->pn_extra |= PNX_FUNCDEFS;
- else
- tc->flags |= TCF_HAS_FUNCTION_STMT;
- }
- PN_APPEND(pn, pn2);
- }
- /*
- * Handle the case where there was a let declaration under this block. If
- * it replaced tc->blockNode with a new block node then we must refresh pn
- * and then restore tc->blockNode.
- */
- if (tc->blockNode != pn)
- pn = tc->blockNode;
- tc->blockNode = saveBlock;
- pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
- return pn;
- }
- static JSParseNode *
- Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
- {
- JSParseNode *pn;
- MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
- pn = ParenExpr(cx, ts, tc, NULL, NULL);
- if (!pn)
- return NULL;
- MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
- /*
- * Check for (a = b) and warn about possible (a == b) mistype iff b's
- * operator has greater precedence than ==.
- */
- if (pn->pn_type == TOK_ASSIGN &&
- pn->pn_op == JSOP_NOP &&
- pn->pn_right->pn_type > TOK_EQOP)
- {
- if (!js_ReportCompileErrorNumber(cx, ts, NULL,
- JSREPORT_WARNING | JSREPORT_STRICT,
- JSMSG_EQUAL_AS_ASSIGN,
- "")) {
- return NULL;
- }
- }
- return pn;
- }
- static JSBool
- MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
- {
- JSAtom *label;
- JSTokenType tt;
- tt = js_PeekTokenSameLine(cx, ts);
- if (tt == TOK_ERROR)
- return JS_FALSE;
- if (tt == TOK_NAME) {
- (void) js_GetToken(cx, ts);
- label = CURRENT_TOKEN(ts).t_atom;
- } else {
- label = NULL;
- }
- pn->pn_atom = label;
- return JS_TRUE;
- }
- static JSBool
- BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
- {
- JSObje…
Large files files are truncated, but you can click here to view the full file