diff --git a/js/src/jsapi.c b/js/src/jsapi.c index 53b6f8e23888..7e5d911cbdbd 100644 --- a/js/src/jsapi.c +++ b/js/src/jsapi.c @@ -2933,43 +2933,41 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, const char *bytes, size_t length) { jschar *chars; - JSScript *script; - void *mark; + JSBool result; + JSExceptionState *exnState; + void *tempMark; JSTokenStream *ts; JSErrorReporter older; - JSBool hitEOF, result; - JSExceptionState *exnState; CHECK_REQUEST(cx); - mark = JS_ARENA_MARK(&cx->tempPool); chars = js_InflateString(cx, bytes, length); if (!chars) return JS_TRUE; + + /* + * Return true on any out-of-memory error, so our caller doesn't try to + * collect more buffered source. + */ + result = JS_TRUE; exnState = JS_SaveExceptionState(cx); + tempMark = JS_ARENA_MARK(&cx->tempPool); ts = js_NewTokenStream(cx, chars, length, NULL, 0, NULL); - if (!ts) { - result = JS_TRUE; - goto out; + if (ts) { + older = JS_SetErrorReporter(cx, NULL); + if (!js_ParseTokenStream(cx, obj, ts)) { + /* + * We ran into an error. If it was because we ran out of source, + * we return false, so our caller will know to try to collect more + * buffered source. + */ + result = (ts->flags & TSF_EOF) == 0; + } + + JS_SetErrorReporter(cx, older); + js_CloseTokenStream(cx, ts); + JS_ARENA_RELEASE(&cx->tempPool, tempMark); } - older = JS_SetErrorReporter(cx, NULL); - script = CompileTokenStream(cx, obj, ts, mark, &hitEOF); - JS_SetErrorReporter(cx, older); - - if (script == NULL) { - /* - * We ran into an error, but it was because we ran out of source, - * and not for some other reason. For this case (and this case - * only) we return false, so the calling function knows to try to - * collect more source. - */ - result = hitEOF ? JS_FALSE : JS_TRUE; - } else { - result = JS_TRUE; - js_DestroyScript(cx, script); - } - -out: JS_free(cx, chars); JS_RestoreExceptionState(cx, exnState); return result; diff --git a/js/src/jsemit.c b/js/src/jsemit.c index 83797e328d55..d5899674641b 100644 --- a/js/src/jsemit.c +++ b/js/src/jsemit.c @@ -3373,13 +3373,27 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) case TOK_MINUS: case TOK_STAR: case TOK_DIVOP: - /* Binary operators that evaluate both operands unconditionally. */ - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - if (js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; + if (pn->pn_arity == PN_LIST) { + /* Left-associative operator chain: avoid too much recursion. */ + pn2 = pn->pn_head; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + op = pn->pn_op; + while ((pn2 = pn2->pn_next) != NULL) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + } + } else { + /* Binary operators that evaluate both operands unconditionally. */ + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + if (js_Emit1(cx, cg, pn->pn_op) < 0) + return JS_FALSE; + } break; #if JS_HAS_EXCEPTIONS diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 27af23409a75..eef8b19e6118 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -85,6 +85,7 @@ typedef enum JSOp { #define JOF_ASSIGNING 0x2000 /* hint for JSClass.resolve, used for ops that do simplex assignment */ #define JOF_BACKPATCH 0x4000 /* backpatch placeholder during codegen */ +#define JOF_LEFTASSOC 0x8000 /* left-associative operator */ #define JOF_TYPE_IS_EXTENDED_JUMP(t) \ ((unsigned)((t) - JOF_JUMPX) <= (unsigned)(JOF_LOOKUPSWITCHX - JOF_JUMPX)) diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index b02d79dcb012..85e8c5c8675d 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -85,23 +85,23 @@ OPDEF(JSOP_FORVAR, 11, "forvar", NULL, 3, 0, 1, 0, JOF_QVAR|J OPDEF(JSOP_DUP, 12, "dup", NULL, 1, 1, 2, 0, JOF_BYTE) OPDEF(JSOP_DUP2, 13, "dup2", NULL, 1, 2, 4, 0, JOF_BYTE) OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 1, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 2, JOF_BYTE) -OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 3, JOF_BYTE) -OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 4, JOF_BYTE) -OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 5, JOF_BYTE) -OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 5, JOF_BYTE) -OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 6, JOF_BYTE) -OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 6, JOF_BYTE) -OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 6, JOF_BYTE) -OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 6, JOF_BYTE) -OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 7, JOF_BYTE) -OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 7, JOF_BYTE) -OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 7, JOF_BYTE) -OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 8, JOF_BYTE) -OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 8, JOF_BYTE) -OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 9, JOF_BYTE) -OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 9, JOF_BYTE) -OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 9, JOF_BYTE) +OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 2, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 3, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 4, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 10, JOF_BYTE) OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 10, JOF_BYTE) OPDEF(JSOP_NEG, 34, "neg", "-", 1, 1, 1, 10, JOF_BYTE) @@ -213,8 +213,8 @@ OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 1, JOF_CONST| OPDEF(JSOP_THROW, 110,"throw", NULL, 1, 1, 0, 0, JOF_BYTE) /* 'in' and 'instanceof' ops. */ -OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 6, JOF_BYTE) -OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,6,JOF_BYTE) +OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,6,JOF_BYTE|JOF_LEFTASSOC) /* debugger op */ OPDEF(JSOP_DEBUGGER, 113,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE) diff --git a/js/src/jsparse.c b/js/src/jsparse.c index 02817b857f1e..93ca4b4dddff 100644 --- a/js/src/jsparse.c +++ b/js/src/jsparse.c @@ -311,8 +311,6 @@ js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts) JSTreeContext tc; JSParseNode *pn; - JS_ASSERT(cx->runtime->gcDisabled); - /* * Push a compiler frame if we have no frames, or if the top frame is a * lightweight function activation, or if its scope chain doesn't match @@ -330,6 +328,13 @@ js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts) cx->fp = &frame; } + /* + * Prevent GC activation (possible if out of memory when atomizing, + * or from pre-ECMAv2 switch case expr eval in the unlikely case of a + * branch-callback -- unlikely because it means the switch case must + * have called a function). + */ + JS_DISABLE_GC(cx->runtime); TREE_CONTEXT_INIT(&tc); pn = Statements(cx, ts, &tc); if (pn) { @@ -345,6 +350,7 @@ js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts) } TREE_CONTEXT_FINISH(&tc); + JS_ENABLE_GC(cx->runtime); cx->fp = fp; return pn; } @@ -380,12 +386,7 @@ js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, cx->fp = &frame; } - /* - * Prevent GC activation (possible if out of memory when atomizing, - * or from pre-ECMAv2 switch case expr eval in the unlikely case of a - * branch-callback -- unlikely because it means the switch case must - * have called a function). - */ + /* Prevent GC activation while compiling. */ JS_DISABLE_GC(cx->runtime); pn = Statements(cx, ts, &cg->treeContext); @@ -3258,5 +3259,31 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) default:; } + /* + * Flatten a left-associative (left-heavy) tree of a given operator into + * a list, to reduce js_EmitTree recursion. + */ + if (pn->pn_arity == PN_BINARY && + pn1 && + pn1->pn_type == pn->pn_type && + pn1->pn_op == pn->pn_op && + (js_CodeSpec[pn->pn_op].format & JOF_LEFTASSOC)) { + if (pn1->pn_arity == PN_LIST) { + /* We already flattened pn1, so move it to pn and append pn2. */ + PN_MOVE_NODE(pn, pn1); + PN_APPEND(pn, pn2); + } else { + /* Convert pn into a list containing pn1's kids followed by pn2. */ + pn->pn_arity = PN_LIST; + PN_INIT_LIST_1(pn, pn1->pn_left); + PN_APPEND(pn, pn1->pn_right); + PN_APPEND(pn, pn2); + + /* Clear pn1 so it can be recycled by itself, without its kids. */ + PN_CLEAR_NODE(pn1); + } + RecycleTree(pn1, tc); + } + return JS_TRUE; } diff --git a/js/src/jsparse.h b/js/src/jsparse.h index 7712b5ab8611..1e551fc069d6 100644 --- a/js/src/jsparse.h +++ b/js/src/jsparse.h @@ -249,9 +249,14 @@ struct JSParseNode { (pn)->pn_op = (pn2)->pn_op; \ (pn)->pn_arity = (pn2)->pn_arity; \ (pn)->pn_u = (pn2)->pn_u; \ - (pn2)->pn_type = TOK_EOF; \ - (pn2)->pn_op = JSOP_NOP; \ - (pn2)->pn_arity = PN_NULLARY; \ + PN_CLEAR_NODE(pn2); \ + JS_END_MACRO + +#define PN_CLEAR_NODE(pn) \ + JS_BEGIN_MACRO \ + (pn)->pn_type = TOK_EOF; \ + (pn)->pn_op = JSOP_NOP; \ + (pn)->pn_arity = PN_NULLARY; \ JS_END_MACRO /* True if pn is a parsenode representing a literal constant. */