diff options
| author | Bob Jamison <ishmalius@gmail.com> | 2007-03-05 10:34:59 +0000 |
|---|---|---|
| committer | ishmal <ishmal@users.sourceforge.net> | 2007-03-05 10:34:59 +0000 |
| commit | 33837efd4b94c4ebb80f95b3d9dbb6efd5499a98 (patch) | |
| tree | de482d7687b3bffa97cc78608ba75a8ad7e52b60 /src/dom/js/jsparse.c | |
| parent | Adding optional dialog preview. Implments RFE [ 1435276 ] switch preview on/o... (diff) | |
| download | inkscape-33837efd4b94c4ebb80f95b3d9dbb6efd5499a98.tar.gz inkscape-33837efd4b94c4ebb80f95b3d9dbb6efd5499a98.zip | |
update JS
(bzr r2555)
Diffstat (limited to 'src/dom/js/jsparse.c')
| -rw-r--r-- | src/dom/js/jsparse.c | 1719 |
1 files changed, 1482 insertions, 237 deletions
diff --git a/src/dom/js/jsparse.c b/src/dom/js/jsparse.c index 6b3f7600c..1438f35a2 100644 --- a/src/dom/js/jsparse.c +++ b/src/dom/js/jsparse.c @@ -48,9 +48,7 @@ * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to * generate bytecode. * - * This parser attempts no error recovery. The dense JSTokenType enumeration - * was designed with error recovery built on 64-bit first and follow bitsets - * in mind, however. + * This parser attempts no error recovery. */ #include "jsstddef.h" #include <stdlib.h> @@ -76,11 +74,15 @@ #include "jsscript.h" #include "jsstr.h" +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + /* * JS parsers, from lowest to highest precedence. * - * Each parser takes a context and a token stream, and emits bytecode using - * a code generator. + * 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 * @@ -121,7 +123,8 @@ static JSParser PrimaryExpr; #define MUST_MATCH_TOKEN(tt, errno) \ JS_BEGIN_MACRO \ if (js_GetToken(cx, ts) != tt) { \ - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \ + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \ + errno); \ return NULL; \ } \ JS_END_MACRO @@ -130,7 +133,7 @@ static JSParser PrimaryExpr; JS_BEGIN_MACRO \ int stackDummy; \ if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { \ - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \ + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \ JSMSG_OVER_RECURSED); \ return NULL; \ } \ @@ -142,17 +145,21 @@ static uint32 maxparsenodes = 0; static uint32 recyclednodes = 0; #endif -static void +static JSParseNode * RecycleTree(JSParseNode *pn, JSTreeContext *tc) { + JSParseNode *next; + if (!pn) - return; + return NULL; JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */ + next = pn->pn_next; pn->pn_next = tc->nodeList; tc->nodeList = pn; #ifdef METER_PARSENODES recyclednodes++; #endif + return next; } static JSParseNode * @@ -202,6 +209,13 @@ NewOrRecycledNode(JSContext *cx, JSTreeContext *tc) break; } } +#ifdef METER_PARSENODES + if (pn) { + parsenodes++; + if (parsenodes - recyclednodes > maxparsenodes) + maxparsenodes = parsenodes - recyclednodes; + } +#endif return pn; } @@ -209,23 +223,23 @@ NewOrRecycledNode(JSContext *cx, JSTreeContext *tc) * Allocate a JSParseNode from cx's temporary arena. */ static JSParseNode * -NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity, +NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity, JSTreeContext *tc) { JSParseNode *pn; + JSToken *tp; pn = NewOrRecycledNode(cx, tc); if (!pn) return NULL; - pn->pn_type = tok->type; - pn->pn_pos = tok->pos; + tp = &CURRENT_TOKEN(ts); + pn->pn_type = tp->type; + pn->pn_pos = tp->pos; pn->pn_op = JSOP_NOP; pn->pn_arity = arity; pn->pn_next = NULL; -#ifdef METER_PARSENODES - parsenodes++; - if (parsenodes - recyclednodes > maxparsenodes) - maxparsenodes = parsenodes - recyclednodes; +#if JS_HAS_XML_SUPPORT + pn->pn_ts = ts; #endif return pn; } @@ -252,7 +266,6 @@ NewBinary(JSContext *cx, JSTokenType tt, left->pn_arity = PN_LIST; PN_INIT_LIST_1(left, pn1); PN_APPEND(left, pn2); - left->pn_extra = 0; if (tt == TOK_PLUS) { if (pn1->pn_type == TOK_STRING) left->pn_extra |= PNX_STRCAT; @@ -302,10 +315,8 @@ NewBinary(JSContext *cx, JSTokenType tt, pn->pn_left = left; pn->pn_right = right; pn->pn_next = NULL; -#ifdef METER_PARSENODES - parsenodes++; - if (parsenodes - recyclednodes > maxparsenodes) - maxparsenodes = parsenodes - recyclednodes; +#if JS_HAS_XML_SUPPORT + pn->pn_ts = NULL; #endif return pn; } @@ -332,7 +343,7 @@ CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt) return TOK_NAME; (void) js_GetToken(cx, ts); if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_GETTER_OR_SETTER, (op == JSOP_GETTER) ? js_getter_str @@ -342,7 +353,8 @@ CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt) CURRENT_TOKEN(ts).t_op = op; name = js_AtomToPrintableString(cx, atom); if (!name || - !js_ReportCompileErrorNumber(cx, ts, NULL, + !js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DEPRECATED_USAGE, @@ -377,13 +389,17 @@ js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts) frame.varobj = chain; } frame.down = fp; + if (fp) { + frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO | + JSFRAME_SCRIPT_OBJECT); + } cx->fp = &frame; } /* * 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_AllocGCThing), or + * 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. @@ -393,7 +409,7 @@ js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts) pn = Statements(cx, ts, &tc); if (pn) { if (!js_MatchToken(cx, ts, TOK_EOF)) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); pn = NULL; } else { @@ -438,6 +454,10 @@ js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, frame.varobj = chain; } frame.down = fp; + if (fp) { + frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO | + JSFRAME_SCRIPT_OBJECT); + } cx->fp = &frame; } flags = cx->fp->flags; @@ -453,7 +473,7 @@ js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, if (!pn) { ok = JS_FALSE; } else if (!js_MatchToken(cx, ts, TOK_EOF)) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); ok = JS_FALSE; } else { @@ -548,6 +568,9 @@ HasFinalReturn(JSParseNode *pn) case TOK_RETURN: return ENDS_IN_RETURN; + case TOK_COLON: + return HasFinalReturn(pn->pn_expr); + #if JS_HAS_EXCEPTIONS case TOK_THROW: return ENDS_IN_RETURN; @@ -588,12 +611,14 @@ ReportNoReturnValue(JSContext *cx, JSTokenStream *ts) fun = cx->fp->fun; if (fun->atom) { char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom)); - ok = js_ReportCompileErrorNumber(cx, ts, NULL, + ok = js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_NO_RETURN_VALUE, name); } else { - ok = js_ReportCompileErrorNumber(cx, ts, NULL, + ok = js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_ANON_NO_RETURN_VALUE); @@ -624,7 +649,8 @@ FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, frame.fun = fun; frame.varobj = frame.scopeChain = funobj; frame.down = fp; - frame.flags = (fp->flags & JSFRAME_COMPILE_N_GO); + if (fp) + frame.flags = fp->flags & JSFRAME_COMPILE_N_GO; cx->fp = &frame; } @@ -640,7 +666,7 @@ FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, } cx->fp = fp; - tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); + tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS)); return pn; } @@ -718,8 +744,8 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool lambda) { JSOp op, prevop; - JSParseNode *pn, *body; - JSAtom *funAtom, *argAtom; + JSParseNode *pn, *body, *result; + JSAtom *funAtom, *objAtom, *argAtom; JSStackFrame *fp; JSObject *varobj, *pobj; JSAtomListElement *ale; @@ -733,12 +759,19 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, #if JS_HAS_GETTER_SETTER op = CURRENT_TOKEN(ts).t_op; #endif - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_FUNC, tc); + pn = NewParseNode(cx, ts, PN_FUNC, tc); if (!pn) return NULL; /* Scan the optional function name into funAtom. */ funAtom = js_MatchToken(cx, ts, TOK_NAME) ? CURRENT_TOKEN(ts).t_atom : NULL; +#if !JS_HAS_LEXICAL_CLOSURE + if (!funAtom && !lambda) { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_SYNTAX_ERROR); + return NULL; + } +#endif /* Find the nearest variable-declaring scope and use it as our parent. */ fp = cx->fp; @@ -755,11 +788,12 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) { const char *name = js_AtomToPrintableString(cx, funAtom); if (!name || - !js_ReportCompileErrorNumber(cx, ts, NULL, + !js_ReportCompileErrorNumber(cx, ts, (prevop != JSOP_DEFCONST) - ? JSREPORT_WARNING | + ? JSREPORT_TS | + JSREPORT_WARNING | JSREPORT_STRICT - : JSREPORT_ERROR, + : JSREPORT_TS | JSREPORT_ERROR, JSMSG_REDECLARED_VAR, (prevop == JSOP_DEFFUN || prevop == JSOP_CLOSURE) @@ -786,32 +820,49 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * 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 LookupArgOrVar can optimize a JSOP_NAME into a + * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a * JSOP_GETVAR bytecode). */ if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) { + JSScopeProperty *sprop; + /* - * Define a property on the outer function so that LookupArgOrVar + * Define a property on the outer function so that BindNameToSlot * can properly optimize accesses. - * - * XXX Here and in Variables, we use the function object's scope, - * XXX arguably polluting it, when we could use a compiler-private - * XXX scope structure. Tradition! */ JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass); JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj)); - if (!js_LookupProperty(cx, varobj, (jsid)funAtom, &pobj, &prop)) + if (!js_LookupHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom), + &pobj, &prop)) { return NULL; + } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); - if (!prop || pobj != varobj) { - if (!js_DefineNativeProperty(cx, varobj, (jsid)funAtom, - JSVAL_VOID, - js_GetLocalVariable, - js_SetLocalVariable, - JSPROP_ENUMERATE | JSPROP_SHARED, - SPROP_HAS_SHORTID, fp->fun->nvars, - NULL)) { + sprop = NULL; + if (!prop || + pobj != varobj || + (sprop = (JSScopeProperty *)prop, + sprop->getter != js_GetLocalVariable)) { + uintN sflags; + + /* + * Use SPROP_IS_DUPLICATE if there is a formal argument of the + * same name, so the decompiler can find the parameter name. + */ + sflags = (sprop && sprop->getter == js_GetArgument) + ? SPROP_IS_DUPLICATE | SPROP_HAS_SHORTID + : SPROP_HAS_SHORTID; + if (!js_AddHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom), + js_GetLocalVariable, + js_SetLocalVariable, + SPROP_INVALID_SLOT, + JSPROP_PERMANENT | JSPROP_SHARED, + sflags, fp->fun->nvars)) { + return NULL; + } + if (fp->fun->nvars == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_VARS); return NULL; } fp->fun->nvars++; @@ -829,6 +880,27 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER; #endif + + /* + * Set interpreted early so js_EmitTree can test it to decide whether to + * eliminate useless expressions. + */ + fun->interpreted = JS_TRUE; + + /* + * Atomize fun->object early to protect against a last-ditch GC under + * js_LookupHiddenProperty. + * + * Absent use of the new scoped local GC roots API around compiler calls, + * we need to atomize here to protect against a GC activation. Atoms are + * protected from GC during compilation by the JS_FRIEND_API entry points + * in this file. There doesn't seem to be any gain in switching from the + * atom-keeping method to the bulkier, slower scoped local roots method. + */ + objAtom = js_AtomizeObject(cx, fun->object, 0); + if (!objAtom) + return NULL; + /* 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)) { @@ -836,8 +908,8 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL); argAtom = CURRENT_TOKEN(ts).t_atom; pobj = NULL; - if (!js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj, - &prop)) { + if (!js_LookupHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom), + &pobj, &prop)) { return NULL; } dupflag = 0; @@ -854,7 +926,8 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * mapped by an entry in scope. */ ok = name && - js_ReportCompileErrorNumber(cx, ts, NULL, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DUPLICATE_FORMAL, @@ -867,15 +940,19 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, return NULL; prop = NULL; } - if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom, + if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom), js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, - JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_SHARED, - SPROP_HAS_SHORTID | dupflag, + JSPROP_PERMANENT | JSPROP_SHARED, + dupflag | SPROP_HAS_SHORTID, fun->nargs)) { return NULL; } + if (fun->nargs == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_ARGS); + return NULL; + } fun->nargs++; } while (js_MatchToken(cx, ts, TOK_COMMA)); @@ -918,14 +995,27 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, } #endif + result = pn; #if JS_HAS_LEXICAL_CLOSURE - if (lambda || !funAtom) { + if (lambda) { /* - * ECMA ed. 3 standard: function expression, possibly anonymous (even - * if at top-level, an unnamed function is an expression statement, not - * a function declaration). + * ECMA ed. 3 standard: function expression, possibly anonymous. */ - op = fun->atom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ; + 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 trumps all. + */ + 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 (tc->topStmt) { /* * ECMA ed. 3 extension: a function expression statement not at the @@ -938,23 +1028,13 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, #endif op = JSOP_NOP; - /* - * Absent use of the new scoped local GC roots API around compiler calls, - * we need to atomize here to protect against a GC activation. Atoms are - * protected from GC during compilation by the JS_FRIEND_API entry points - * in this file. There doesn't seem to be any gain in switching from the - * atom-keeping method to the bulkier, slower scoped local roots method. - */ - pn->pn_funAtom = js_AtomizeObject(cx, fun->object, 0); - if (!pn->pn_funAtom) - return NULL; - + pn->pn_funAtom = objAtom; pn->pn_op = op; pn->pn_body = body; - pn->pn_flags = funtc.flags & TCF_FUN_FLAGS; + pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS); pn->pn_tryCount = funtc.tryCount; TREE_CONTEXT_FINISH(&funtc); - return pn; + return result; } static JSParseNode * @@ -984,7 +1064,7 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) CHECK_RECURSION(); - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; PN_INIT_LIST(pn); @@ -993,8 +1073,11 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) { ts->flags &= ~TSF_OPERAND; pn2 = Statement(cx, ts, tc); - if (!pn2) + if (!pn2) { + if (ts->flags & TSF_EOF) + ts->flags |= TSF_UNEXPECTED_EOF; return NULL; + } ts->flags |= TSF_OPERAND; /* If compiling top-level statements, emit as we go to save space. */ @@ -1058,9 +1141,11 @@ Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_op == JSOP_NOP && pn->pn_right->pn_type > TOK_EQOP) { - JSBool rewrite = !JSVERSION_IS_ECMA(cx->version); - if (!js_ReportCompileErrorNumber(cx, ts, NULL, - JSREPORT_WARNING | JSREPORT_STRICT, + JSBool rewrite = !JS_VERSION_IS_ECMA(cx); + if (!js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | + JSREPORT_WARNING | + JSREPORT_STRICT, JSMSG_EQUAL_AS_ASSIGN, rewrite ? "\nAssuming equality test" @@ -1116,11 +1201,11 @@ MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) static JSParseNode * ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - JSParseNode *pn, *pn2, *pn3; + JSParseNode *pn, *pn2; JSTokenType tt; MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME); - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + pn = NewParseNode(cx, ts, PN_NAME, tc); if (!pn) return NULL; pn->pn_op = JSOP_NAME; @@ -1136,7 +1221,7 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) goto bad_import; if (tt == TOK_DOT) { - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; if (js_MatchToken(cx, ts, TOK_STAR)) { @@ -1153,21 +1238,12 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn2->pn_pos.begin = pn->pn_pos.begin; pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; } else { - /* Make a TOK_LB node. */ - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + /* Make a TOK_LB binary node. */ + pn2 = NewBinary(cx, tt, JSOP_GETELEM, pn, Expr(cx, ts, tc), tc); if (!pn2) return NULL; - pn3 = Expr(cx, ts, tc); - if (!pn3) - return NULL; MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); - pn2->pn_pos.begin = pn->pn_pos.begin; - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - - pn2->pn_op = JSOP_GETELEM; - pn2->pn_left = pn; - pn2->pn_right = pn3; } pn = pn2; @@ -1193,7 +1269,8 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return pn; bad_import: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_IMPORT); + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_IMPORT); return NULL; } #endif /* JS_HAS_EXPORT_IMPORT */ @@ -1225,19 +1302,19 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) switch (tt) { #if JS_HAS_EXPORT_IMPORT case TOK_EXPORT: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; PN_INIT_LIST(pn); if (js_MatchToken(cx, ts, TOK_STAR)) { - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn2) return NULL; PN_APPEND(pn, pn2); } else { do { MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME); - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; pn2->pn_op = JSOP_NAME; @@ -1253,7 +1330,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; case TOK_IMPORT: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; PN_INIT_LIST(pn); @@ -1269,11 +1346,15 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #endif /* JS_HAS_EXPORT_IMPORT */ case TOK_FUNCTION: +#if JS_HAS_XML_SUPPORT + if (js_PeekToken(cx, ts) == TOK_DBLCOLON) + goto expression; +#endif return FunctionStmt(cx, ts, tc); case TOK_IF: /* An IF node has three kids: condition, then, and optional else. */ - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + pn = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn) return NULL; pn1 = Condition(cx, ts, tc); @@ -1305,7 +1386,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) JSParseNode *pn5; JSBool seenDefault = JS_FALSE; - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH); @@ -1319,7 +1400,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH); /* pn2 is a list of case nodes. The default case has pn_left == NULL */ - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn2 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn2) return NULL; PN_INIT_LIST(pn2); @@ -1330,7 +1411,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) switch (tt) { case TOK_DEFAULT: if (seenDefault) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_TOO_MANY_DEFAULTS); return NULL; } @@ -1338,7 +1420,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) /* fall through */ case TOK_CASE: - pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + pn3 = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn3) return NULL; if (tt == TOK_DEFAULT) { @@ -1350,7 +1432,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } PN_APPEND(pn2, pn3); if (pn2->pn_count == JS_BIT(16)) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_TOO_MANY_CASES); return NULL; } @@ -1360,19 +1443,22 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return NULL; default: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_SWITCH); return NULL; } MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE); - pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn4 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn4) return NULL; pn4->pn_type = TOK_LC; PN_INIT_LIST(pn4); + ts->flags |= TSF_OPERAND; while ((tt = js_PeekToken(cx, ts)) != TOK_RC && tt != TOK_CASE && tt != TOK_DEFAULT) { + ts->flags &= ~TSF_OPERAND; if (tt == TOK_ERROR) return NULL; pn5 = Statement(cx, ts, tc); @@ -1380,7 +1466,9 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return NULL; pn4->pn_pos.end = pn5->pn_pos.end; PN_APPEND(pn4, pn5); + ts->flags |= TSF_OPERAND; } + ts->flags &= ~TSF_OPERAND; /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */ if (pn4->pn_head) @@ -1399,7 +1487,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #endif /* JS_HAS_SWITCH_STATEMENT */ case TOK_WHILE: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1); @@ -1417,7 +1505,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #if JS_HAS_DO_WHILE_LOOP case TOK_DO: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1); @@ -1432,7 +1520,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) js_PopStatement(tc); pn->pn_pos.end = pn2->pn_pos.end; pn->pn_right = pn2; - if (cx->version != JSVERSION_ECMA_3) { + if ((cx->version & JSVERSION_MASK) != JSVERSION_ECMA_3) { /* * All legacy and extended versions must do automatic semicolon * insertion after do-while. See the testcase and discussion in @@ -1446,16 +1534,31 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_FOR: /* A FOR node is binary, left is loop control and right is the body. */ - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1); +#if JS_HAS_XML_SUPPORT + pn->pn_op = JSOP_NOP; + if (js_MatchToken(cx, ts, TOK_NAME)) { + if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom) + pn->pn_op = JSOP_FOREACH; + else + js_UngetToken(ts); + } +#endif + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); ts->flags |= TSF_OPERAND; tt = js_PeekToken(cx, ts); ts->flags &= ~TSF_OPERAND; if (tt == TOK_SEMI) { +#if JS_HAS_XML_SUPPORT + if (pn->pn_op == JSOP_FOREACH) + goto bad_for_each; +#endif + /* No initializer -- set first kid of left sub-node to null. */ pn1 = NULL; } else { @@ -1495,12 +1598,22 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) stmtInfo.type = STMT_FOR_IN_LOOP; /* Check that the left side of the 'in' is valid. */ + while (pn1->pn_type == TOK_RP) + pn1 = pn1->pn_kid; if ((pn1->pn_type == TOK_VAR) ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST) : (pn1->pn_type != TOK_NAME && pn1->pn_type != TOK_DOT && +#if JS_HAS_LVALUE_RETURN + pn1->pn_type != TOK_LP && +#endif +#if JS_HAS_XML_SUPPORT + (pn1->pn_type != TOK_UNARYOP || + pn1->pn_op != JSOP_XMLNAME) && +#endif pn1->pn_type != TOK_LB)) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE); return NULL; } @@ -1515,6 +1628,14 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn1->pn_extra |= PNX_POPVAR; } else { pn2 = pn1; +#if JS_HAS_LVALUE_RETURN + if (pn2->pn_type == TOK_LP) + pn2->pn_op = JSOP_SETCALL; +#endif +#if JS_HAS_XML_SUPPORT + if (pn2->pn_type == TOK_UNARYOP) + pn2->pn_op = JSOP_BINDXMLNAME; +#endif } /* Beware 'for (arguments in ...)' with or without a 'var'. */ @@ -1529,6 +1650,11 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return NULL; pn->pn_left = pn2; } else { +#if JS_HAS_XML_SUPPORT + if (pn->pn_op == JSOP_FOREACH) + goto bad_for_each; +#endif + /* Parse the loop condition or null into pn2. */ MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); ts->flags |= TSF_OPERAND; @@ -1556,7 +1682,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } /* Build the RESERVED node to use as the left kid of pn. */ - pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + pn4 = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn4) return NULL; pn4->pn_type = TOK_RESERVED; @@ -1580,6 +1706,14 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_pos.end = pn2->pn_pos.end; return pn; +#if JS_HAS_XML_SUPPORT + bad_for_each: + js_ReportCompileErrorNumber(cx, pn, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_BAD_FOR_EACH_LOOP); + return NULL; +#endif + #if JS_HAS_EXCEPTIONS case TOK_TRY: { JSParseNode *catchtail = NULL; @@ -1601,7 +1735,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) * * finally nodes are unary (just the finally expression) */ - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + pn = NewParseNode(cx, ts, PN_TERNARY, tc); pn->pn_op = JSOP_NOP; MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); @@ -1616,7 +1750,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) while (js_PeekToken(cx, ts) == TOK_CATCH) { /* check for another catch after unconditional catch */ if (catchtail != pn && !catchtail->pn_kid1->pn_expr) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_CATCH_AFTER_GENERAL); return NULL; } @@ -1628,7 +1763,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD) */ (void) js_GetToken(cx, ts); /* eat `catch' */ - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn2) return NULL; @@ -1638,7 +1773,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) */ MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER); - pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + pn3 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn3) return NULL; @@ -1686,7 +1821,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_kid3 = NULL; } if (!pn->pn_kid2 && !pn->pn_kid3) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_CATCH_OR_FINALLY); return NULL; } @@ -1695,7 +1830,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } case TOK_THROW: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; @@ -1706,7 +1841,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (tt == TOK_ERROR) return NULL; if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); return NULL; } @@ -1721,19 +1856,19 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */ case TOK_CATCH: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_CATCH_WITHOUT_TRY); return NULL; case TOK_FINALLY: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_FINALLY_WITHOUT_TRY); return NULL; #endif /* JS_HAS_EXCEPTIONS */ case TOK_BREAK: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; if (!MatchLabel(cx, ts, pn)) @@ -1743,7 +1878,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (label) { for (; ; stmt = stmt->down) { if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND); return NULL; } @@ -1753,7 +1889,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } else { for (; ; stmt = stmt->down) { if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_TOUGH_BREAK); return NULL; } @@ -1766,7 +1903,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; case TOK_CONTINUE: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; if (!MatchLabel(cx, ts, pn)) @@ -1776,14 +1913,16 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (label) { for (stmt2 = NULL; ; stmt = stmt->down) { if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND); return NULL; } if (stmt->type == STMT_LABEL) { if (stmt->label == label) { if (!stmt2 || !STMT_IS_LOOP(stmt2)) { - js_ReportCompileErrorNumber(cx, ts, NULL, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_CONTINUE); return NULL; @@ -1797,7 +1936,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } else { for (; ; stmt = stmt->down) { if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_CONTINUE); return NULL; } @@ -1810,14 +1950,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; case TOK_WITH: - if (!js_ReportCompileErrorNumber(cx, ts, NULL, - JSREPORT_WARNING | JSREPORT_STRICT, - JSMSG_DEPRECATED_USAGE, - js_with_statement_str)) { - return NULL; - } - - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH); @@ -1849,11 +1982,11 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_RETURN: if (!(tc->flags & TCF_IN_FUNCTION)) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_RETURN); return NULL; } - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; @@ -1899,7 +2032,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_EOL: case TOK_SEMI: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn->pn_type = TOK_SEMI; @@ -1908,7 +2041,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #if JS_HAS_DEBUGGER_KEYWORD case TOK_DEBUGGER: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_type = TOK_DEBUGGER; @@ -1916,10 +2049,38 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; #endif /* JS_HAS_DEBUGGER_KEYWORD */ +#if JS_HAS_XML_SUPPORT + case TOK_DEFAULT: + pn = NewParseNode(cx, ts, PN_UNARY, tc); + if (!pn) + return NULL; + if (!js_MatchToken(cx, ts, TOK_NAME) || + CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom || + !js_MatchToken(cx, ts, TOK_NAME) || + CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom || + !js_MatchToken(cx, ts, TOK_ASSIGN) || + CURRENT_TOKEN(ts).t_op != JSOP_NOP) { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_DEFAULT_XML_NAMESPACE); + return NULL; + } + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_op = JSOP_DEFXMLNS; + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_kid = pn2; + tc->flags |= TCF_HAS_DEFXMLNS; + break; +#endif + case TOK_ERROR: return NULL; default: +#if JS_HAS_XML_SUPPORT + expression: +#endif js_UngetToken(ts); pn2 = Expr(cx, ts, tc); if (!pn2) @@ -1927,14 +2088,16 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (js_PeekToken(cx, ts) == TOK_COLON) { if (pn2->pn_type != TOK_NAME) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_LABEL); return NULL; } label = pn2->pn_atom; for (stmt = tc->topStmt; stmt; stmt = stmt->down) { if (stmt->type == STMT_LABEL && stmt->label == label) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_DUPLICATE_LABEL); return NULL; } @@ -1956,7 +2119,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return pn2; } - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn->pn_type = TOK_SEMI; @@ -1971,7 +2134,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (tt == TOK_ERROR) return NULL; if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SEMI_BEFORE_STMNT); return NULL; } @@ -2006,11 +2169,10 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) * this by looking up the variable id in the current variable scope. */ JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_VAR); - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; pn->pn_op = CURRENT_TOKEN(ts).t_op; - pn->pn_extra = 0; /* assume no JSOP_POP needed */ PN_INIT_LIST(pn); /* @@ -2053,17 +2215,18 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) ATOM_LIST_SEARCH(ale, &tc->decls, atom); if (ale) { prevop = ALE_JSOP(ale); - if (JS_HAS_STRICT_OPTION(cx) || - pn->pn_op == JSOP_DEFCONST || - prevop == JSOP_DEFCONST) { + if (JS_HAS_STRICT_OPTION(cx) + ? pn->pn_op != JSOP_DEFVAR || prevop != JSOP_DEFVAR + : pn->pn_op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) { const char *name = js_AtomToPrintableString(cx, atom); if (!name || - !js_ReportCompileErrorNumber(cx, ts, NULL, + !js_ReportCompileErrorNumber(cx, ts, (pn->pn_op != JSOP_DEFCONST && prevop != JSOP_DEFCONST) - ? JSREPORT_WARNING | + ? JSREPORT_TS | + JSREPORT_WARNING | JSREPORT_STRICT - : JSREPORT_ERROR, + : JSREPORT_TS | JSREPORT_ERROR, JSMSG_REDECLARED_VAR, (prevop == JSOP_DEFFUN || prevop == JSOP_CLOSURE) @@ -2084,7 +2247,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } ALE_SET_JSOP(ale, pn->pn_op); - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; pn2->pn_op = JSOP_NAME; @@ -2092,15 +2255,20 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn2->pn_expr = NULL; pn2->pn_slot = -1; pn2->pn_attrs = (pn->pn_op == JSOP_DEFCONST) - ? JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_READONLY - : JSPROP_ENUMERATE | JSPROP_PERMANENT; + ? JSPROP_PERMANENT | JSPROP_READONLY + : JSPROP_PERMANENT; PN_APPEND(pn, pn2); if (!fun) { - prop = NULL; /* don't lookup global variables at compile time */ + /* Don't lookup global variables at compile time. */ + prop = NULL; + } else if (OBJ_IS_NATIVE(obj)) { + if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), + &pobj, &prop)) { + return NULL; + } } else { - if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop)) + if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return NULL; } if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) { @@ -2110,14 +2278,16 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (!name) { ok = JS_FALSE; } else if (pn->pn_op == JSOP_DEFCONST) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_REDECLARED_PARAM, name); ok = JS_FALSE; } else { currentGetter = js_GetArgument; currentSetter = js_SetArgument; - ok = js_ReportCompileErrorNumber(cx, ts, NULL, + ok = js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_VAR_HIDES_ARG, @@ -2129,7 +2299,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (clasp == &js_FunctionClass) { JS_ASSERT(sprop->getter == js_GetLocalVariable); JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && - sprop->shortid < fun->nvars); + (uint16) sprop->shortid < fun->nvars); } else if (clasp == &js_CallClass) { if (sprop->getter == js_GetCallVariable) { /* @@ -2138,7 +2308,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) * that the slot number we have is in range. */ JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && - sprop->shortid < fun->nvars); + (uint16) sprop->shortid < fun->nvars); } else { /* * A variable introduced through another eval: @@ -2182,12 +2352,17 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) atom != cx->runtime->atomState.argumentsAtom && fp->scopeChain == obj && !js_InWithStatement(tc)) { - if (!js_AddNativeProperty(cx, obj, (jsid)atom, + if (!js_AddHiddenProperty(cx, obj, ATOM_TO_JSID(atom), currentGetter, currentSetter, SPROP_INVALID_SLOT, pn2->pn_attrs | JSPROP_SHARED, SPROP_HAS_SHORTID, fun->nvars)) { - ok = JS_FALSE; + return NULL; + } + if (fun->nvars == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_VARS); + return NULL; } fun->nvars++; } @@ -2195,7 +2370,8 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (js_MatchToken(cx, ts, TOK_ASSIGN)) { if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_VAR_INIT); ok = JS_FALSE; } else { @@ -2229,7 +2405,7 @@ Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn = AssignExpr(cx, ts, tc); if (pn && js_MatchToken(cx, ts, TOK_COMMA)) { - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn2 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn2) return NULL; pn2->pn_pos.begin = pn->pn_pos.begin; @@ -2282,7 +2458,9 @@ AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) tc->flags |= TCF_FUN_HEAVYWEIGHT; break; case TOK_DOT: - pn2->pn_op = JSOP_SETPROP; + pn2->pn_op = (pn2->pn_op == JSOP_GETMETHOD) + ? JSOP_SETMETHOD + : JSOP_SETPROP; break; case TOK_LB: pn2->pn_op = JSOP_SETELEM; @@ -2292,8 +2470,16 @@ AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn2->pn_op = JSOP_SETCALL; break; #endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + if (pn2->pn_op == JSOP_XMLNAME) { + pn2->pn_op = JSOP_SETXMLNAME; + break; + } + /* FALL THROUGH */ +#endif default: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS); return NULL; } @@ -2312,7 +2498,7 @@ CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn = OrExpr(cx, ts, tc); if (pn && js_MatchToken(cx, ts, TOK_HOOK)) { pn1 = pn; - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + pn = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn) return NULL; #if JS_HAS_IN_OPERATOR @@ -2520,8 +2706,11 @@ SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid, #if JS_HAS_LVALUE_RETURN (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) && #endif +#if JS_HAS_XML_SUPPORT + (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) && +#endif kid->pn_type != TOK_LB) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_OPERAND, name); return NULL; } @@ -2558,7 +2747,15 @@ SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, #if JS_HAS_LVALUE_RETURN case TOK_LP: + JS_ASSERT(kid->pn_op == JSOP_CALL); kid->pn_op = JSOP_SETCALL; + /* FALL THROUGH */ +#endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + if (kid->pn_op == JSOP_XMLNAME) + kid->pn_op = JSOP_SETXMLNAME; + /* FALL THROUGH */ #endif case TOK_LB: op = (tt == TOK_INC) @@ -2580,6 +2777,8 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) JSTokenType tt; JSParseNode *pn, *pn2; + CHECK_RECURSION(); + ts->flags |= TSF_OPERAND; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_OPERAND; @@ -2588,7 +2787,7 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_UNARYOP: case TOK_PLUS: case TOK_MINUS: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */ @@ -2602,7 +2801,7 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_INC: case TOK_DEC: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn2 = MemberExpr(cx, ts, tc, JS_TRUE); @@ -2614,7 +2813,7 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; case TOK_DELETE: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn2 = UnaryExpr(cx, ts, tc); @@ -2645,7 +2844,7 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) tt = js_PeekTokenSameLine(cx, ts); if (tt == TOK_INC || tt == TOK_DEC) { (void) js_GetToken(cx, ts); - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn2 = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn2) return NULL; if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE)) @@ -2677,7 +2876,7 @@ ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, } while (js_MatchToken(cx, ts, TOK_COMMA)); if (js_GetToken(cx, ts) != TOK_RP) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_PAREN_AFTER_ARGS); return JS_FALSE; } @@ -2701,7 +2900,7 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, if (tt == TOK_NEW) { (void) js_GetToken(cx, ts); - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; pn2 = MemberExpr(cx, ts, tc, JS_FALSE); @@ -2723,21 +2922,102 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, pn = PrimaryExpr(cx, ts, tc); if (!pn) return NULL; + + if (pn->pn_type == TOK_ANYNAME || + pn->pn_type == TOK_AT || + pn->pn_type == TOK_DBLCOLON) { + pn2 = NewOrRecycledNode(cx, tc); + if (!pn2) + return NULL; + pn2->pn_type = TOK_UNARYOP; + pn2->pn_pos = pn->pn_pos; + pn2->pn_op = JSOP_XMLNAME; + pn2->pn_arity = PN_UNARY; + pn2->pn_kid = pn; + pn2->pn_next = NULL; +#if JS_HAS_XML_SUPPORT + pn2->pn_ts = ts; +#endif + pn = pn2; + } } while ((tt = js_GetToken(cx, ts)) > TOK_EOF) { if (tt == TOK_DOT) { - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; +#if JS_HAS_XML_SUPPORT + pn3 = PrimaryExpr(cx, ts, tc); + if (!pn3) + return NULL; + tt = pn3->pn_type; + if (tt == TOK_NAME || + (tt == TOK_DBLCOLON && + pn3->pn_arity == PN_NAME && + pn3->pn_expr->pn_type == TOK_FUNCTION)) { + pn2->pn_op = (tt == TOK_NAME) ? JSOP_GETPROP : JSOP_GETMETHOD; + pn2->pn_expr = pn; + pn2->pn_atom = pn3->pn_atom; + RecycleTree(pn3, tc); + } else { + if (TOKEN_TYPE_IS_XML(tt)) { + pn2->pn_type = TOK_LB; + pn2->pn_op = JSOP_GETELEM; + } else if (tt == TOK_RP) { + JSParseNode *group = pn3; + + /* Recycle the useless TOK_RP/JSOP_GROUP node. */ + pn3 = group->pn_kid; + group->pn_kid = NULL; + RecycleTree(group, tc); + pn2->pn_type = TOK_FILTER; + pn2->pn_op = JSOP_FILTER; + } else { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_NAME_AFTER_DOT); + return NULL; + } + pn2->pn_arity = PN_BINARY; + pn2->pn_left = pn; + pn2->pn_right = pn3; + } +#else MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT); - pn2->pn_pos.begin = pn->pn_pos.begin; - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; pn2->pn_op = JSOP_GETPROP; pn2->pn_expr = pn; pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; +#endif + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; +#if JS_HAS_XML_SUPPORT + } else if (tt == TOK_DBLDOT) { + pn2 = NewParseNode(cx, ts, PN_BINARY, tc); + if (!pn2) + return NULL; + pn3 = PrimaryExpr(cx, ts, tc); + if (!pn3) + return NULL; + tt = pn3->pn_type; + if (tt == TOK_NAME) { + pn3->pn_type = TOK_STRING; + pn3->pn_arity = PN_NULLARY; + pn3->pn_op = JSOP_STRING; + } else if (!TOKEN_TYPE_IS_XML(tt)) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_NAME_AFTER_DOT); + return NULL; + } + pn2->pn_op = JSOP_DESCENDANTS; + pn2->pn_left = pn; + pn2->pn_right = pn3; + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; +#endif } else if (tt == TOK_LB) { - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + pn2 = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn2) return NULL; pn3 = Expr(cx, ts, tc); @@ -2761,7 +3041,7 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, pn2->pn_right = pn3; } } else if (allowCallSyntax && tt == TOK_LP) { - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn2 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn2) return NULL; @@ -2797,11 +3077,704 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, } static JSParseNode * +BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + uintN oldflags; + JSParseNode *pn; + +#if JS_HAS_IN_OPERATOR + /* + * Always accept the 'in' operator in a parenthesized expression, + * where it's unambiguous, even if we might be parsing the init of a + * for statement. + */ + oldflags = tc->flags; + tc->flags &= ~TCF_IN_FOR_INIT; +#endif + pn = Expr(cx, ts, tc); +#if JS_HAS_IN_OPERATOR + tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); +#endif + return pn; +} + +#if JS_HAS_XML_SUPPORT + +static JSParseNode * +EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = BracketedExpr(cx, ts, tc); + if (!pn) + return NULL; + + MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR); + return pn; +} + +/* + * From the ECMA-357 grammar in 11.1.1 and 11.1.2: + * + * AttributeIdentifier: + * @ PropertySelector + * @ QualifiedIdentifier + * @ [ Expression ] + * + * PropertySelector: + * Identifier + * * + * + * QualifiedIdentifier: + * PropertySelector :: PropertySelector + * PropertySelector :: [ Expression ] + * + * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so: + * + * AttributeIdentifier: + * @ QualifiedIdentifier + * @ [ Expression ] + * + * PropertySelector: + * Identifier + * * + * + * QualifiedIdentifier: + * PropertySelector :: PropertySelector + * PropertySelector :: [ Expression ] + * PropertySelector + * + * Since PrimaryExpression: Identifier in ECMA-262 and we want the semantics + * for that rule to result in a name node, but extend the grammar to include + * PrimaryExpression: QualifiedIdentifier, we factor further: + * + * QualifiedIdentifier: + * PropertySelector QualifiedSuffix + * + * QualifiedSuffix: + * :: PropertySelector + * :: [ Expression ] + * /nothing/ + * + * And use this production instead of PrimaryExpression: QualifiedIdentifier: + * + * PrimaryExpression: + * Identifier QualifiedSuffix + * + * We hoists the :: match into callers of QualifiedSuffix, in order to tweak + * PropertySelector vs. Identifier pn_arity, pn_op, and other members. + */ +static JSParseNode * +PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = NewParseNode(cx, ts, PN_NULLARY, tc); + if (!pn) + return NULL; + if (pn->pn_type == TOK_STAR) { + pn->pn_type = TOK_ANYNAME; + pn->pn_op = JSOP_ANYNAME; + pn->pn_atom = cx->runtime->atomState.starAtom; + } else { + JS_ASSERT(pn->pn_type == TOK_NAME); + pn->pn_op = JSOP_QNAMEPART; + pn->pn_arity = PN_NAME; + pn->pn_atom = CURRENT_TOKEN(ts).t_atom; + pn->pn_expr = NULL; + pn->pn_slot = -1; + pn->pn_attrs = 0; + } + return pn; +} + +static JSParseNode * +QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, + JSTreeContext *tc) +{ + JSParseNode *pn2, *pn3; + JSTokenType tt; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON); + pn2 = NewParseNode(cx, ts, PN_NAME, tc); + if (!pn2) + return NULL; + + /* Left operand of :: must be evaluated if it is an identifier. */ + if (pn->pn_op == JSOP_QNAMEPART) + pn->pn_op = JSOP_NAME; + + tt = js_GetToken(cx, ts); + if (tt == TOK_STAR || tt == TOK_NAME) { + /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */ + pn2->pn_op = JSOP_QNAMECONST; + pn2->pn_atom = (tt == TOK_STAR) + ? cx->runtime->atomState.starAtom + : CURRENT_TOKEN(ts).t_atom; + pn2->pn_expr = pn; + pn2->pn_slot = -1; + pn2->pn_attrs = 0; + return pn2; + } + + if (tt != TOK_LB) { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_SYNTAX_ERROR); + return NULL; + } + pn3 = EndBracketedExpr(cx, ts, tc); + if (!pn3) + return NULL; + + pn2->pn_op = JSOP_QNAME; + pn2->pn_arity = PN_BINARY; + pn2->pn_left = pn; + pn2->pn_right = pn3; + return pn2; +} + +static JSParseNode * +QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = PropertySelector(cx, ts, tc); + if (!pn) + return NULL; + if (js_MatchToken(cx, ts, TOK_DBLCOLON)) + pn = QualifiedSuffix(cx, ts, pn, tc); + return pn; +} + +static JSParseNode * +AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2; + JSTokenType tt; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT); + pn = NewParseNode(cx, ts, PN_UNARY, tc); + if (!pn) + return NULL; + pn->pn_op = JSOP_TOATTRNAME; + tt = js_GetToken(cx, ts); + if (tt == TOK_STAR || tt == TOK_NAME) { + pn2 = QualifiedIdentifier(cx, ts, tc); + } else if (tt == TOK_LB) { + pn2 = EndBracketedExpr(cx, ts, tc); + } else { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_SYNTAX_ERROR); + return NULL; + } + if (!pn2) + return NULL; + pn->pn_kid = pn2; + return pn; +} + +/* + * Make a TOK_LC unary node whose pn_kid is an expression. + */ +static JSParseNode * +XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2; + uintN oldflags; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC); + pn = NewParseNode(cx, ts, PN_UNARY, tc); + if (!pn) + return NULL; + + /* + * Turn off XML tag mode, but don't restore it after parsing this braced + * expression. Instead, simply restore ts's old flags. This is required + * because XMLExpr is called both from within a tag, and from within text + * contained in an element, but outside of any start, end, or point tag. + */ + oldflags = ts->flags; + ts->flags = oldflags & ~TSF_XMLTAGMODE; + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR); + ts->flags = oldflags; + pn->pn_kid = pn2; + pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR; + return pn; +} + +/* + * Make a terminal node for oneof TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE, + * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting + * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole + * child of a container tag. + */ +static JSParseNode * +XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + JSToken *tp; + + pn = NewParseNode(cx, ts, PN_NULLARY, tc); + if (!pn) + return NULL; + tp = &CURRENT_TOKEN(ts); + pn->pn_op = tp->t_op; + pn->pn_atom = tp->t_atom; + if (tp->type == TOK_XMLPI) + pn->pn_atom2 = tp->t_atom2; + return pn; +} + +/* + * Parse the productions: + * + * XMLNameExpr: + * XMLName XMLNameExpr? + * { Expr } XMLNameExpr? + * + * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces + * a list of names and/or expressions, a single expression, or a single name. + * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type + * will be TOK_LC. + */ +static JSParseNode * +XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2, *list; + JSTokenType tt; + + pn = list = NULL; + do { + tt = CURRENT_TOKEN(ts).type; + if (tt == TOK_LC) { + pn2 = XMLExpr(cx, ts, JS_TRUE, tc); + if (!pn2) + return NULL; + } else { + JS_ASSERT(tt == TOK_XMLNAME); + pn2 = XMLAtomNode(cx, ts, tc); + if (!pn2) + return NULL; + } + + if (!pn) { + pn = pn2; + } else { + if (!list) { + list = NewParseNode(cx, ts, PN_LIST, tc); + if (!list) + return NULL; + list->pn_type = TOK_XMLNAME; + list->pn_pos.begin = pn->pn_pos.begin; + PN_INIT_LIST_1(list, pn); + list->pn_extra = PNX_CANTFOLD; + pn = list; + } + pn->pn_pos.end = pn2->pn_pos.end; + PN_APPEND(pn, pn2); + } + } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC); + + js_UngetToken(ts); + return pn; +} + +/* + * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded + * at compile time into a JSXML tree. + */ +#define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \ + ? ((pn)->pn_extra & PNX_CANTFOLD) == 0 \ + : (pn)->pn_type != TOK_LC) + +/* + * Parse the productions: + * + * XMLTagContent: + * XMLNameExpr + * XMLTagContent S XMLNameExpr S? = S? XMLAttr + * XMLTagContent S XMLNameExpr S? = S? { Expr } + * + * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent + * produces a list of name and attribute values and/or braced expressions, a + * single expression, or a single name. + * + * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where + * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is + * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and + * we parsed exactly one expression. + */ +static JSParseNode * +XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSTokenType tagtype, JSAtom **namep) +{ + JSParseNode *pn, *pn2, *list; + JSTokenType tt; + + pn = XMLNameExpr(cx, ts, tc); + if (!pn) + return NULL; + *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL; + list = NULL; + + while (js_MatchToken(cx, ts, TOK_XMLSPACE)) { + tt = js_GetToken(cx, ts); + if (tt != TOK_XMLNAME && tt != TOK_LC) { + js_UngetToken(ts); + break; + } + + pn2 = XMLNameExpr(cx, ts, tc); + if (!pn2) + return NULL; + if (!list) { + list = NewParseNode(cx, ts, PN_LIST, tc); + if (!list) + return NULL; + list->pn_type = tagtype; + list->pn_pos.begin = pn->pn_pos.begin; + PN_INIT_LIST_1(list, pn); + pn = list; + } + PN_APPEND(pn, pn2); + if (!XML_FOLDABLE(pn2)) + pn->pn_extra |= PNX_CANTFOLD; + + js_MatchToken(cx, ts, TOK_XMLSPACE); + MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR); + js_MatchToken(cx, ts, TOK_XMLSPACE); + + tt = js_GetToken(cx, ts); + if (tt == TOK_XMLATTR) { + pn2 = XMLAtomNode(cx, ts, tc); + } else if (tt == TOK_LC) { + pn2 = XMLExpr(cx, ts, JS_TRUE, tc); + pn->pn_extra |= PNX_CANTFOLD; + } else { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_ATTR_VALUE); + return NULL; + } + if (!pn2) + return NULL; + pn->pn_pos.end = pn2->pn_pos.end; + PN_APPEND(pn, pn2); + } + + return pn; +} + +#define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \ + JS_BEGIN_MACRO \ + if ((tt) <= TOK_EOF) { \ + if ((tt) == TOK_EOF) { \ + js_ReportCompileErrorNumber(cx, ts, \ + JSREPORT_TS | JSREPORT_ERROR, \ + JSMSG_END_OF_XML_SOURCE); \ + } \ + return result; \ + } \ + JS_END_MACRO + +static JSParseNode * +XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowList); + +/* + * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence + * that opens the end tag for the container. + */ +static JSBool +XMLElementContent(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, + JSTreeContext *tc) +{ + JSTokenType tt; + JSParseNode *pn2; + JSAtom *textAtom; + + ts->flags &= ~TSF_XMLTAGMODE; + for (;;) { + ts->flags |= TSF_XMLTEXTMODE; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_XMLTEXTMODE; + XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE); + + JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT); + textAtom = CURRENT_TOKEN(ts).t_atom; + if (textAtom) { + /* Non-zero-length XML text scanned. */ + pn2 = XMLAtomNode(cx, ts, tc); + if (!pn2) + return JS_FALSE; + pn->pn_pos.end = pn2->pn_pos.end; + PN_APPEND(pn, pn2); + } + + ts->flags |= TSF_OPERAND; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE); + if (tt == TOK_XMLETAGO) + break; + + if (tt == TOK_LC) { + pn2 = XMLExpr(cx, ts, JS_FALSE, tc); + pn->pn_extra |= PNX_CANTFOLD; + } else if (tt == TOK_XMLSTAGO) { + pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE); + if (pn2) { + pn2->pn_extra &= ~PNX_XMLROOT; + pn->pn_extra |= pn2->pn_extra; + } + } else { + JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT || + tt == TOK_XMLPI); + pn2 = XMLAtomNode(cx, ts, tc); + } + if (!pn2) + return JS_FALSE; + pn->pn_pos.end = pn2->pn_pos.end; + PN_APPEND(pn, pn2); + } + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO); + ts->flags |= TSF_XMLTAGMODE; + return JS_TRUE; +} + +/* + * Return a PN_LIST node containing an XML or XMLList Initialiser. + */ +static JSParseNode * +XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowList) +{ + JSParseNode *pn, *pn2, *list; + JSBool hadSpace; + JSTokenType tt; + JSAtom *startAtom, *endAtom; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO); + pn = NewParseNode(cx, ts, PN_LIST, tc); + if (!pn) + return NULL; + + ts->flags |= TSF_XMLTAGMODE; + hadSpace = js_MatchToken(cx, ts, TOK_XMLSPACE); + tt = js_GetToken(cx, ts); + if (tt == TOK_ERROR) + return NULL; + + if (tt == TOK_XMLNAME || tt == TOK_LC) { + /* + * XMLElement. Append the tag and its contents, if any, to pn. + */ + pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom); + if (!pn2) + return NULL; + js_MatchToken(cx, ts, TOK_XMLSPACE); + + tt = js_GetToken(cx, ts); + if (tt == TOK_XMLPTAGC) { + /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */ + if (pn2->pn_type == TOK_XMLSTAGO) { + PN_INIT_LIST(pn); + RecycleTree(pn, tc); + pn = pn2; + } else { + JS_ASSERT(pn2->pn_type == TOK_XMLNAME || + pn2->pn_type == TOK_LC); + PN_INIT_LIST_1(pn, pn2); + if (!XML_FOLDABLE(pn2)) + pn->pn_extra |= PNX_CANTFOLD; + } + pn->pn_type = TOK_XMLPTAGC; + pn->pn_extra |= PNX_XMLROOT; + } else { + /* We had better have a tag-close (>) at this point. */ + if (tt != TOK_XMLTAGC) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_TAG_SYNTAX); + return NULL; + } + pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + + /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */ + if (pn2->pn_type != TOK_XMLSTAGO) { + PN_INIT_LIST_1(pn, pn2); + if (!XML_FOLDABLE(pn2)) + pn->pn_extra |= PNX_CANTFOLD; + pn2 = pn; + pn = NewParseNode(cx, ts, PN_LIST, tc); + if (!pn) + return NULL; + } + + /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */ + pn->pn_type = TOK_XMLELEM; + PN_INIT_LIST_1(pn, pn2); + if (!XML_FOLDABLE(pn2)) + pn->pn_extra |= PNX_CANTFOLD; + pn->pn_extra |= PNX_XMLROOT; + + /* Get element contents and delimiting end-tag-open sequence. */ + if (!XMLElementContent(cx, ts, pn, tc)) + return NULL; + + js_MatchToken(cx, ts, TOK_XMLSPACE); + tt = js_GetToken(cx, ts); + XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL); + if (tt != TOK_XMLNAME && tt != TOK_LC) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_TAG_SYNTAX); + return NULL; + } + + /* Parse end tag; check mismatch at compile-time if we can. */ + pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom); + if (!pn2) + return NULL; + if (pn2->pn_type == TOK_XMLETAGO) { + /* Oops, end tag has attributes! */ + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_TAG_SYNTAX); + return NULL; + } + if (endAtom && startAtom && endAtom != startAtom) { + /* End vs. start tag name mismatch: point to the tag name. */ + ++pn2->pn_pos.begin.index; + js_ReportCompileErrorNumber(cx, pn2, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_XML_TAG_NAME_MISMATCH); + return NULL; + } + + /* Make a TOK_XMLETAGO list with pn2 as its single child. */ + JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC); + list = NewParseNode(cx, ts, PN_LIST, tc); + if (!list) + return NULL; + list->pn_type = TOK_XMLETAGO; + PN_INIT_LIST_1(list, pn2); + PN_APPEND(pn, list); + if (!XML_FOLDABLE(pn2)) { + list->pn_extra |= PNX_CANTFOLD; + pn->pn_extra |= PNX_CANTFOLD; + } + + js_MatchToken(cx, ts, TOK_XMLSPACE); + MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX); + } + + /* Set pn_op now that pn has been updated to its final value. */ + pn->pn_op = JSOP_TOXML; + } else if (!hadSpace && allowList && tt == TOK_XMLTAGC) { + /* XMLList Initialiser. */ + pn->pn_type = TOK_XMLLIST; + pn->pn_op = JSOP_TOXMLLIST; + PN_INIT_LIST(pn); + pn->pn_extra |= PNX_XMLROOT; + if (!XMLElementContent(cx, ts, pn, tc)) + return NULL; + + MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX); + } else { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_NAME_SYNTAX); + return NULL; + } + + pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + ts->flags &= ~TSF_XMLTAGMODE; + return pn; +} + +static JSParseNode * +XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowList) +{ + uint32 oldopts; + JSParseNode *pn; + + /* + * Force XML support to be enabled so that comments and CDATA literals + * are recognized, instead of <! followed by -- starting an HTML comment + * to end of line (used in script tags to hide content from old browsers + * that don't recognize <script>). + */ + oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML); + pn = XMLElementOrList(cx, ts, tc, allowList); + JS_SetOptions(cx, oldopts); + return pn; +} + +JS_FRIEND_API(JSParseNode *) +js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, + JSBool allowList) +{ + JSStackFrame *fp, frame; + JSParseNode *pn; + JSTreeContext tc; + JSTokenType tt; + + /* + * Push a compiler frame if we have no frames, or if the top frame is a + * lightweight function activation, or if its scope chain doesn't match + * the one passed to us. + */ + fp = cx->fp; + if (!fp || !fp->varobj || fp->scopeChain != chain) { + memset(&frame, 0, sizeof frame); + frame.varobj = frame.scopeChain = chain; + if (cx->options & JSOPTION_VAROBJFIX) { + while ((chain = JS_GetParent(cx, chain)) != NULL) + frame.varobj = chain; + } + frame.down = fp; + if (fp) { + frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO | + JSFRAME_SCRIPT_OBJECT); + } + cx->fp = &frame; + } + + JS_KEEP_ATOMS(cx->runtime); + TREE_CONTEXT_INIT(&tc); + + /* Set XML-only mode to turn off special treatment of {expr} in XML. */ + ts->flags |= TSF_OPERAND | TSF_XMLONLYMODE; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + + if (tt != TOK_XMLSTAGO) { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_MARKUP); + pn = NULL; + } else { + pn = XMLElementOrListRoot(cx, ts, &tc, allowList); + } + + ts->flags &= ~TSF_XMLONLYMODE; + TREE_CONTEXT_FINISH(&tc); + JS_UNKEEP_ATOMS(cx->runtime); + cx->fp = fp; + return pn; +} + +#endif /* JS_HAS_XMLSUPPORT */ + +static JSParseNode * PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSTokenType tt; JSParseNode *pn, *pn2, *pn3; - char *badWord; #if JS_HAS_GETTER_SETTER JSAtom *atom; JSRuntime *rt; @@ -2836,8 +3809,20 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #endif switch (tt) { -#if JS_HAS_LEXICAL_CLOSURE +#if JS_HAS_LEXICAL_CLOSURE || JS_HAS_XML_SUPPORT case TOK_FUNCTION: +#if JS_HAS_XML_SUPPORT + if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { + pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); + if (!pn2) + return NULL; + pn2->pn_type = TOK_FUNCTION; + pn = QualifiedSuffix(cx, ts, pn2, tc); + if (!pn) + return NULL; + break; + } +#endif pn = FunctionExpr(cx, ts, tc); if (!pn) return NULL; @@ -2850,11 +3835,10 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) JSBool matched; jsuint atomIndex; - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; pn->pn_type = TOK_RB; - pn->pn_extra = 0; #if JS_HAS_SHARP_VARS if (defsharp) { @@ -2868,7 +3852,14 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) matched = js_MatchToken(cx, ts, TOK_RB); ts->flags &= ~TSF_OPERAND; if (!matched) { - for (atomIndex = 0; atomIndex < ATOM_INDEX_LIMIT; atomIndex++) { + for (atomIndex = 0; ; atomIndex++) { + if (atomIndex == ATOM_INDEX_LIMIT) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_ARRAY_INIT_TOO_BIG); + return NULL; + } + ts->flags |= TSF_OPERAND; tt = js_PeekToken(cx, ts); ts->flags &= ~TSF_OPERAND; @@ -2880,7 +3871,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (tt == TOK_COMMA) { /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */ js_MatchToken(cx, ts, TOK_COMMA); - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); } else { pn2 = AssignExpr(cx, ts, tc); } @@ -2902,7 +3893,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } case TOK_LC: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; pn->pn_type = TOK_RC; @@ -2922,7 +3913,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) tt = js_GetToken(cx, ts); switch (tt) { case TOK_NUMBER: - pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); if (pn3) pn3->pn_dval = CURRENT_TOKEN(ts).t_dval; break; @@ -2936,8 +3927,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) ? JSOP_GETTER : JSOP_SETTER; if (js_MatchToken(cx, ts, TOK_NAME)) { - pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, - tc); + pn3 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn3) return NULL; pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; @@ -2954,12 +3944,13 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) /* else fall thru ... */ #endif case TOK_STRING: - pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); if (pn3) pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; break; case TOK_RC: - if (!js_ReportCompileErrorNumber(cx, ts, NULL, + if (!js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_TRAILING_COMMA)) { @@ -2967,7 +3958,8 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } goto end_obj_init; default: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_PROP_ID); return NULL; } @@ -2981,7 +3973,8 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } #endif if (tt != TOK_COLON) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_COLON_AFTER_ID); return NULL; } @@ -3006,7 +3999,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_DEFSHARP: if (defsharp) goto badsharp; - defsharp = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + defsharp = NewParseNode(cx, ts, PN_UNARY, tc); if (!defsharp) return NULL; defsharp->pn_kid = NULL; @@ -3015,7 +4008,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_USESHARP: /* Check for forward/dangling references at runtime, to allow eval. */ - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval; @@ -3025,26 +4018,10 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #endif /* JS_HAS_INITIALIZERS */ case TOK_LP: - { -#if JS_HAS_IN_OPERATOR - uintN oldflags; -#endif - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; -#if JS_HAS_IN_OPERATOR - /* - * Always accept the 'in' operator in a parenthesized expression, - * where it's unambiguous, even if we might be parsing the init of a - * for statement. - */ - oldflags = tc->flags; - tc->flags &= ~TCF_IN_FOR_INIT; -#endif - pn2 = Expr(cx, ts, tc); -#if JS_HAS_IN_OPERATOR - tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); -#endif + pn2 = BracketedExpr(cx, ts, tc); if (!pn2) return NULL; @@ -3053,26 +4030,68 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; pn->pn_kid = pn2; break; - } + +#if JS_HAS_XML_SUPPORT + case TOK_STAR: + pn = QualifiedIdentifier(cx, ts, tc); + if (!pn) + return NULL; + notsharp = JS_TRUE; + break; + + case TOK_AT: + pn = AttributeIdentifier(cx, ts, tc); + if (!pn) + return NULL; + notsharp = JS_TRUE; + break; + + case TOK_XMLSTAGO: + pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE); + if (!pn) + return NULL; + notsharp = JS_TRUE; /* XXXbe could be sharp? */ + break; +#endif /* JS_HAS_XML_SUPPORT */ case TOK_STRING: #if JS_HAS_SHARP_VARS notsharp = JS_TRUE; -#endif /* FALL THROUGH */ +#endif + +#if JS_HAS_XML_SUPPORT + case TOK_XMLCDATA: + case TOK_XMLCOMMENT: + case TOK_XMLPI: +#endif case TOK_NAME: case TOK_OBJECT: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; - pn->pn_op = CURRENT_TOKEN(ts).t_op; pn->pn_atom = CURRENT_TOKEN(ts).t_atom; +#if JS_HAS_XML_SUPPORT + if (tt == TOK_XMLPI) + pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2; + else +#endif + pn->pn_op = CURRENT_TOKEN(ts).t_op; if (tt == TOK_NAME) { pn->pn_arity = PN_NAME; pn->pn_expr = NULL; pn->pn_slot = -1; pn->pn_attrs = 0; +#if JS_HAS_XML_SUPPORT + if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { + pn = QualifiedSuffix(cx, ts, pn, tc); + if (!pn) + return NULL; + break; + } +#endif + /* Unqualified __parent__ and __proto__ uses require activations. */ if (pn->pn_atom == cx->runtime->atomState.parentAtom || pn->pn_atom == cx->runtime->atomState.protoAtom) { @@ -3102,7 +4121,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; case TOK_NUMBER: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_dval = CURRENT_TOKEN(ts).t_dval; @@ -3112,7 +4131,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; case TOK_PRIMARY: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_op = CURRENT_TOKEN(ts).t_op; @@ -3125,21 +4144,12 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_EXPORT: case TOK_IMPORT: #endif - case TOK_RESERVED: - badWord = js_DeflateString(cx, CURRENT_TOKEN(ts).ptr, - (size_t) CURRENT_TOKEN(ts).pos.end.index - - CURRENT_TOKEN(ts).pos.begin.index); - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, - JSMSG_RESERVED_ID, badWord); - JS_free(cx, badWord); - return NULL; - case TOK_ERROR: /* The scanner or one of its subroutines reported the error. */ return NULL; default: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); return NULL; } @@ -3148,7 +4158,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (defsharp) { if (notsharp) { badsharp: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF); return NULL; } @@ -3329,6 +4339,157 @@ FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2, return JS_TRUE; } +#if JS_HAS_XML_SUPPORT + +static JSBool +FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) +{ + JSTokenType tt; + JSParseNode **pnp, *pn1, *pn2; + JSString *accum, *str; + uint32 i, j; + + JS_ASSERT(pn->pn_arity == PN_LIST); + tt = pn->pn_type; + pnp = &pn->pn_head; + pn1 = *pnp; + accum = NULL; + if ((pn->pn_extra & PNX_CANTFOLD) == 0) { + if (tt == TOK_XMLETAGO) + accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom); + else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) + accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom); + } + + for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) { + /* The parser already rejected end-tags with attributes. */ + JS_ASSERT(tt != TOK_XMLETAGO || i == 0); + switch (pn2->pn_type) { + case TOK_XMLATTR: + if (!accum) + goto cantfold; + /* FALL THROUGH */ + case TOK_XMLNAME: + case TOK_XMLSPACE: + case TOK_XMLTEXT: + case TOK_STRING: + if (pn2->pn_arity == PN_LIST) + goto cantfold; + str = ATOM_TO_STRING(pn2->pn_atom); + break; + + case TOK_XMLCDATA: + str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom)); + if (!str) + return JS_FALSE; + break; + + case TOK_XMLCOMMENT: + str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom)); + if (!str) + return JS_FALSE; + break; + + case TOK_XMLPI: + str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom), + ATOM_TO_STRING(pn2->pn_atom2)); + if (!str) + return JS_FALSE; + break; + + cantfold: + default: + JS_ASSERT(*pnp == pn1); + if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && + (i & 1) ^ (j & 1)) { +#ifdef DEBUG_brendanXXX + printf("1: %d, %d => %s\n", + i, j, accum ? JS_GetStringBytes(accum) : "NULL"); +#endif + } else if (accum && pn1 != pn2) { + while (pn1->pn_next != pn2) { + pn1 = RecycleTree(pn1, tc); + --pn->pn_count; + } + pn1->pn_type = TOK_XMLTEXT; + pn1->pn_op = JSOP_STRING; + pn1->pn_arity = PN_NULLARY; + pn1->pn_atom = js_AtomizeString(cx, accum, 0); + if (!pn1->pn_atom) + return JS_FALSE; + JS_ASSERT(pnp != &pn1->pn_next); + *pnp = pn1; + } + pnp = &pn2->pn_next; + pn1 = *pnp; + accum = NULL; + continue; + } + + if (accum) { + str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0) + ? js_AddAttributePart(cx, i & 1, accum, str) + : js_ConcatStrings(cx, accum, str); + if (!str) + return JS_FALSE; +#ifdef DEBUG_brendanXXX + printf("2: %d, %d => %s (%u)\n", + i, j, JS_GetStringBytes(str), JSSTRING_LENGTH(str)); +#endif + ++j; + } + accum = str; + } + + if (accum) { + str = NULL; + if ((pn->pn_extra & PNX_CANTFOLD) == 0) { + if (tt == TOK_XMLPTAGC) + str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom); + else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO) + str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom); + } + if (str) { + accum = js_ConcatStrings(cx, accum, str); + if (!accum) + return JS_FALSE; + } + + JS_ASSERT(*pnp == pn1); + while (pn1->pn_next) { + pn1 = RecycleTree(pn1, tc); + --pn->pn_count; + } + pn1->pn_type = TOK_XMLTEXT; + pn1->pn_op = JSOP_STRING; + pn1->pn_arity = PN_NULLARY; + pn1->pn_atom = js_AtomizeString(cx, accum, 0); + if (!pn1->pn_atom) + return JS_FALSE; + JS_ASSERT(pnp != &pn1->pn_next); + *pnp = pn1; + } + + if (pn1 && pn->pn_count == 1) { + /* + * Only one node under pn, and it has been folded: move pn1 onto pn + * unless pn is an XML root (in which case we need it to tell the code + * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an + * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid + * extra "<" and "/>" bracketing at runtime. + */ + if (!(pn->pn_extra & PNX_XMLROOT)) { + PN_MOVE_NODE(pn, pn1); + } else if (tt == TOK_XMLPTAGC) { + pn->pn_type = TOK_XMLELEM; + pn->pn_op = JSOP_TOXML; + } + } + return JS_TRUE; +} + +#endif /* JS_HAS_XML_SUPPORT */ + JSBool js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) { @@ -3342,11 +4503,61 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) switch (pn->pn_arity) { case PN_FUNC: + { + uint16 oldflags = tc->flags; + + tc->flags = (uint16) pn->pn_flags; if (!js_FoldConstants(cx, pn->pn_body, tc)) return JS_FALSE; + tc->flags = oldflags; break; + } case PN_LIST: +#if 0 /* JS_HAS_XML_SUPPORT */ + switch (pn->pn_type) { + case TOK_XMLELEM: + case TOK_XMLLIST: + case TOK_XMLPTAGC: + /* + * Try to fold this XML parse tree once, from the top down, into + * a JSXML tree with just one object wrapping the tree root. + * + * Certain subtrees could be folded similarly, but we'd have to + * ensure that none used namespace prefixes declared elsewhere in + * its super-tree, and we would have to convert each XML object + * created at runtime for such sub-trees back into a string, and + * concatenate and re-parse anyway. + */ + if ((pn->pn_extra & (PNX_XMLROOT | PNX_CANTFOLD)) == PNX_XMLROOT && + !(tc->flags & TCF_HAS_DEFXMLNS)) { + JSObject *obj; + JSAtom *atom; + + obj = js_ParseNodeToXMLObject(cx, pn); + if (!obj) + return JS_FALSE; + atom = js_AtomizeObject(cx, obj, 0); + if (!atom) + return JS_FALSE; + pn->pn_op = JSOP_XMLOBJECT; + pn->pn_arity = PN_NULLARY; + pn->pn_atom = atom; + return JS_TRUE; + } + + /* + * Can't fold from parse node to XML tree -- try folding strings + * as much as possible, and folding XML sub-trees bottom up to + * minimize string concatenation and ToXML/ToXMLList operations + * at runtime. + */ + break; + + default:; + } +#endif + /* Save the list head in pn1 for later use. */ for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (!js_FoldConstants(cx, pn2, tc)) @@ -3485,13 +4696,11 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) } /* Fill the buffer, advancing chars and recycling kids as we go. */ - for (pn2 = pn1; pn2; pn2 = pn3) { + for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) { str2 = ATOM_TO_STRING(pn2->pn_atom); length2 = str2->length; js_strncpy(chars, str2->chars, length2); chars += length2; - pn3 = pn2->pn_next; - RecycleTree(pn2, tc); } *chars = 0; @@ -3628,6 +4837,42 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) } break; +#if JS_HAS_XML_SUPPORT + case TOK_XMLELEM: + case TOK_XMLLIST: + case TOK_XMLPTAGC: + case TOK_XMLSTAGO: + case TOK_XMLETAGO: + case TOK_XMLNAME: + if (pn->pn_arity == PN_LIST) { + JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0); + if (!FoldXMLConstants(cx, pn, tc)) + return JS_FALSE; + } + break; + + case TOK_AT: + if (pn1->pn_type == TOK_XMLNAME) { + jsval v; + JSAtom *atom; + + v = ATOM_KEY(pn1->pn_atom); + if (!js_ToAttributeName(cx, &v)) + return JS_FALSE; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + atom = js_AtomizeObject(cx, JSVAL_TO_OBJECT(v), 0); + if (!atom) + return JS_FALSE; + + pn->pn_type = TOK_XMLNAME; + pn->pn_op = JSOP_OBJECT; + pn->pn_arity = PN_NULLARY; + pn->pn_atom = atom; + RecycleTree(pn1, tc); + } + break; +#endif /* JS_HAS_XML_SUPPORT */ + default:; } |
