author | Benjamin Peterson <benjamin@python.org> |
Wed, 06 Jun 2012 21:53:07 -0500 | |
changeset 96029 | 34476c720f8fa8624cf2100d1e6fa5aae16d301e |
parent 96028 | ce0c716baefdd91fa93d4c3b5b36b2d9f81f0440 |
child 96030 | 21ff29da4c41c86bc573d31a983557688c6d13f3 |
push id | 22869 |
push user | [email protected] |
push date | Thu, 07 Jun 2012 09:35:19 +0000 |
treeherder | mozilla-central@3933384d8315 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jorendorff |
bugs | 574130 |
milestone | 16.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3718,16 +3718,18 @@ ParseNode::getConstantValue(JSContext *c vp->setBoolean(true); return true; case PNK_FALSE: vp->setBoolean(false); return true; case PNK_NULL: vp->setNull(); return true; + case PNK_SPREAD: + return false; case PNK_RB: { JS_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST)); RootedObject obj(cx, NewDenseAllocatedArray(cx, pn_count)); if (!obj) return false; unsigned idx = 0; @@ -5801,39 +5803,61 @@ EmitArray(JSContext *cx, BytecodeEmitter /* Emit the usual op needed for decompilation. */ return Emit1(cx, bce, JSOP_ENDINIT) >= 0; } #endif /* JS_HAS_GENERATORS */ if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && bce->checkSingletonContext()) return EmitSingletonInitialiser(cx, bce, pn); + int32_t nspread = 0; + for (ParseNode *elt = pn->pn_head; elt; elt = elt->pn_next) { + if (elt->isKind(PNK_SPREAD)) + nspread++; + } + ptrdiff_t off = EmitN(cx, bce, JSOP_NEWARRAY, 3); if (off < 0) return false; CheckTypeSet(cx, bce, JSOP_NEWARRAY); jsbytecode *pc = bce->code(off); - SET_UINT24(pc, pn->pn_count); + + // For arrays with spread, this is a very pessimistic allocation, the + // minimum possible final size. + SET_UINT24(pc, pn->pn_count - nspread); ParseNode *pn2 = pn->pn_head; jsatomid atomIndex; + if (nspread && !EmitNumberOp(cx, 0, bce)) + return false; for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) { - if (!EmitNumberOp(cx, atomIndex, bce)) + if (!nspread && !EmitNumberOp(cx, atomIndex, bce)) return false; if (pn2->isKind(PNK_COMMA) && pn2->isArity(PN_NULLARY)) { if (Emit1(cx, bce, JSOP_HOLE) < 0) return false; } else { - if (!EmitTree(cx, bce, pn2)) + ParseNode *expr = pn2->isKind(PNK_SPREAD) ? pn2->pn_kid : pn2; + if (!EmitTree(cx, bce, expr)) return false; } - if (Emit1(cx, bce, JSOP_INITELEM) < 0) - return false; + if (pn2->isKind(PNK_SPREAD)) { + if (Emit1(cx, bce, JSOP_SPREAD) < 0) + return false; + } else if (Emit1(cx, bce, nspread ? JSOP_INITELEM_INC : JSOP_INITELEM) < 0) { + return false; + } } JS_ASSERT(atomIndex == pn->pn_count); + if (nspread) { + if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0) + return false; + if (Emit1(cx, bce, JSOP_POP) < 0) + return false; + } if (pn->pn_xflags & PNX_ENDCOMMA) { /* Emit a source note so we know to decompile an extra comma. */ if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0) return false; } /* Emit an op to finish the array and aid in decompilation. */
--- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -159,16 +159,17 @@ enum ParseNodeKind { PNK_ARRAYPUSH, PNK_LEXICALSCOPE, PNK_LET, PNK_SEQ, PNK_FORIN, PNK_FORHEAD, PNK_ARGSBODY, PNK_UPVARS, + PNK_SPREAD, /* * The following parse node kinds occupy contiguous ranges to enable easy * range-testing. */ /* Equality operators. */ PNK_STRICTEQ, @@ -235,16 +236,17 @@ enum ParseNodeKind { * PNK_STATEMENTLIST node for function body * statements as final element * pn_count: 1 + number of formal parameters * PNK_UPVARS nameset pn_names: lexical dependencies (js::Definitions) * defined in enclosing scopes, or ultimately not * defined (free variables, either global property * references or reference errors). * pn_tree: PNK_ARGSBODY or PNK_STATEMENTLIST node + * PNK_SPREAD unary pn_kid: expression being spread * * <Statements> * PNK_STATEMENTLIST list pn_head: list of pn_count statements * PNK_IF ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else or null. * In body of a comprehension or desugared generator * expression, pn_kid2 is PNK_YIELD, PNK_ARRAYPUSH, * or (if the push was optimized away) empty * PNK_STATEMENTLIST.
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -6651,16 +6651,17 @@ Parser::primaryExpr(TokenKind tt, bool a pn->makeEmpty(); #if JS_HAS_GENERATORS pn->pn_blockid = tc->sc->blockidGen; #endif matched = tokenStream.matchToken(TOK_RB, TSF_OPERAND); if (!matched) { + bool spread = false; for (index = 0; ; index++) { if (index == StackSpace::ARGS_LENGTH_MAX) { reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ARRAY_INIT_TOO_BIG); return NULL; } tt = tokenStream.peekToken(TSF_OPERAND); if (tt == TOK_RB) { @@ -6669,22 +6670,34 @@ Parser::primaryExpr(TokenKind tt, bool a } if (tt == TOK_COMMA) { /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */ tokenStream.matchToken(TOK_COMMA); pn2 = NullaryNode::create(PNK_COMMA, this); pn->pn_xflags |= PNX_HOLEY | PNX_NONCONST; } else { + ParseNode *spreadNode = NULL; + if (tt == TOK_TRIPLEDOT) { + spread = true; + spreadNode = UnaryNode::create(PNK_SPREAD, this); + if (!spreadNode) + return NULL; + tokenStream.getToken(); + } pn2 = assignExpr(); if (pn2) { if (foldConstants && !FoldConstants(context, pn2, this)) return NULL; - if (!pn2->isConstant()) + if (!pn2->isConstant() || spreadNode) pn->pn_xflags |= PNX_NONCONST; + if (spreadNode) { + spreadNode->pn_kid = pn2; + pn2 = spreadNode; + } } } if (!pn2) return NULL; pn->append(pn2); if (tt != TOK_COMMA) { /* If we didn't already match TOK_COMMA in above case. */ @@ -6727,20 +6740,20 @@ Parser::primaryExpr(TokenKind tt, bool a * * Each var declaration in a let-block binds a name in <o> at * compile time, and allocates a slot on the operand stack at * runtime via JSOP_ENTERBLOCK. A block-local var is accessed by * the JSOP_GETLOCAL and JSOP_SETLOCAL ops. These ops have an * immediate operand, the local slot's stack index from fp->spbase. * * The array comprehension iteration step, array.push(i * j) in - * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>, + * the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>, * where <array> is the index of array's stack slot. */ - if (index == 0 && pn->pn_count != 0 && tokenStream.matchToken(TOK_FOR)) { + if (index == 0 && !spread && pn->pn_count != 0 && tokenStream.matchToken(TOK_FOR)) { ParseNode *pnexp, *pntop; /* Relabel pn as an array comprehension node. */ pn->setKind(PNK_ARRAYCOMP); /* * Remove the comprehension expression from pn's linked list * and save it via pnexp. We'll re-install it underneath the
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/spread-array-decompile.js @@ -0,0 +1,14 @@ +var samples = [ + "[...a]", + "[...[1]]", + "[1, ...a, 2]", + "[1, ...[2, 3], 4]", + "[...[1], , ]", + "[1, , ...[2]]", + "[, 1, ...[2], ...[3], , 4, 5, , ]" +]; +for (var sample of samples) { + var source = "function f() {\n return " + sample + ";\n}"; + eval(source); + assertEq(f.toString(), source); +}
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/spread-array-evaluation-order.js @@ -0,0 +1,12 @@ +load(libdir + "eqArrayHelper.js"); + +var check = []; +function t(token) { + check.push(token); + return token; +} +[3, ...[t(1)],, ...[t(2), t(3)], 34, 42, ...[t(4)]]; +assertEqArray(check, [1, 2, 3, 4]); + +var arr = [1, 2, 3]; +assertEqArray([...arr, arr.pop()], [1, 2, 3, 3]);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/spread-array-invalid-syntax.js @@ -0,0 +1,14 @@ +load(libdir + "asserts.js"); + +var offenders = [ + "(1 ... n)", + "[1 ... n]", + "(...x)", + "[...x for (x of y)]", + "[...]", + "(...)", + "[...,]" +]; +for (var sample of offenders) { + assertThrowsInstanceOf(function () { eval(sample); }, SyntaxError); +}
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/spread-array-wrap.js @@ -0,0 +1,4 @@ +load(libdir + "eqArrayHelper.js"); + +assertEqArray([...wrap([1])], [1]); +assertEqArray([1,, ...wrap([2, 3, 4]), 5, ...wrap([6])], [1,, 2, 3, 4, 5, 6]);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/spread-array.js @@ -0,0 +1,15 @@ +load(libdir + "eqArrayHelper.js"); + +assertEqArray([...[1, 2, 3]], [1, 2, 3]); +assertEqArray([1, ...[2, 3, 4], 5], [1, 2, 3, 4, 5]); +assertEqArray([1, ...[], 2], [1, 2]); +assertEqArray([1, ...[2, 3], 4, ...[5, 6]], [1, 2, 3, 4, 5, 6]); +assertEqArray([1, ...[], 2], [1, 2]); +assertEqArray([1,, ...[2]], [1,, 2]); +assertEqArray([1,, ...[2],, 3,, 4,], [1,, 2,, 3,, 4,]); + +// According to the draft spec, null and undefined are to be treated as empty +// arrays. However, they are not iterable. If the spec is not changed to be in +// terms of iterables, these tests should be fixed. +//assertEqArray([1, ...null, 2], [1, 2]); +//assertEqArray([1, ...undefined, 2], [1, 2]);
--- a/js/src/js.msg +++ b/js/src/js.msg @@ -110,17 +110,17 @@ MSG_DEF(JSMSG_TRAILING_SLASH, 5 MSG_DEF(JSMSG_BAD_CLASS_RANGE, 57, 0, JSEXN_SYNTAXERR, "invalid range in character class") MSG_DEF(JSMSG_BAD_REGEXP_FLAG, 58, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}") MSG_DEF(JSMSG_NO_INPUT, 59, 5, JSEXN_SYNTAXERR, "no input for /{0}/{1}{2}{3}{4}") MSG_DEF(JSMSG_CANT_OPEN, 60, 2, JSEXN_ERR, "can't open {0}: {1}") MSG_DEF(JSMSG_TOO_MANY_FUN_APPLY_ARGS, 61, 0, JSEXN_RANGEERR, "arguments array passed to Function.prototype.apply is too large") MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 62, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression") MSG_DEF(JSMSG_TOO_BIG_TO_ENCODE, 63, 0, JSEXN_INTERNALERR, "data are to big to encode") MSG_DEF(JSMSG_ARG_INDEX_OUT_OF_RANGE, 64, 1, JSEXN_RANGEERR, "argument {0} accesses an index that is out of range") -MSG_DEF(JSMSG_UNUSED65, 65, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_SPREAD_TOO_LARGE, 65, 0, JSEXN_RANGEERR, "array too large due to spread operand(s)") MSG_DEF(JSMSG_UNUSED66, 66, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_UNUSED67, 67, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_BAD_SCRIPT_MAGIC, 68, 0, JSEXN_INTERNALERR, "bad script XDR magic number") MSG_DEF(JSMSG_PAREN_BEFORE_FORMAL, 69, 0, JSEXN_SYNTAXERR, "missing ( before formal parameters") MSG_DEF(JSMSG_MISSING_FORMAL, 70, 0, JSEXN_SYNTAXERR, "missing formal parameter") MSG_DEF(JSMSG_PAREN_AFTER_FORMAL, 71, 0, JSEXN_SYNTAXERR, "missing ) after formal parameters") MSG_DEF(JSMSG_CURLY_BEFORE_BODY, 72, 0, JSEXN_SYNTAXERR, "missing { before function body") MSG_DEF(JSMSG_CURLY_AFTER_BODY, 73, 0, JSEXN_SYNTAXERR, "missing } after function body")
--- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -1462,16 +1462,21 @@ ScriptAnalysis::analyzeSSA(JSContext *cx case JSOP_MOREITER: stack[stackDepth - 2].v = code->poppedValues[0]; break; case JSOP_INITPROP: stack[stackDepth - 1].v = code->poppedValues[1]; break; + case JSOP_SPREAD: + case JSOP_INITELEM_INC: + stack[stackDepth - 2].v = code->poppedValues[2]; + break; + case JSOP_INITELEM: stack[stackDepth - 1].v = code->poppedValues[2]; break; case JSOP_DUP: stack[stackDepth - 1].v = stack[stackDepth - 2].v = code->poppedValues[0]; break;
--- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -3824,17 +3824,19 @@ ScriptAnalysis::analyzeTypesBytecode(JSC types->addType(cx, Type::UnknownType()); } break; } case JSOP_ENDINIT: break; - case JSOP_INITELEM: { + case JSOP_INITELEM: + case JSOP_INITELEM_INC: + case JSOP_SPREAD: { const SSAValue &objv = poppedValue(pc, 2); jsbytecode *initpc = script->code + objv.pushedOffset(); TypeObject *initializer = GetInitializerType(cx, script, initpc); if (initializer) { pushed[0].addType(cx, Type::ObjectType(initializer)); if (!initializer->unknownProperties()) { /* @@ -3845,23 +3847,34 @@ ScriptAnalysis::analyzeTypesBytecode(JSC TypeSet *types = initializer->getProperty(cx, JSID_VOID, true); if (!types) return false; if (state.hasGetSet) { types->addType(cx, Type::UnknownType()); } else if (state.hasHole) { if (!initializer->unknownProperties()) initializer->setFlags(cx, OBJECT_FLAG_NON_PACKED_ARRAY); + } else if (op == JSOP_SPREAD) { + // Iterator could put arbitrary things into the array. + types->addType(cx, Type::UnknownType()); } else { poppedTypes(pc, 0)->addSubset(cx, types); } } } else { pushed[0].addType(cx, Type::UnknownType()); } + switch (op) { + case JSOP_SPREAD: + case JSOP_INITELEM_INC: + poppedTypes(pc, 1)->addSubset(cx, &pushed[1]); + break; + default: + break; + } state.hasGetSet = false; state.hasHole = false; break; } case JSOP_GETTER: case JSOP_SETTER: state.hasGetSet = true;
--- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1014,16 +1014,37 @@ TypeCheckNextBytecode(JSContext *cx, JSS #ifdef DEBUG if (cx->typeInferenceEnabled() && n == GetBytecodeLength(regs.pc)) { TypeScript::CheckBytecode(cx, script, regs.pc, regs.sp); } #endif } +class SpreadContext { +public: + JSContext *cx; + RootedObject arr; + int32_t *count; + SpreadContext(JSContext *cx, JSObject *array, int32_t *count) + : cx(cx), arr(cx, array), count(count) { + JS_ASSERT(array->isArray()); + } + SpreadContext(SpreadContext &scx) + : cx(cx), arr(scx.cx, scx.arr), count(scx.count) {} + bool operator ()(JSContext *cx, const Value &item) { + if (*count == INT32_MAX) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_SPREAD_TOO_LARGE); + return false; + } + return arr->defineElement(cx, (*count)++, item, NULL, NULL, JSPROP_ENUMERATE); + } +}; + JS_NEVER_INLINE bool js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) { JSAutoResolveFlags rf(cx, RESOLVE_INFER); gc::MaybeVerifyBarriers(cx, true); JS_ASSERT(!cx->compartment->activeAnalysis); @@ -1433,27 +1454,25 @@ js::Interpret(JSContext *cx, StackFrame switchMask = moreInterrupts ? -1 : 0; switchOp = int(op); goto do_switch; #endif } /* No-ops for ease of decompilation. */ ADD_EMPTY_CASE(JSOP_NOP) -ADD_EMPTY_CASE(JSOP_UNUSED0) ADD_EMPTY_CASE(JSOP_UNUSED1) ADD_EMPTY_CASE(JSOP_UNUSED2) ADD_EMPTY_CASE(JSOP_UNUSED3) ADD_EMPTY_CASE(JSOP_UNUSED8) ADD_EMPTY_CASE(JSOP_UNUSED9) ADD_EMPTY_CASE(JSOP_UNUSED10) ADD_EMPTY_CASE(JSOP_UNUSED11) ADD_EMPTY_CASE(JSOP_UNUSED12) ADD_EMPTY_CASE(JSOP_UNUSED13) -ADD_EMPTY_CASE(JSOP_UNUSED14) ADD_EMPTY_CASE(JSOP_UNUSED15) ADD_EMPTY_CASE(JSOP_UNUSED17) ADD_EMPTY_CASE(JSOP_UNUSED18) ADD_EMPTY_CASE(JSOP_UNUSED19) ADD_EMPTY_CASE(JSOP_UNUSED20) ADD_EMPTY_CASE(JSOP_UNUSED21) ADD_EMPTY_CASE(JSOP_UNUSED22) ADD_EMPTY_CASE(JSOP_UNUSED23) @@ -3151,16 +3170,17 @@ BEGIN_CASE(JSOP_INITPROP) JSPROP_ENUMERATE, 0, 0, 0)) { goto error; } regs.sp--; } END_CASE(JSOP_INITPROP); +BEGIN_CASE(JSOP_INITELEM_INC) BEGIN_CASE(JSOP_INITELEM) { /* Pop the element's value into rval. */ JS_ASSERT(regs.stackDepth() >= 3); const Value &rref = regs.sp[-1]; RootedObject &obj = rootObject0; @@ -3185,20 +3205,43 @@ BEGIN_CASE(JSOP_INITELEM) if (JSOp(regs.pc[JSOP_INITELEM_LENGTH]) == JSOP_ENDINIT && !js_SetLengthProperty(cx, obj, (uint32_t) (JSID_TO_INT(id) + 1))) { goto error; } } else { if (!obj->defineGeneric(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE)) goto error; } - regs.sp -= 2; + if (op == JSOP_INITELEM_INC) { + JS_ASSERT(obj->isArray()); + if (JSID_TO_INT(id) == INT32_MAX) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_SPREAD_TOO_LARGE); + return false; + } + regs.sp[-2].setInt32(JSID_TO_INT(id) + 1); + regs.sp--; + } else { + regs.sp -= 2; + } } END_CASE(JSOP_INITELEM) +BEGIN_CASE(JSOP_SPREAD) +{ + int32_t count = regs.sp[-2].toInt32(); + SpreadContext scx(cx, ®s.sp[-3].toObject(), &count); + const Value iterable = regs.sp[-1]; + if (!ForOf(cx, iterable, scx)) + goto error; + regs.sp[-2].setInt32(count); + regs.sp--; +} +END_CASE(JSOP_SPREAD) + { BEGIN_CASE(JSOP_GOSUB) PUSH_BOOLEAN(false); int32_t i = (regs.pc - script->code) + JSOP_GOSUB_LENGTH; len = GET_JUMP_OFFSET(regs.pc); PUSH_INT32(i); END_VARLEN_CASE }
--- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -881,17 +881,17 @@ js::ValueToIterator(JSContext *cx, unsig } else { /* * Enumerating over null and undefined gives an empty enumerator. * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of * the first production in 12.6.4 and step 4 of the second production, * but it's "web JS" compatible. ES5 fixed for-in to match this de-facto * standard. */ - if ((flags & JSITER_ENUMERATE)) { + if (flags & JSITER_ENUMERATE) { if (!js_ValueToObjectOrNull(cx, *vp, obj.address())) return false; /* fall through */ } else { obj = js_ValueToNonNullObject(cx, *vp); if (!obj) return false; }
--- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -5102,35 +5102,49 @@ Decompile(SprintStack *ss, jsbytecode *p (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "", inArray ? ']' : '}'); break; } { JSBool isFirst; const char *maybeComma; + const char *maybeSpread; case JSOP_INITELEM: + case JSOP_INITELEM_INC: + case JSOP_SPREAD: + JS_ASSERT(ss->top >= 3); isFirst = IsInitializerOp(ss->opcodes[ss->top - 3]); /* Turn off most parens. */ rval = PopStr(ss, JSOP_SETNAME, &rvalpc); /* Turn off all parens for xval and lval, which we control. */ - xval = PopStr(ss, JSOP_NOP); + xval = PopStr(ss, JSOP_NOP, &xvalpc); lval = PopStr(ss, JSOP_NOP, &lvalpc); sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_INITPROP) { atom = NULL; goto do_initprop; } maybeComma = isFirst ? "" : ", "; - todo = Sprint(&ss->sprinter, "%s%s", lval, maybeComma); + maybeSpread = op == JSOP_SPREAD ? "..." : ""; + todo = Sprint(&ss->sprinter, "%s%s%s", lval, maybeComma, maybeSpread); SprintOpcode(ss, rval, rvalpc, pc, todo); + if (op != JSOP_INITELEM && todo != -1) { + if (!UpdateDecompiledText(ss, pushpc, todo)) + return NULL; + if (!PushOff(ss, todo, saveop, pushpc)) + return NULL; + if (!PushStr(ss, "", JSOP_NOP)) + return NULL; + todo = -2; + } break; case JSOP_INITPROP: LOAD_ATOM(0); xval = QuoteString(&ss->sprinter, atom, jschar(IsIdentifier(atom) ? 0 : '\'')); if (!xval) return NULL; isFirst = IsInitializerOp(ss->opcodes[ss->top - 2]);
--- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -194,17 +194,17 @@ OPDEF(JSOP_FUNAPPLY, 79, "funapply", OPDEF(JSOP_OBJECT, 80, "object", NULL, 5, 0, 1, 19, JOF_OBJECT) /* Pop value and discard it. */ OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE) /* Call a function as a constructor; operand is argc. */ OPDEF(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) -OPDEF(JSOP_UNUSED0, 83, "unused0", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_SPREAD, 83, "spread", NULL, 1, 3, 2, 3, JOF_BYTE|JOF_ELEM|JOF_SET) /* Fast get/set ops for function arguments and local variables. */ OPDEF(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME) OPDEF(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, 3, JOF_QARG |JOF_NAME|JOF_SET) OPDEF(JSOP_GETLOCAL, 86,"getlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME) OPDEF(JSOP_SETLOCAL, 87,"setlocal", NULL, 3, 1, 1, 3, JOF_LOCAL|JOF_NAME|JOF_SET|JOF_DETECTING) /* Push unsigned 16-bit int constant. */ @@ -219,17 +219,17 @@ OPDEF(JSOP_UINT16, 88, "uint16", * NEWINIT has an extra byte so it can be exchanged with NEWOBJECT during emit. */ OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 5, 0, 1, 19, JOF_UINT8|JOF_TYPESET) OPDEF(JSOP_NEWARRAY, 90, "newarray", NULL, 4, 0, 1, 19, JOF_UINT24|JOF_TYPESET) OPDEF(JSOP_NEWOBJECT, 91, "newobject", NULL, 5, 0, 1, 19, JOF_OBJECT|JOF_TYPESET) OPDEF(JSOP_ENDINIT, 92, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE) OPDEF(JSOP_INITPROP, 93, "initprop", NULL, 5, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) OPDEF(JSOP_INITELEM, 94, "initelem", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) -OPDEF(JSOP_UNUSED14, 95, "unused14", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_INITELEM_INC,95, "initelem_inc", NULL, 1, 3, 2, 3, JOF_BYTE|JOF_ELEM|JOF_SET) OPDEF(JSOP_UNUSED15, 96, "unused15", NULL, 1, 0, 0, 0, JOF_BYTE) /* Fast inc/dec ops for args and locals. */ OPDEF(JSOP_INCARG, 97, "incarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC|JOF_TMPSLOT3) OPDEF(JSOP_DECARG, 98, "decarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC|JOF_TMPSLOT3) OPDEF(JSOP_ARGINC, 99, "arginc", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3) OPDEF(JSOP_ARGDEC, 100, "argdec", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3)