Support generator expressions for JS1.8 (380237, r=mrbkap).

This commit is contained in:
brendan@mozilla.org 2007-05-17 18:41:17 -07:00
Родитель 58a40748d5
Коммит 48e1521edc
9 изменённых файлов: 675 добавлений и 264 удалений

Просмотреть файл

@ -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)

Просмотреть файл

@ -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