summaryrefslogtreecommitdiffstats
path: root/src/dom/js/jsparse.c
diff options
context:
space:
mode:
authorBob Jamison <ishmalius@gmail.com>2007-03-05 10:34:59 +0000
committerishmal <ishmal@users.sourceforge.net>2007-03-05 10:34:59 +0000
commit33837efd4b94c4ebb80f95b3d9dbb6efd5499a98 (patch)
treede482d7687b3bffa97cc78608ba75a8ad7e52b60 /src/dom/js/jsparse.c
parentAdding optional dialog preview. Implments RFE [ 1435276 ] switch preview on/o... (diff)
downloadinkscape-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.c1719
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:;
}