зеркало из https://github.com/mozilla/gecko-dev.git
Support generator expressions for JS1.8 (380237, r=mrbkap).
This commit is contained in:
Родитель
58a40748d5
Коммит
48e1521edc
|
@ -294,8 +294,10 @@ MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing in after
|
|||
MSG_DEF(JSMSG_BAD_ITERATOR_RETURN, 212, 2, JSEXN_TYPEERR, "{0}.{1} returned a primitive value")
|
||||
MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used as namespace")
|
||||
MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}")
|
||||
MSG_DEF(JSMSG_BAD_YIELD_SYNTAX, 215, 0, JSEXN_SYNTAXERR, "yield expression must be parenthesized")
|
||||
MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX, 215, 1, JSEXN_SYNTAXERR, "{0} expression must be parenthesized")
|
||||
MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
|
||||
MSG_DEF(JSMSG_YIELD_FROM_FILTER, 217, 0, JSEXN_INTERNALERR, "yield not yet supported from filtering predicate")
|
||||
MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 218, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
|
||||
MSG_DEF(JSMSG_NON_LIST_XML_METHOD, 219, 2, JSEXN_TYPEERR, "cannot call {0} method on an XML list with {1} elements")
|
||||
MSG_DEF(JSMSG_BAD_DELETE_OPERAND, 220, 0, JSEXN_SYNTAXERR, "invalid delete operand")
|
||||
MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 221, 0, JSEXN_SYNTAXERR, "invalid increment/decrement operand")
|
||||
|
|
|
@ -41,14 +41,14 @@
|
|||
* JS configuration macros.
|
||||
*/
|
||||
#ifndef JS_VERSION
|
||||
#define JS_VERSION 170
|
||||
#define JS_VERSION 180
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Compile-time JS version configuration. The JS version numbers lie on the
|
||||
* number line like so:
|
||||
*
|
||||
* 1.0 1.1 1.2 1.3 1.4 ECMAv3 1.5 1.6
|
||||
* 1.0 1.1 1.2 1.3 1.4 ECMAv3 1.5 1.6 1.7 1.8
|
||||
* ^ ^
|
||||
* | |
|
||||
* basis for ECMAv1 close to ECMAv2
|
||||
|
@ -107,6 +107,7 @@
|
|||
#define JS_HAS_GENERATORS 0 /* has yield in generator function */
|
||||
#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */
|
||||
#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */
|
||||
#define JS_HAS_GENERATOR_EXPRS 0 /* has (expr for (lhs in iterable)) */
|
||||
|
||||
#elif JS_VERSION < 150
|
||||
|
||||
|
@ -139,6 +140,7 @@
|
|||
#define JS_HAS_GENERATORS 0 /* has yield in generator function */
|
||||
#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */
|
||||
#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */
|
||||
#define JS_HAS_GENERATOR_EXPRS 0 /* has (expr for (lhs in iterable)) */
|
||||
|
||||
#elif JS_VERSION == 160
|
||||
|
||||
|
@ -167,6 +169,7 @@
|
|||
#define JS_HAS_GENERATORS 0 /* has yield in generator function */
|
||||
#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */
|
||||
#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */
|
||||
#define JS_HAS_GENERATOR_EXPRS 0 /* has (expr for (lhs in iterable)) */
|
||||
|
||||
#elif JS_VERSION == 170
|
||||
|
||||
|
@ -195,6 +198,36 @@
|
|||
#define JS_HAS_GENERATORS 1 /* has yield in generator function */
|
||||
#define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */
|
||||
#define JS_HAS_DESTRUCTURING 1 /* has [a,b] = ... or {p:a,q:b} = ... */
|
||||
#define JS_HAS_GENERATOR_EXPRS 0 /* has (expr for (lhs in iterable)) */
|
||||
|
||||
#elif JS_VERSION == 180
|
||||
|
||||
#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */
|
||||
#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */
|
||||
#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */
|
||||
#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */
|
||||
#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */
|
||||
#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */
|
||||
#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */
|
||||
#define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */
|
||||
#define JS_HAS_XDR 1 /* has XDR API and internal support */
|
||||
#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */
|
||||
#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */
|
||||
#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */
|
||||
#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */
|
||||
#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */
|
||||
#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */
|
||||
#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */
|
||||
#define JS_HAS_CONST 1 /* has JS2 const as alternative var */
|
||||
#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */
|
||||
#define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */
|
||||
#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */
|
||||
#define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */
|
||||
#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */
|
||||
#define JS_HAS_GENERATORS 1 /* has yield in generator function */
|
||||
#define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */
|
||||
#define JS_HAS_DESTRUCTURING 1 /* has [a,b] = ... or {p:a,q:b} = ... */
|
||||
#define JS_HAS_GENERATOR_EXPRS 1 /* has (expr for (lhs in iterable)) */
|
||||
|
||||
#else
|
||||
|
||||
|
|
|
@ -4098,6 +4098,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
|
||||
/* Emit a bytecode pointing to the closure object in its immediate. */
|
||||
if (pn->pn_op != JSOP_NOP) {
|
||||
if ((pn->pn_flags & TCF_GENEXP_LAMBDA) &&
|
||||
js_NewSrcNote(cx, cg, SRC_GENEXP) < 0) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);
|
||||
break;
|
||||
}
|
||||
|
@ -5215,6 +5219,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
|
||||
#if JS_HAS_GENERATORS
|
||||
case TOK_YIELD:
|
||||
if (!(cg->treeContext.flags & TCF_IN_FUNCTION)) {
|
||||
js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
|
||||
JSMSG_BAD_RETURN_OR_YIELD,
|
||||
js_yield_str);
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (pn->pn_kid) {
|
||||
if (!js_EmitTree(cx, cg, pn->pn_kid))
|
||||
return JS_FALSE;
|
||||
|
@ -5222,6 +5232,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (pn->pn_hidden && js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
|
||||
return JS_FALSE;
|
||||
if (js_Emit1(cx, cg, JSOP_YIELD) < 0)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
|
@ -5866,10 +5878,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
#endif
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
case TOK_LP:
|
||||
if (pn2->pn_op != JSOP_SETCALL) {
|
||||
JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL);
|
||||
pn2->pn_op = JSOP_SETCALL;
|
||||
}
|
||||
top = CG_OFFSET(cg);
|
||||
if (!js_EmitTree(cx, cg, pn2))
|
||||
return JS_FALSE;
|
||||
|
|
|
@ -183,6 +183,7 @@ struct JSTreeContext { /* tree context for semantic checks */
|
|||
#define TCF_FUN_FLAGS 0x1E0 /* flags to propagate from FunctionBody */
|
||||
#define TCF_HAS_DEFXMLNS 0x200 /* default xml namespace = ...; parsed */
|
||||
#define TCF_HAS_FUNCTION_STMT 0x400 /* block contains a function statement */
|
||||
#define TCF_GENEXP_LAMBDA 0x800 /* flag lambda from generator expression */
|
||||
|
||||
#define TREE_CONTEXT_INIT(tc) \
|
||||
((tc)->flags = (tc)->numGlobalVars = 0, \
|
||||
|
@ -528,6 +529,7 @@ typedef enum JSSrcNoteType {
|
|||
SRC_INITPROP = 1, /* disjoint meaning applied to JSOP_INITELEM or
|
||||
to an index label in a regular (structuring)
|
||||
or a destructuring object initialiser */
|
||||
SRC_GENEXP = 1, /* JSOP_ANONFUNOBJ from generator expression */
|
||||
SRC_IF_ELSE = 2, /* JSOP_IFEQ bytecode is from an if-then-else */
|
||||
SRC_WHILE = 3, /* JSOP_IFEQ is from a while loop */
|
||||
SRC_FOR = 4, /* JSOP_NOP or JSOP_POP in for loop head */
|
||||
|
|
|
@ -802,6 +802,7 @@ typedef struct SprintStack {
|
|||
jsbytecode *opcodes; /* parallel stack of JS opcodes */
|
||||
uintN top; /* top of stack index */
|
||||
uintN inArrayInit; /* array initialiser/comprehension level */
|
||||
JSBool inGenExp; /* in generator expression */
|
||||
JSPrinter *printer; /* permanent output goes here */
|
||||
} SprintStack;
|
||||
|
||||
|
@ -870,6 +871,13 @@ GetStr(SprintStack *ss, uintN i)
|
|||
#define JSOP_GETPROP2 256
|
||||
#define JSOP_GETELEM2 257
|
||||
|
||||
static void
|
||||
AddParenSlop(SprintStack *ss)
|
||||
{
|
||||
memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP);
|
||||
ss->sprinter.offset += PAREN_SLOP;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
|
||||
{
|
||||
|
@ -892,8 +900,7 @@ PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
|
|||
: (op == JSOP_GETELEM2) ? JSOP_GETELEM
|
||||
: (jsbytecode) op;
|
||||
ss->top = ++top;
|
||||
memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP);
|
||||
ss->sprinter.offset += PAREN_SLOP;
|
||||
AddParenSlop(ss);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
|
@ -1579,6 +1586,29 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
|
|||
|
||||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
||||
static JSBool
|
||||
InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth)
|
||||
{
|
||||
size_t offsetsz, opcodesz;
|
||||
void *space;
|
||||
|
||||
INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP);
|
||||
|
||||
/* Allocate the parallel (to avoid padding) offset and opcode stacks. */
|
||||
offsetsz = depth * sizeof(ptrdiff_t);
|
||||
opcodesz = depth * sizeof(jsbytecode);
|
||||
JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz);
|
||||
if (!space)
|
||||
return JS_FALSE;
|
||||
ss->offsets = (ptrdiff_t *) space;
|
||||
ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
|
||||
|
||||
ss->top = ss->inArrayInit = 0;
|
||||
ss->inGenExp = JS_FALSE;
|
||||
ss->printer = jp;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
|
||||
* the decompiler starts at pc and continues until it reaches an opcode for
|
||||
|
@ -2653,6 +2683,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
#if JS_HAS_GENERATORS
|
||||
case JSOP_YIELD:
|
||||
op = JSOP_SETNAME; /* turn off most parens */
|
||||
|
||||
if (!ss->inGenExp || !(sn = js_GetSrcNote(jp->script, pc))) {
|
||||
rval = POP_STR();
|
||||
todo = (*rval != '\0')
|
||||
? Sprint(&ss->sprinter,
|
||||
|
@ -2663,39 +2695,86 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
js_yield_str, rval)
|
||||
: SprintCString(&ss->sprinter, js_yield_str);
|
||||
break;
|
||||
}
|
||||
LOCAL_ASSERT(SN_TYPE(sn) == SRC_HIDDEN);
|
||||
/* FALL THROUGH */
|
||||
|
||||
case JSOP_ARRAYPUSH:
|
||||
{
|
||||
uintN pos, blockpos, startpos;
|
||||
uintN pos, forpos;
|
||||
ptrdiff_t start;
|
||||
|
||||
/* Pop the expression being pushed or yielded. */
|
||||
rval = POP_STR();
|
||||
|
||||
/*
|
||||
* Skip down over iterables left stacked by JSOP_FOR* until
|
||||
* we hit a block-local or the new Array initialiser (empty
|
||||
* destructuring patterns yield zero-count blocks).
|
||||
*/
|
||||
pos = ss->top;
|
||||
while ((op = ss->opcodes[--pos]) != JSOP_ENTERBLOCK &&
|
||||
op != JSOP_NEWINIT) {
|
||||
LOCAL_ASSERT(pos != 0);
|
||||
if (pos == 0)
|
||||
break;
|
||||
}
|
||||
blockpos = pos;
|
||||
|
||||
/*
|
||||
* Make forpos index the space before the left-most |for| in
|
||||
* the single string of accumulated |for| heads and optional
|
||||
* final |if (condition)|.
|
||||
*/
|
||||
forpos = pos + (op == JSOP_ENTERBLOCK || op == JSOP_NEWINIT);
|
||||
LOCAL_ASSERT(forpos < ss->top);
|
||||
|
||||
/*
|
||||
* Now skip down over the block's local slots, if any. There
|
||||
* may be no locals for an empty destructuring pattern.
|
||||
*/
|
||||
while (ss->opcodes[pos] == JSOP_ENTERBLOCK) {
|
||||
if (pos == 0)
|
||||
break;
|
||||
--pos;
|
||||
}
|
||||
|
||||
if (saveop == JSOP_YIELD) {
|
||||
/*
|
||||
* Generator expression: decompile just rval followed by
|
||||
* the string starting at forpos. Leave the result string
|
||||
* in ss->offsets[0] so it can be recovered by our caller
|
||||
* (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the
|
||||
* top of stack to balance yield, which is an expression
|
||||
* (so has neutral stack balance).
|
||||
*/
|
||||
LOCAL_ASSERT(pos == 0);
|
||||
xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]);
|
||||
ss->sprinter.offset = PAREN_SLOP;
|
||||
todo = Sprint(&ss->sprinter, ss_format, rval, xval);
|
||||
if (todo < 0)
|
||||
return NULL;
|
||||
ss->offsets[0] = todo;
|
||||
++ss->top;
|
||||
return pc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Array comprehension: retract the sprinter to the beginning
|
||||
* of the array initialiser and decompile "[<rval> for ...]".
|
||||
*/
|
||||
LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
|
||||
startpos = pos;
|
||||
start = ss->offsets[pos];
|
||||
LOCAL_ASSERT(ss->sprinter.base[start] == '[' ||
|
||||
ss->sprinter.base[start] == '#');
|
||||
pos = blockpos + 1;
|
||||
LOCAL_ASSERT(pos < ss->top);
|
||||
xval = OFF2STR(&ss->sprinter, ss->offsets[pos]);
|
||||
LOCAL_ASSERT(forpos < ss->top);
|
||||
xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]);
|
||||
lval = OFF2STR(&ss->sprinter, start);
|
||||
RETRACT(&ss->sprinter, lval);
|
||||
|
||||
todo = Sprint(&ss->sprinter, "%s%s%.*s",
|
||||
lval, rval, rval - xval, xval);
|
||||
if (todo < 0)
|
||||
return NULL;
|
||||
ss->offsets[startpos] = todo;
|
||||
ss->offsets[pos] = todo;
|
||||
todo = -2;
|
||||
break;
|
||||
}
|
||||
|
@ -2782,10 +2861,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
case SRC_IF_ELSE:
|
||||
op = JSOP_NOP; /* turn off parens */
|
||||
rval = POP_STR();
|
||||
if (ss->inArrayInit) {
|
||||
if (ss->inArrayInit || ss->inGenExp) {
|
||||
LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF);
|
||||
ss->sprinter.offset -= PAREN_SLOP;
|
||||
if (Sprint(&ss->sprinter, " if (%s)", rval) < 0)
|
||||
return NULL;
|
||||
AddParenSlop(ss);
|
||||
} else {
|
||||
js_printf(SET_MAYBE_BRACE(jp),
|
||||
elseif ? " if (%s) {\n" : "\tif (%s) {\n",
|
||||
|
@ -2796,7 +2877,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
if (SN_TYPE(sn) == SRC_IF) {
|
||||
DECOMPILE_CODE(pc + oplen, len - oplen);
|
||||
} else {
|
||||
LOCAL_ASSERT(!ss->inArrayInit);
|
||||
LOCAL_ASSERT(!ss->inArrayInit && !ss->inGenExp);
|
||||
tail = js_GetSrcNoteOffset(sn, 0);
|
||||
DECOMPILE_CODE(pc + oplen, tail - oplen);
|
||||
jp->indent -= 4;
|
||||
|
@ -2827,7 +2908,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
DECOMPILE_CODE(pc + oplen, len - oplen);
|
||||
}
|
||||
|
||||
if (!ss->inArrayInit) {
|
||||
if (!ss->inArrayInit && !ss->inGenExp) {
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
}
|
||||
|
@ -2999,12 +3080,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
lval = OFF2STR(&ss->sprinter, todo);
|
||||
rval = GetStr(ss, ss->top-1);
|
||||
RETRACT(&ss->sprinter, rval);
|
||||
if (ss->inArrayInit) {
|
||||
if (ss->inArrayInit || ss->inGenExp) {
|
||||
if (ss->top > 1 &&
|
||||
(js_CodeSpec[ss->opcodes[ss->top-2]].format &
|
||||
JOF_FOR)) {
|
||||
ss->sprinter.offset -= PAREN_SLOP;
|
||||
}
|
||||
todo = Sprint(&ss->sprinter, " %s in %s)", lval, rval);
|
||||
if (todo < 0)
|
||||
return NULL;
|
||||
ss->offsets[ss->top-1] = todo;
|
||||
ss->sprinter.offset += PAREN_SLOP;
|
||||
ss->opcodes[ss->top-1] = op;
|
||||
AddParenSlop(ss);
|
||||
DECOMPILE_CODE(pc + oplen, tail - oplen);
|
||||
} else {
|
||||
js_printf(SET_MAYBE_BRACE(jp), "\t%s in %s) {\n",
|
||||
|
@ -3627,9 +3714,105 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
todo = STR2OFF(&ss->sprinter, rval);
|
||||
END_LITOPX_CASE
|
||||
|
||||
case JSOP_ANONFUNOBJ:
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
if (sn && SN_TYPE(sn) == SRC_GENEXP) {
|
||||
JSScript *inner, *outer;
|
||||
void *mark;
|
||||
SprintStack ss2;
|
||||
|
||||
LOAD_ATOM(0);
|
||||
obj = ATOM_TO_OBJECT(atom);
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, obj);
|
||||
LOCAL_ASSERT(FUN_INTERPRETED(fun));
|
||||
inner = fun->u.i.script;
|
||||
|
||||
/*
|
||||
* All allocation when decompiling is LIFO, using malloc
|
||||
* or, more commonly, arena-alloocating from cx->tempPool.
|
||||
* After InitSprintStack succeeds, we must release to mark
|
||||
* before returning.
|
||||
*/
|
||||
mark = JS_ARENA_MARK(&cx->tempPool);
|
||||
if (!InitSprintStack(cx, &ss2, jp, inner->depth))
|
||||
return NULL;
|
||||
ss2.inGenExp = JS_TRUE;
|
||||
|
||||
/*
|
||||
* Recursively decompile this generator function as an
|
||||
* un-parenthesized generator expression. The ss->inGenExp
|
||||
* special case of JSOP_YIELD shares array comprehension
|
||||
* decompilation code that leaves the result as the single
|
||||
* string pushed on ss2.
|
||||
*/
|
||||
outer = jp->script;
|
||||
LOCAL_ASSERT(JS_UPTRDIFF(pc, outer->code) <= outer->length);
|
||||
jp->script = inner;
|
||||
if (!Decompile(&ss2, inner->code, inner->length)) {
|
||||
JS_ARENA_RELEASE(&cx->tempPool, mark);
|
||||
return NULL;
|
||||
}
|
||||
jp->script = outer;
|
||||
|
||||
/*
|
||||
* Advance over this op and its null |this| push, and
|
||||
* arrange to advance over the call to this lambda.
|
||||
*/
|
||||
pc2 = pc;
|
||||
pc += len;
|
||||
LOCAL_ASSERT(*pc == JSOP_NULL);
|
||||
pc += JSOP_NULL_LENGTH;
|
||||
LOCAL_ASSERT(*pc == JSOP_CALL);
|
||||
LOCAL_ASSERT(GET_ARGC(pc) == 0);
|
||||
len = JSOP_CALL_LENGTH;
|
||||
|
||||
/*
|
||||
* Arrange to parenthesize this genexp unless:
|
||||
*
|
||||
* 1. It is consumed by a control flow bytecode such
|
||||
* as JSOP_TABLESWITCH (the syntax from which such ops
|
||||
* come always parenthesizes the controlling expression).
|
||||
* 2. It is the sole argument to a function call.
|
||||
* 3. It is the condition of an if statement and not of a
|
||||
* ?: expression.
|
||||
*
|
||||
* But always parenthesize if this genexp is an operand in
|
||||
* a comma expression (i.e. if JSOP_ANONFUNOBJ is preceded
|
||||
* immediately by JSOP_POP with SRC_PCDELTA).
|
||||
*/
|
||||
LOCAL_ASSERT(ss2.top == 1);
|
||||
ss2.opcodes[0] = JSOP_POP;
|
||||
op = (JSOp) pc[len];
|
||||
op = (((js_CodeSpec[op].format & JOF_PARENHEAD) ||
|
||||
((js_CodeSpec[op].format & JOF_INVOKE) &&
|
||||
GET_ARGC(pc + len) == 1) ||
|
||||
(((op == JSOP_IFEQ || op == JSOP_IFEQX) &&
|
||||
(sn2 = js_GetSrcNote(outer, pc + len)) &&
|
||||
SN_TYPE(sn2) != SRC_COND))) &&
|
||||
!(pc2 > outer->main &&
|
||||
pc2[-1] == JSOP_POP &&
|
||||
(sn2 = js_GetSrcNote(outer, pc2 - 1)) &&
|
||||
SN_TYPE(sn2) == SRC_PCDELTA))
|
||||
? JSOP_POP
|
||||
: JSOP_SETNAME;
|
||||
|
||||
/*
|
||||
* Alas, we have to malloc a copy of the result left on
|
||||
* the top of ss2 because both ss and ss2 arena-allocate
|
||||
* from cx's tempPool.
|
||||
*/
|
||||
rval = JS_strdup(cx, PopStr(&ss2, op));
|
||||
JS_ARENA_RELEASE(&cx->tempPool, mark);
|
||||
if (!rval)
|
||||
return NULL;
|
||||
todo = SprintCString(&ss->sprinter, rval);
|
||||
JS_free(cx, (void *)rval);
|
||||
break;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
|
||||
case JSOP_OBJECT:
|
||||
case JSOP_REGEXP:
|
||||
case JSOP_ANONFUNOBJ:
|
||||
case JSOP_NAMEDFUNOBJ:
|
||||
LOAD_ATOM(0);
|
||||
if (op == JSOP_OBJECT || op == JSOP_REGEXP) {
|
||||
|
@ -4029,6 +4212,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
break;
|
||||
|
||||
case JSOP_INITELEM:
|
||||
op = JSOP_SETNAME; /* turn off most parens */
|
||||
rval = POP_STR();
|
||||
xval = POP_STR();
|
||||
lval = POP_STR();
|
||||
|
@ -4272,28 +4456,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
return pc;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth)
|
||||
{
|
||||
size_t offsetsz, opcodesz;
|
||||
void *space;
|
||||
|
||||
INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP);
|
||||
|
||||
/* Allocate the parallel (to avoid padding) offset and opcode stacks. */
|
||||
offsetsz = depth * sizeof(ptrdiff_t);
|
||||
opcodesz = depth * sizeof(jsbytecode);
|
||||
JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz);
|
||||
if (!space)
|
||||
return JS_FALSE;
|
||||
ss->offsets = (ptrdiff_t *) space;
|
||||
ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
|
||||
|
||||
ss->top = ss->inArrayInit = 0;
|
||||
ss->printer = jp;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len,
|
||||
uintN pcdepth)
|
||||
|
|
|
@ -110,6 +110,9 @@ typedef enum JSOpLength {
|
|||
#define JOF_DECLARING 0x20000 /* var, const, or function declaration op */
|
||||
#define JOF_ATOMBASE 0x40000 /* atom segment base setting prefix op */
|
||||
#define JOF_CALLOP 0x80000 /* call operation pushing function and this */
|
||||
#define JOF_PARENHEAD 0x100000 /* opcode consumes value of expression in
|
||||
parenthesized statement head */
|
||||
#define JOF_INVOKE 0x200000 /* JSOP_CALL, JSOP_NEW, JSOP_EVAL */
|
||||
|
||||
#define JOF_TYPE_IS_EXTENDED_JUMP(t) \
|
||||
((unsigned)((t) - JOF_JUMPX) <= (unsigned)(JOF_LOOKUPSWITCHX - JOF_JUMPX))
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
* 13 +, -, etc. JSOP_ADD, JSOP_SUB, etc.
|
||||
* 14 *, /, % JSOP_MUL, JSOP_DIV, JSOP_MOD
|
||||
* 15 !, ~, etc. JSOP_NOT, JSOP_BITNOT, etc.
|
||||
* 16 0, function(){} etc. JSOP_ZERO, JSOP_ANONFUNOBJ, etc.
|
||||
* 16 3.14, 0, etc. JSOP_NUMBER, JSOP_ZERO, etc.
|
||||
* 17 delete, new JSOP_DEL*, JSOP_NEW
|
||||
* 18 x.y, f(), etc. JSOP_GETPROP, JSOP_CALL, etc.
|
||||
* 19 x, null, etc. JSOP_NAME, JSOP_NULL, etc.
|
||||
|
@ -100,12 +100,12 @@
|
|||
OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE)
|
||||
OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE|JOF_PARENHEAD)
|
||||
OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_RETURN, 5, "return", NULL, 1, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_GOTO, 6, "goto", NULL, 3, 0, 0, 0, JOF_JUMP)
|
||||
OPDEF(JSOP_IFEQ, 7, "ifeq", NULL, 3, 1, 0, 4, JOF_JUMP|JOF_DETECTING)
|
||||
OPDEF(JSOP_IFNE, 8, "ifne", NULL, 3, 1, 0, 0, JOF_JUMP)
|
||||
OPDEF(JSOP_IFNE, 8, "ifne", NULL, 3, 1, 0, 0, JOF_JUMP|JOF_PARENHEAD)
|
||||
|
||||
/* Get the arguments object for the current, lightweight function activation. */
|
||||
OPDEF(JSOP_ARGUMENTS, 9, js_arguments_str, js_arguments_str, 1, 0, 1, 18, JOF_BYTE)
|
||||
|
@ -138,7 +138,7 @@ OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 14, JOF_BYTE|J
|
|||
OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING)
|
||||
OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 15, JOF_BYTE)
|
||||
OPDEF(JSOP_NEG, 34, "neg", "- ", 1, 1, 1, 15, JOF_BYTE)
|
||||
OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16)
|
||||
OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16|JOF_INVOKE)
|
||||
OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 17, JOF_CONST|JOF_NAME|JOF_DEL)
|
||||
OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 17, JOF_CONST|JOF_PROP|JOF_DEL)
|
||||
OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL)
|
||||
|
@ -161,7 +161,7 @@ OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 3, JOF_CONST|
|
|||
OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC)
|
||||
OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_ASSIGNING|JOF_DETECTING)
|
||||
OPDEF(JSOP_CALLNAME, 57, "callname", NULL, 3, 0, 2, 19, JOF_CONST|JOF_NAME|JOF_CALLOP)
|
||||
OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16)
|
||||
OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)
|
||||
OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME)
|
||||
OPDEF(JSOP_NUMBER, 60, "number", NULL, 3, 0, 1, 16, JOF_CONST)
|
||||
OPDEF(JSOP_STRING, 61, "string", NULL, 3, 0, 1, 19, JOF_CONST)
|
||||
|
@ -175,8 +175,8 @@ OPDEF(JSOP_OR, 68, "or", NULL, 3, 1, 0, 5, JOF_JUMP|J
|
|||
OPDEF(JSOP_AND, 69, "and", NULL, 3, 1, 0, 6, JOF_JUMP|JOF_DETECTING)
|
||||
|
||||
/* The switch bytecodes have variable length. */
|
||||
OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLESWITCH|JOF_DETECTING)
|
||||
OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCH|JOF_DETECTING)
|
||||
OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLESWITCH|JOF_DETECTING|JOF_PARENHEAD)
|
||||
OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCH|JOF_DETECTING|JOF_PARENHEAD)
|
||||
|
||||
/* New, infallible/transitive identity ops. */
|
||||
OPDEF(JSOP_STRICTEQ, 72, "stricteq", NULL, 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING)
|
||||
|
@ -269,14 +269,14 @@ OPDEF(JSOP_SETSP, 117,"setsp", NULL, 3, 0, 0, 0, JOF_UINT16
|
|||
* CONDSWITCH is a decompilable NOP; CASE is ===, POP, jump if true, re-push
|
||||
* lval if false; and DEFAULT is POP lval and GOTO.
|
||||
*/
|
||||
OPDEF(JSOP_CONDSWITCH,118,"condswitch", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_CONDSWITCH,118,"condswitch", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_PARENHEAD)
|
||||
OPDEF(JSOP_CASE, 119,"case", NULL, 3, 1, 0, 0, JOF_JUMP)
|
||||
OPDEF(JSOP_DEFAULT, 120,"default", NULL, 3, 1, 0, 0, JOF_JUMP)
|
||||
|
||||
/*
|
||||
* ECMA-compliant call to eval op
|
||||
*/
|
||||
OPDEF(JSOP_EVAL, 121,"eval", NULL, 3, -1, 1, 18, JOF_UINT16)
|
||||
OPDEF(JSOP_EVAL, 121,"eval", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)
|
||||
|
||||
/*
|
||||
* ECMA-compliant helper for 'for (x[i] in o)' loops.
|
||||
|
@ -299,10 +299,10 @@ OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_CONST|
|
|||
OPDEF(JSOP_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_CONST|JOF_DECLARING)
|
||||
|
||||
/* Auto-clone (if needed due to re-parenting) and push an anonymous function. */
|
||||
OPDEF(JSOP_ANONFUNOBJ, 128, "anonfunobj", NULL, 3, 0, 1, 16, JOF_CONST)
|
||||
OPDEF(JSOP_ANONFUNOBJ, 128, "anonfunobj", NULL, 3, 0, 1, 19, JOF_CONST)
|
||||
|
||||
/* ECMA ed. 3 named function expression. */
|
||||
OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 16, JOF_CONST)
|
||||
OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 19, JOF_CONST)
|
||||
|
||||
/*
|
||||
* Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately
|
||||
|
@ -345,15 +345,15 @@ OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 5, 0, 0, 0, JOF_INDEXC
|
|||
|
||||
/* Extended jumps. */
|
||||
OPDEF(JSOP_GOTOX, 139,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX)
|
||||
OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 3, JOF_JUMPX|JOF_DETECTING)
|
||||
OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX)
|
||||
OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 4, JOF_JUMPX|JOF_DETECTING)
|
||||
OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX|JOF_PARENHEAD)
|
||||
OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 5, JOF_JUMPX|JOF_DETECTING)
|
||||
OPDEF(JSOP_ANDX, 143,"andx", NULL, 5, 1, 0, 6, JOF_JUMPX|JOF_DETECTING)
|
||||
OPDEF(JSOP_GOSUBX, 144,"gosubx", NULL, 5, 0, 0, 0, JOF_JUMPX)
|
||||
OPDEF(JSOP_CASEX, 145,"casex", NULL, 5, 1, 0, 0, JOF_JUMPX)
|
||||
OPDEF(JSOP_DEFAULTX, 146,"defaultx", NULL, 5, 1, 0, 0, JOF_JUMPX)
|
||||
OPDEF(JSOP_TABLESWITCHX, 147,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING)
|
||||
OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX|JOF_DETECTING)
|
||||
OPDEF(JSOP_TABLESWITCHX, 147,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING|JOF_PARENHEAD)
|
||||
OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX|JOF_DETECTING|JOF_PARENHEAD)
|
||||
|
||||
/* Placeholders for a real jump opcode set during backpatch chain fixup. */
|
||||
OPDEF(JSOP_BACKPATCH, 149,"backpatch",NULL, 3, 0, 0, 0, JOF_JUMP|JOF_BACKPATCH)
|
||||
|
|
576
js/src/jsparse.c
576
js/src/jsparse.c
|
@ -102,6 +102,10 @@ typedef JSParseNode *
|
|||
JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
JSTokenType tt, JSBool afterDot);
|
||||
|
||||
typedef JSParseNode *
|
||||
JSParenParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
JSParseNode *pn1, JSBool *genexp);
|
||||
|
||||
static JSParser FunctionStmt;
|
||||
static JSParser FunctionExpr;
|
||||
static JSParser Statements;
|
||||
|
@ -123,6 +127,7 @@ static JSParser MulExpr;
|
|||
static JSParser UnaryExpr;
|
||||
static JSMemberParser MemberExpr;
|
||||
static JSPrimaryParser PrimaryExpr;
|
||||
static JSParenParser ParenExpr;
|
||||
|
||||
/*
|
||||
* Insist that the next token be of type tt, or report errno and return null.
|
||||
|
@ -224,6 +229,8 @@ NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
|
|||
maxparsenodes = parsenodes - recyclednodes;
|
||||
}
|
||||
#endif
|
||||
memset(&pn->pn_u, 0, sizeof pn->pn_u);
|
||||
pn->pn_next = NULL;
|
||||
return pn;
|
||||
}
|
||||
|
||||
|
@ -245,9 +252,7 @@ NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity,
|
|||
pn->pn_pos = tp->pos;
|
||||
pn->pn_op = JSOP_NOP;
|
||||
pn->pn_arity = arity;
|
||||
pn->pn_next = NULL;
|
||||
pn->pn_ts = ts;
|
||||
pn->pn_source = NULL;
|
||||
return pn;
|
||||
}
|
||||
|
||||
|
@ -321,9 +326,7 @@ NewBinary(JSContext *cx, JSTokenType tt,
|
|||
pn->pn_arity = PN_BINARY;
|
||||
pn->pn_left = left;
|
||||
pn->pn_right = right;
|
||||
pn->pn_next = NULL;
|
||||
pn->pn_ts = NULL;
|
||||
pn->pn_source = NULL;
|
||||
pn->pn_ts = left->pn_ts;
|
||||
return pn;
|
||||
}
|
||||
|
||||
|
@ -1277,9 +1280,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
rhs->pn_type = TOK_NAME;
|
||||
rhs->pn_op = JSOP_GETARG;
|
||||
rhs->pn_atom = cx->runtime->atomState.emptyAtom;
|
||||
rhs->pn_expr = NULL;
|
||||
rhs->pn_slot = slot;
|
||||
rhs->pn_attrs = 0;
|
||||
|
||||
item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc);
|
||||
if (!item)
|
||||
|
@ -1542,7 +1543,7 @@ Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
JSParseNode *pn;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
|
||||
pn = Expr(cx, ts, tc);
|
||||
pn = ParenExpr(cx, ts, tc, NULL, NULL);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
|
||||
|
@ -1599,9 +1600,7 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
return NULL;
|
||||
pn->pn_op = JSOP_NAME;
|
||||
pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
|
||||
pn->pn_expr = NULL;
|
||||
pn->pn_slot = -1;
|
||||
pn->pn_attrs = 0;
|
||||
|
||||
ts->flags |= TSF_OPERAND;
|
||||
while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
|
||||
|
@ -1616,15 +1615,12 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
ts->flags |= TSF_KEYWORD_IS_NAME;
|
||||
if (js_MatchToken(cx, ts, TOK_STAR)) {
|
||||
pn2->pn_op = JSOP_IMPORTALL;
|
||||
pn2->pn_atom = NULL;
|
||||
pn2->pn_slot = -1;
|
||||
pn2->pn_attrs = 0;
|
||||
} else {
|
||||
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
|
||||
pn2->pn_op = JSOP_GETPROP;
|
||||
pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
|
||||
pn2->pn_slot = -1;
|
||||
pn2->pn_attrs = 0;
|
||||
}
|
||||
ts->flags &= ~TSF_KEYWORD_IS_NAME;
|
||||
pn2->pn_expr = pn;
|
||||
|
@ -1907,6 +1903,22 @@ BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
MakeSetCall(JSContext *cx, JSParseNode *pn, uintN msg)
|
||||
{
|
||||
JSParseNode *pn2;
|
||||
|
||||
JS_ASSERT(pn->pn_arity == PN_LIST);
|
||||
JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL);
|
||||
pn2 = pn->pn_head;
|
||||
if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_flags & TCF_GENEXP_LAMBDA)) {
|
||||
js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, msg);
|
||||
return JS_FALSE;
|
||||
}
|
||||
pn->pn_op = JSOP_SETCALL;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
|
||||
* LHS expression except a destructuring initialiser, and R is on the stack.
|
||||
|
@ -1943,8 +1955,8 @@ BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
|
|||
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
case TOK_LP:
|
||||
JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL);
|
||||
pn->pn_op = JSOP_SETCALL;
|
||||
if (!MakeSetCall(cx, pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
@ -2300,13 +2312,9 @@ ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
JSParseNode *pn, *pn2;
|
||||
|
||||
tt = CURRENT_TOKEN(ts).type;
|
||||
if (!(tc->flags & TCF_IN_FUNCTION)) {
|
||||
if (tt == TOK_RETURN && !(tc->flags & TCF_IN_FUNCTION)) {
|
||||
js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
|
||||
JSMSG_BAD_RETURN_OR_YIELD,
|
||||
#if JS_HAS_GENERATORS
|
||||
(tt == TOK_YIELD) ? js_yield_str :
|
||||
#endif
|
||||
js_return_str);
|
||||
JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2346,7 +2354,6 @@ ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
if (tt == TOK_RETURN)
|
||||
#endif
|
||||
tc->flags |= TCF_RETURN_VOID;
|
||||
pn->pn_kid = NULL;
|
||||
}
|
||||
|
||||
if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
|
||||
|
@ -2392,9 +2399,7 @@ PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
pn->pn_type = TOK_LEXICALSCOPE;
|
||||
pn->pn_op = JSOP_LEAVEBLOCK;
|
||||
pn->pn_atom = atom;
|
||||
pn->pn_expr = NULL;
|
||||
pn->pn_slot = -1;
|
||||
pn->pn_attrs = 0;
|
||||
return pn;
|
||||
}
|
||||
|
||||
|
@ -2511,9 +2516,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
return NULL;
|
||||
pn2->pn_op = JSOP_NAME;
|
||||
pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
|
||||
pn2->pn_expr = NULL;
|
||||
pn2->pn_slot = -1;
|
||||
pn2->pn_attrs = 0;
|
||||
PN_APPEND(pn, pn2);
|
||||
} while (js_MatchToken(cx, ts, TOK_COMMA));
|
||||
}
|
||||
|
@ -2589,7 +2592,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
|
||||
|
||||
/* pn1 points to the switch's discriminant. */
|
||||
pn1 = Expr(cx, ts, tc);
|
||||
pn1 = ParenExpr(cx, ts, tc, NULL, NULL);
|
||||
if (!pn1)
|
||||
return NULL;
|
||||
|
||||
|
@ -2616,15 +2619,13 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
return NULL;
|
||||
}
|
||||
seenDefault = JS_TRUE;
|
||||
/* fall through */
|
||||
/* FALL THROUGH */
|
||||
|
||||
case TOK_CASE:
|
||||
pn3 = NewParseNode(cx, ts, PN_BINARY, tc);
|
||||
if (!pn3)
|
||||
return NULL;
|
||||
if (tt == TOK_DEFAULT) {
|
||||
pn3->pn_left = NULL;
|
||||
} else {
|
||||
if (tt == TOK_CASE) {
|
||||
pn3->pn_left = Expr(cx, ts, tc);
|
||||
if (!pn3->pn_left)
|
||||
return NULL;
|
||||
|
@ -2872,8 +2873,10 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
} else {
|
||||
pn2 = pn1;
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
if (pn2->pn_type == TOK_LP)
|
||||
pn2->pn_op = JSOP_SETCALL;
|
||||
if (pn2->pn_type == TOK_LP &&
|
||||
!MakeSetCall(cx, pn2, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (pn2->pn_type == TOK_UNARYOP)
|
||||
|
@ -3094,9 +3097,6 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
if (!pn3)
|
||||
return NULL;
|
||||
pn3->pn_atom = label;
|
||||
pn3->pn_expr = NULL;
|
||||
pn3->pn_slot = 0;
|
||||
pn3->pn_attrs = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -3107,7 +3107,6 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
}
|
||||
|
||||
pn2->pn_kid1 = pn3;
|
||||
pn2->pn_kid2 = NULL;
|
||||
#if JS_HAS_CATCH_GUARD
|
||||
/*
|
||||
* We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
|
||||
|
@ -3149,7 +3148,6 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
js_PopStatement(tc);
|
||||
} else {
|
||||
js_UngetToken(ts);
|
||||
pn->pn_kid3 = NULL;
|
||||
}
|
||||
if (!catchList && !pn->pn_kid3) {
|
||||
js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
|
||||
|
@ -3283,7 +3281,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
if (!pn)
|
||||
return NULL;
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
|
||||
pn2 = Expr(cx, ts, tc);
|
||||
pn2 = ParenExpr(cx, ts, tc, NULL, NULL);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
|
||||
|
@ -3419,7 +3417,6 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
pn1->pn_atom = atom;
|
||||
pn1->pn_expr = tc->blockNode;
|
||||
pn1->pn_slot = -1;
|
||||
pn1->pn_attrs = 0;
|
||||
tc->blockNode = pn1;
|
||||
}
|
||||
|
||||
|
@ -3469,7 +3466,6 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
if (!pn)
|
||||
return NULL;
|
||||
pn->pn_type = TOK_SEMI;
|
||||
pn->pn_kid = NULL;
|
||||
return pn;
|
||||
|
||||
#if JS_HAS_DEBUGGER_KEYWORD
|
||||
|
@ -3712,9 +3708,9 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
return NULL;
|
||||
pn2->pn_op = JSOP_NAME;
|
||||
pn2->pn_atom = atom;
|
||||
pn2->pn_expr = NULL;
|
||||
pn2->pn_slot = -1;
|
||||
pn2->pn_attrs = let ? 0 : data.u.var.attrs;
|
||||
if (!let)
|
||||
pn2->pn_attrs = data.u.var.attrs;
|
||||
PN_APPEND(pn, pn2);
|
||||
|
||||
if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
|
||||
|
@ -3760,7 +3756,8 @@ Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
if (pn2->pn_type == TOK_YIELD) {
|
||||
js_ReportCompileErrorNumber(cx, pn2,
|
||||
JSREPORT_PN | JSREPORT_ERROR,
|
||||
JSMSG_BAD_YIELD_SYNTAX);
|
||||
JSMSG_BAD_GENERATOR_SYNTAX,
|
||||
js_yield_str);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
@ -3839,8 +3836,8 @@ AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
#endif
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
case TOK_LP:
|
||||
JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL);
|
||||
pn2->pn_op = JSOP_SETCALL;
|
||||
if (!MakeSetCall(cx, pn2, JSMSG_BAD_LEFTSIDE_OF_ASS))
|
||||
return NULL;
|
||||
break;
|
||||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
|
@ -4106,8 +4103,8 @@ 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_EVAL);
|
||||
kid->pn_op = JSOP_SETCALL;
|
||||
if (!MakeSetCall(cx, kid, JSMSG_BAD_INCOP_OPERAND))
|
||||
return JS_FALSE;
|
||||
/* FALL THROUGH */
|
||||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
|
@ -4186,6 +4183,11 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
*/
|
||||
while (pn2->pn_type == TOK_RP)
|
||||
pn2 = pn2->pn_kid;
|
||||
if (pn2->pn_type == TOK_LP &&
|
||||
pn2->pn_op != JSOP_SETCALL &&
|
||||
!MakeSetCall(cx, pn2, JSMSG_BAD_DELETE_OPERAND)) {
|
||||
return NULL;
|
||||
}
|
||||
pn->pn_kid = pn2;
|
||||
break;
|
||||
|
||||
|
@ -4219,6 +4221,244 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
return pn;
|
||||
}
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
|
||||
/*
|
||||
* Starting from a |for| keyword after the first array initialiser element or
|
||||
* an expression in an open parenthesis, parse the tail of the comprehension
|
||||
* or generator expression signified by this |for| keyword in context.
|
||||
*
|
||||
* Return null on failure, else return the top-most parse node for the array
|
||||
* comprehension or generator expression, with a unary node as the body of the
|
||||
* (possibly nested) for-loop, initialized by |type, op, kid|.
|
||||
*/
|
||||
static JSParseNode *
|
||||
ComprehensionTail(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
JSTokenType type, JSOp op, JSParseNode *kid)
|
||||
{
|
||||
JSParseNode *pn, *pn2, *pn3, **pnp;
|
||||
JSStmtInfo stmtInfo;
|
||||
BindData data;
|
||||
JSRuntime *rt;
|
||||
JSTokenType tt;
|
||||
JSAtom *atom;
|
||||
|
||||
JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_FOR);
|
||||
|
||||
/*
|
||||
* Make a parse-node and literal object representing the block scope of
|
||||
* this array comprehension or generator expression.
|
||||
*/
|
||||
pn = PushLexicalScope(cx, ts, tc, &stmtInfo);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
pnp = &pn->pn_expr;
|
||||
|
||||
data.pn = NULL;
|
||||
data.ts = ts;
|
||||
data.obj = tc->blockChain;
|
||||
data.op = JSOP_NOP;
|
||||
data.binder = BindLet;
|
||||
data.u.let.index = 0;
|
||||
data.u.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
|
||||
|
||||
rt = cx->runtime;
|
||||
do {
|
||||
/*
|
||||
* FOR node is binary, left is loop control and right is body. Use
|
||||
* index to count each block-local let-variable on the left-hand side
|
||||
* of the IN.
|
||||
*/
|
||||
pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
|
||||
pn2->pn_op = JSOP_FORIN;
|
||||
if (js_MatchToken(cx, ts, TOK_NAME)) {
|
||||
if (CURRENT_TOKEN(ts).t_atom == rt->atomState.eachAtom)
|
||||
pn2->pn_op = JSOP_FOREACH;
|
||||
else
|
||||
js_UngetToken(ts);
|
||||
}
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
|
||||
|
||||
tt = js_GetToken(cx, ts);
|
||||
switch (tt) {
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case TOK_LB:
|
||||
case TOK_LC:
|
||||
pn3 = DestructuringExpr(cx, &data, tc, tt);
|
||||
if (!pn3)
|
||||
return NULL;
|
||||
|
||||
if (pn3->pn_type != TOK_RB || pn3->pn_count != 2) {
|
||||
js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS |
|
||||
JSREPORT_ERROR,
|
||||
JSMSG_BAD_FOR_LEFTSIDE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Destructuring requires [key, value] enumeration. */
|
||||
if (pn2->pn_op != JSOP_FOREACH)
|
||||
pn2->pn_op = JSOP_FOREACHKEYVAL;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TOK_NAME:
|
||||
atom = CURRENT_TOKEN(ts).t_atom;
|
||||
if (!data.binder(cx, &data, atom, tc))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Create a name node with pn_op JSOP_NAME. We can't set pn_op to
|
||||
* JSOP_GETLOCAL here, because we don't yet know the block's depth
|
||||
* in the operand stack frame. The code generator computes that,
|
||||
* and it tries to bind all names to slots, so we must let it do
|
||||
* the deed.
|
||||
*/
|
||||
pn3 = NewParseNode(cx, ts, PN_NAME, tc);
|
||||
if (!pn3)
|
||||
return NULL;
|
||||
pn3->pn_op = JSOP_NAME;
|
||||
pn3->pn_atom = atom;
|
||||
pn3->pn_slot = -1;
|
||||
break;
|
||||
|
||||
default:
|
||||
js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS|JSREPORT_ERROR,
|
||||
JSMSG_NO_VARIABLE_NAME);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
|
||||
pn3 = NewBinary(cx, TOK_IN, JSOP_NOP, pn3,
|
||||
Expr(cx, ts, tc), tc);
|
||||
if (!pn3)
|
||||
return NULL;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
|
||||
pn2->pn_left = pn3;
|
||||
*pnp = pn2;
|
||||
pnp = &pn2->pn_right;
|
||||
} while (js_MatchToken(cx, ts, TOK_FOR));
|
||||
|
||||
if (js_MatchToken(cx, ts, TOK_IF)) {
|
||||
pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
pn2->pn_kid1 = Condition(cx, ts, tc);
|
||||
if (!pn2->pn_kid1)
|
||||
return NULL;
|
||||
*pnp = pn2;
|
||||
pnp = &pn2->pn_kid2;
|
||||
}
|
||||
|
||||
pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
pn2->pn_type = type;
|
||||
pn2->pn_op = op;
|
||||
pn2->pn_kid = kid;
|
||||
*pnp = pn2;
|
||||
|
||||
js_PopStatement(tc);
|
||||
return pn;
|
||||
}
|
||||
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
|
||||
/*
|
||||
* Starting from a |for| keyword after an expression, parse the comprehension
|
||||
* tail completing this generator expression. Wrap the expression at kid in a
|
||||
* generator function that is immediately called to evaluate to the generator
|
||||
* iterator that is the value of this generator expression.
|
||||
*
|
||||
* Callers pass a blank unary node via pn, which GeneratorExpr fills in as the
|
||||
* yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
|
||||
* expression-statement node that constitutes the body of the |for| loop(s) in
|
||||
* the generator function.
|
||||
*
|
||||
* Note how unlike Python, we do not evaluate the expression to the right of
|
||||
* the first |in| in the chain of |for| heads. Instead, a generator expression
|
||||
* is merely sugar for a generator function expression and its application.
|
||||
*/
|
||||
static JSParseNode *
|
||||
GeneratorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
uintN oldflags, JSParseNode *pn, JSParseNode *kid)
|
||||
{
|
||||
JSParseNode *body, *lambda;
|
||||
JSFunction *fun;
|
||||
|
||||
/* Initialize pn, connecting it to kid. */
|
||||
JS_ASSERT(pn->pn_arity == PN_UNARY);
|
||||
pn->pn_type = TOK_YIELD;
|
||||
pn->pn_op = JSOP_YIELD;
|
||||
pn->pn_pos = kid->pn_pos;
|
||||
pn->pn_kid = kid;
|
||||
pn->pn_hidden = JS_TRUE;
|
||||
|
||||
/*
|
||||
* Parse the comprehension tail at hand, making pn the kid of the loop
|
||||
* body's expression statement.
|
||||
*/
|
||||
body = ComprehensionTail(cx, ts, tc, TOK_SEMI, JSOP_NOP, pn);
|
||||
if (!body)
|
||||
return NULL;
|
||||
body->pn_pos.begin = kid->pn_pos.begin;
|
||||
|
||||
/*
|
||||
* Make the generator function and flag it as interpreted ASAP (see the
|
||||
* comment in FunctionBody).
|
||||
*/
|
||||
fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_LAMBDA, cx->fp->varobj,
|
||||
NULL);
|
||||
if (!fun)
|
||||
return NULL;
|
||||
fun->flags |= JSFUN_INTERPRETED;
|
||||
|
||||
/*
|
||||
* This generator function is referenced by an anonymous function object
|
||||
* node. Here is where we must take care to propagate certain tc->flags
|
||||
* that may have changed from oldflags to reflect crucial facts about the
|
||||
* expression on the left of |for| and in the comprehension tail after it.
|
||||
*/
|
||||
lambda = NewParseNode(cx, ts, PN_FUNC, tc);
|
||||
if (!lambda)
|
||||
return NULL;
|
||||
lambda->pn_type = TOK_FUNCTION;
|
||||
lambda->pn_op = JSOP_ANONFUNOBJ;
|
||||
lambda->pn_pos.begin = body->pn_pos.begin;
|
||||
lambda->pn_funAtom = js_AtomizeObject(cx, fun->object, 0);
|
||||
if (!lambda->pn_funAtom)
|
||||
return NULL;
|
||||
lambda->pn_body = body;
|
||||
lambda->pn_flags = TCF_FUN_IS_GENERATOR | TCF_GENEXP_LAMBDA |
|
||||
((oldflags ^ tc->flags) & TCF_FUN_FLAGS);
|
||||
|
||||
/*
|
||||
* Re-use pn to name the result node, a call expression invoking the
|
||||
* anonymous generator function object.
|
||||
*/
|
||||
pn = NewParseNode(cx, ts, PN_LIST, tc);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
pn->pn_type = TOK_LP;
|
||||
pn->pn_op = JSOP_CALL;
|
||||
pn->pn_pos.begin = lambda->pn_pos.begin;
|
||||
PN_INIT_LIST_1(pn, lambda);
|
||||
|
||||
body->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
|
||||
tc->flags = oldflags;
|
||||
return pn;
|
||||
}
|
||||
|
||||
static const char js_generator_str[] = "generator";
|
||||
|
||||
#endif /* JS_HAS_GENERATOR_EXPRS */
|
||||
#endif /* JS_HAS_GENERATORS */
|
||||
|
||||
static JSBool
|
||||
ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
JSParseNode *listNode)
|
||||
|
@ -4230,6 +4470,9 @@ ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
ts->flags &= ~TSF_OPERAND;
|
||||
if (!matched) {
|
||||
do {
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
uintN oldflags = tc->flags;
|
||||
#endif
|
||||
JSParseNode *argNode = AssignExpr(cx, ts, tc);
|
||||
if (!argNode)
|
||||
return JS_FALSE;
|
||||
|
@ -4237,9 +4480,28 @@ ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
if (argNode->pn_type == TOK_YIELD) {
|
||||
js_ReportCompileErrorNumber(cx, argNode,
|
||||
JSREPORT_PN | JSREPORT_ERROR,
|
||||
JSMSG_BAD_YIELD_SYNTAX);
|
||||
JSMSG_BAD_GENERATOR_SYNTAX,
|
||||
js_yield_str);
|
||||
return JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
if (js_MatchToken(cx, ts, TOK_FOR)) {
|
||||
JSParseNode *pn = NewParseNode(cx, ts, PN_UNARY, tc);
|
||||
if (!pn)
|
||||
return JS_FALSE;
|
||||
argNode = GeneratorExpr(cx, ts, tc, oldflags, pn, argNode);
|
||||
if (!argNode)
|
||||
return JS_FALSE;
|
||||
if (listNode->pn_count > 1 ||
|
||||
js_PeekToken(cx, ts) == TOK_COMMA) {
|
||||
js_ReportCompileErrorNumber(cx, argNode,
|
||||
JSREPORT_PN | JSREPORT_ERROR,
|
||||
JSMSG_BAD_GENERATOR_SYNTAX,
|
||||
js_generator_str);
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
PN_APPEND(listNode, argNode);
|
||||
} while (js_MatchToken(cx, ts, TOK_COMMA));
|
||||
|
@ -4301,9 +4563,7 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
pn2->pn_op = JSOP_XMLNAME;
|
||||
pn2->pn_arity = PN_UNARY;
|
||||
pn2->pn_kid = pn;
|
||||
pn2->pn_next = NULL;
|
||||
pn2->pn_ts = ts;
|
||||
pn2->pn_source = NULL;
|
||||
pn = pn2;
|
||||
}
|
||||
}
|
||||
|
@ -4314,7 +4574,6 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
if (!pn2)
|
||||
return NULL;
|
||||
pn2->pn_slot = -1;
|
||||
pn2->pn_attrs = 0;
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
|
||||
tt = js_GetToken(cx, ts);
|
||||
|
@ -4553,9 +4812,7 @@ PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
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;
|
||||
}
|
||||
|
@ -4587,7 +4844,6 @@ QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
|
|||
: CURRENT_TOKEN(ts).t_atom;
|
||||
pn2->pn_expr = pn;
|
||||
pn2->pn_slot = -1;
|
||||
pn2->pn_attrs = 0;
|
||||
return pn2;
|
||||
}
|
||||
|
||||
|
@ -5289,11 +5545,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
if (index == 0 &&
|
||||
pn->pn_count != 0 &&
|
||||
js_MatchToken(cx, ts, TOK_FOR)) {
|
||||
JSParseNode **pnp, *pnexp, *pntop, *pnlet;
|
||||
BindData data;
|
||||
JSRuntime *rt;
|
||||
JSStmtInfo stmtInfo;
|
||||
JSAtom *atom;
|
||||
JSParseNode *pnexp, *pntop;
|
||||
|
||||
/* Relabel pn as an array comprehension node. */
|
||||
pn->pn_type = TOK_ARRAYCOMP;
|
||||
|
@ -5310,130 +5562,11 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
: &pn->pn_head;
|
||||
*pn->pn_tail = NULL;
|
||||
|
||||
/*
|
||||
* Make a parse-node and literal object representing the array
|
||||
* comprehension's block scope.
|
||||
*/
|
||||
pntop = PushLexicalScope(cx, ts, tc, &stmtInfo);
|
||||
pntop = ComprehensionTail(cx, ts, tc, TOK_ARRAYPUSH,
|
||||
JSOP_ARRAYPUSH, pnexp);
|
||||
if (!pntop)
|
||||
return NULL;
|
||||
pnp = &pntop->pn_expr;
|
||||
|
||||
data.pn = NULL;
|
||||
data.ts = ts;
|
||||
data.obj = tc->blockChain;
|
||||
data.op = JSOP_NOP;
|
||||
data.binder = BindLet;
|
||||
data.u.let.index = 0;
|
||||
data.u.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
|
||||
|
||||
rt = cx->runtime;
|
||||
do {
|
||||
/*
|
||||
* FOR node is binary, left is control and right is body.
|
||||
* Use index to count each block-local let-variable on the
|
||||
* left-hand side of IN.
|
||||
*/
|
||||
pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
|
||||
pn2->pn_op = JSOP_FORIN;
|
||||
if (js_MatchToken(cx, ts, TOK_NAME)) {
|
||||
if (CURRENT_TOKEN(ts).t_atom == rt->atomState.eachAtom)
|
||||
pn2->pn_op = JSOP_FOREACH;
|
||||
else
|
||||
js_UngetToken(ts);
|
||||
}
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
|
||||
|
||||
tt = js_GetToken(cx, ts);
|
||||
switch (tt) {
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case TOK_LB:
|
||||
case TOK_LC:
|
||||
pnlet = DestructuringExpr(cx, &data, tc, tt);
|
||||
if (!pnlet)
|
||||
return NULL;
|
||||
|
||||
if (pnlet->pn_type != TOK_RB || pnlet->pn_count != 2) {
|
||||
js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS |
|
||||
JSREPORT_ERROR,
|
||||
JSMSG_BAD_FOR_LEFTSIDE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Destructuring requires [key, value] enumeration. */
|
||||
if (pn2->pn_op != JSOP_FOREACH)
|
||||
pn2->pn_op = JSOP_FOREACHKEYVAL;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TOK_NAME:
|
||||
atom = CURRENT_TOKEN(ts).t_atom;
|
||||
if (!data.binder(cx, &data, atom, tc))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Create a name node with op JSOP_NAME. We can't set
|
||||
* op to JSOP_GETLOCAL here, because we don't yet know
|
||||
* the block's depth in the operand stack frame. The
|
||||
* code generator computes that, and it tries to bind
|
||||
* all names to slots, so we must let it do the deed.
|
||||
*/
|
||||
pnlet = NewParseNode(cx, ts, PN_NAME, tc);
|
||||
if (!pnlet)
|
||||
return NULL;
|
||||
pnlet->pn_op = JSOP_NAME;
|
||||
pnlet->pn_atom = atom;
|
||||
pnlet->pn_expr = NULL;
|
||||
pnlet->pn_slot = -1;
|
||||
pnlet->pn_attrs = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS|JSREPORT_ERROR,
|
||||
JSMSG_NO_VARIABLE_NAME);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
|
||||
pn3 = NewBinary(cx, TOK_IN, JSOP_NOP, pnlet,
|
||||
Expr(cx, ts, tc), tc);
|
||||
if (!pn3)
|
||||
return NULL;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
|
||||
pn2->pn_left = pn3;
|
||||
*pnp = pn2;
|
||||
pnp = &pn2->pn_right;
|
||||
} while (js_MatchToken(cx, ts, TOK_FOR));
|
||||
|
||||
if (js_MatchToken(cx, ts, TOK_IF)) {
|
||||
pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
pn2->pn_kid1 = Condition(cx, ts, tc);
|
||||
if (!pn2->pn_kid1)
|
||||
return NULL;
|
||||
pn2->pn_kid2 = NULL;
|
||||
pn2->pn_kid3 = NULL;
|
||||
*pnp = pn2;
|
||||
pnp = &pn2->pn_kid2;
|
||||
}
|
||||
|
||||
pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
pn2->pn_type = TOK_ARRAYPUSH;
|
||||
pn2->pn_op = JSOP_ARRAYPUSH;
|
||||
pn2->pn_kid = pnexp;
|
||||
*pnp = pn2;
|
||||
PN_APPEND(pn, pntop);
|
||||
|
||||
js_PopStatement(tc);
|
||||
}
|
||||
#endif /* JS_HAS_GENERATORS */
|
||||
|
||||
|
@ -5503,9 +5636,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
if (!pn3)
|
||||
return NULL;
|
||||
pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
|
||||
pn3->pn_expr = NULL;
|
||||
pn3->pn_slot = -1;
|
||||
pn3->pn_attrs = 0;
|
||||
|
||||
/* We have to fake a 'function' token here. */
|
||||
CURRENT_TOKEN(ts).t_op = JSOP_NOP;
|
||||
|
@ -5584,7 +5715,6 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
defsharp = NewParseNode(cx, ts, PN_UNARY, tc);
|
||||
if (!defsharp)
|
||||
return NULL;
|
||||
defsharp->pn_kid = NULL;
|
||||
defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
|
||||
ts->flags |= TSF_OPERAND;
|
||||
tt = js_GetToken(cx, ts);
|
||||
|
@ -5602,12 +5732,17 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
#endif /* JS_HAS_SHARP_VARS */
|
||||
|
||||
case TOK_LP:
|
||||
{
|
||||
JSBool genexp;
|
||||
|
||||
pn = NewParseNode(cx, ts, PN_UNARY, tc);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
pn2 = BracketedExpr(cx, ts, tc);
|
||||
pn2 = ParenExpr(cx, ts, tc, pn, &genexp);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
if (genexp)
|
||||
return pn2;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
|
||||
if (pn2->pn_type == TOK_RP ||
|
||||
|
@ -5625,7 +5760,6 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
* will null the |obj| interpreter register, causing |this| in any
|
||||
* call of that member expression to bind to the global object.
|
||||
*/
|
||||
pn->pn_kid = NULL;
|
||||
RecycleTree(pn, tc);
|
||||
pn = pn2;
|
||||
} else {
|
||||
|
@ -5634,6 +5768,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
pn->pn_kid = pn2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case TOK_STAR:
|
||||
|
@ -5683,9 +5818,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
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)) {
|
||||
|
@ -5790,6 +5923,67 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
return pn;
|
||||
}
|
||||
|
||||
static JSParseNode *
|
||||
ParenExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
JSParseNode *pn1, JSBool *genexp)
|
||||
{
|
||||
JSTokenPtr begin;
|
||||
JSParseNode *pn;
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
uintN oldflags = tc->flags;
|
||||
#endif
|
||||
|
||||
JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LP);
|
||||
begin = CURRENT_TOKEN(ts).pos.begin;
|
||||
|
||||
if (genexp)
|
||||
*genexp = JS_FALSE;
|
||||
pn = BracketedExpr(cx, ts, tc);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
if (js_MatchToken(cx, ts, TOK_FOR)) {
|
||||
if (pn->pn_type == TOK_YIELD) {
|
||||
js_ReportCompileErrorNumber(cx, pn,
|
||||
JSREPORT_PN | JSREPORT_ERROR,
|
||||
JSMSG_BAD_GENERATOR_SYNTAX,
|
||||
js_yield_str);
|
||||
return NULL;
|
||||
}
|
||||
if (pn->pn_type == TOK_COMMA) {
|
||||
js_ReportCompileErrorNumber(cx, PN_LAST(pn),
|
||||
JSREPORT_PN | JSREPORT_ERROR,
|
||||
JSMSG_BAD_GENERATOR_SYNTAX,
|
||||
js_generator_str);
|
||||
return NULL;
|
||||
}
|
||||
if (!pn1) {
|
||||
pn1 = NewParseNode(cx, ts, PN_UNARY, tc);
|
||||
if (!pn1)
|
||||
return NULL;
|
||||
}
|
||||
pn->pn_pos.begin = begin;
|
||||
pn = GeneratorExpr(cx, ts, tc, oldflags, pn1, pn);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
if (genexp) {
|
||||
if (js_GetToken(cx, ts) != TOK_RP) {
|
||||
js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS | JSREPORT_ERROR,
|
||||
JSMSG_BAD_GENERATOR_SYNTAX,
|
||||
js_generator_str);
|
||||
return NULL;
|
||||
}
|
||||
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
|
||||
*genexp = JS_TRUE;
|
||||
}
|
||||
}
|
||||
#endif /* JS_HAS_GENERATOR_EXPRS */
|
||||
|
||||
return pn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fold from one constant type to another.
|
||||
* XXX handles only strings and numbers for now
|
||||
|
@ -6273,6 +6467,12 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
/* Don't fold a trailing |if (0)| in a generator expression. */
|
||||
if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
|
||||
break;
|
||||
#endif
|
||||
|
||||
if (pn2) {
|
||||
/*
|
||||
* pn2 is the then- or else-statement subtree to compile. Take
|
||||
|
|
|
@ -302,6 +302,7 @@ struct JSParseNode {
|
|||
struct { /* one kid if unary */
|
||||
JSParseNode *kid;
|
||||
jsint num; /* -1 or sharp variable number */
|
||||
JSBool hidden; /* hidden genexp-induced JSOP_YIELD */
|
||||
} unary;
|
||||
struct { /* name, labeled statement, etc. */
|
||||
JSAtom *atom; /* name or label atom, null if slot */
|
||||
|
@ -317,7 +318,6 @@ struct JSParseNode {
|
|||
} pn_u;
|
||||
JSParseNode *pn_next; /* to align dval and pn_u on RISCs */
|
||||
JSTokenStream *pn_ts; /* token stream for error reports */
|
||||
JSAtom *pn_source; /* saved source for decompilation */
|
||||
};
|
||||
|
||||
#define pn_funAtom pn_u.func.funAtom
|
||||
|
@ -336,6 +336,7 @@ struct JSParseNode {
|
|||
#define pn_val pn_u.binary.val
|
||||
#define pn_kid pn_u.unary.kid
|
||||
#define pn_num pn_u.unary.num
|
||||
#define pn_hidden pn_u.unary.hidden
|
||||
#define pn_atom pn_u.name.atom
|
||||
#define pn_expr pn_u.name.expr
|
||||
#define pn_slot pn_u.name.slot
|
||||
|
|
Загрузка…
Ссылка в новой задаче