Bug 1101905 - Part 5: Add strict variant of JSOP_SETELEM. (r=Waldo)
authorEric Faust <efaustbmo@gmail.com>
Wed, 26 Nov 2014 14:42:52 -0800
changeset 242112 663083ca3c123f7badd734292aa70f5473c9133e
parent 242111 b1eae4acfc9047857b764eb6bb2896438aab926d
child 242113 5b7b730484e49f890b15236dec156c8bc9c67ffd
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1101905
milestone36.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
Bug 1101905 - Part 5: Add strict variant of JSOP_SETELEM. (r=Waldo)
js/src/frontend/BytecodeEmitter.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/jit/BaselineIC.cpp
js/src/jit/IonBuilder.cpp
js/src/jsopcode.h
js/src/vm/Interpreter.cpp
js/src/vm/Opcodes.h
js/src/vm/Xdr.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2506,17 +2506,18 @@ EmitElemOperands(ExclusiveContext *cx, P
 {
     MOZ_ASSERT(pn->isArity(PN_BINARY));
     if (!EmitTree(cx, bce, pn->pn_left))
         return false;
     if (op == JSOP_CALLELEM && Emit1(cx, bce, JSOP_DUP) < 0)
         return false;
     if (!EmitTree(cx, bce, pn->pn_right))
         return false;
-    if (op == JSOP_SETELEM && Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0)
+    bool isSetElem = op == JSOP_SETELEM || op == JSOP_STRICTSETELEM;
+    if (isSetElem && Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0)
         return false;
     return true;
 }
 
 static inline bool
 EmitElemOpBase(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
 {
     if (Emit1(cx, bce, op) < 0)
@@ -2566,17 +2567,18 @@ EmitElemIncDec(ExclusiveContext *cx, Par
         if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0)   // KEY N N+1 OBJ
             return false;
         if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0)   // N N+1 OBJ KEY
             return false;
         if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0)   // N OBJ KEY N+1
             return false;
     }
 
-    if (!EmitElemOpBase(cx, bce, JSOP_SETELEM))     // N? N+1
+    JSOp setOp = bce->sc->strict ? JSOP_STRICTSETELEM : JSOP_SETELEM;
+    if (!EmitElemOpBase(cx, bce, setOp))     // N? N+1
         return false;
     if (post && Emit1(cx, bce, JSOP_POP) < 0)       // RESULT
         return false;
 
     return true;
 }
 
 static bool
@@ -3413,22 +3415,25 @@ EmitDestructuringLHS(ExclusiveContext *c
                 return false;
             JSOp setOp = bce->sc->strict ? JSOP_STRICTSETPROP : JSOP_SETPROP;
             if (!EmitAtomOp(cx, pn, setOp, bce))
                 return false;
             break;
           }
 
           case PNK_ELEM:
+          {
             // See the comment at `case PNK_DOT:` above. This case,
             // `[a[x]] = [b]`, is handled much the same way. The JSOP_SWAP
             // is emitted by EmitElemOperands.
-            if (!EmitElemOp(cx, pn, JSOP_SETELEM, bce))
+            JSOp setOp = bce->sc->strict ? JSOP_STRICTSETELEM : JSOP_SETELEM;
+            if (!EmitElemOp(cx, pn, setOp, bce))
                 return false;
             break;
+          }
 
           case PNK_CALL:
             MOZ_ASSERT(pn->pn_xflags & PNX_SETCALL);
             if (!EmitTree(cx, bce, pn))
                 return false;
 
             // Pop the call return value. Below, we pop the RHS too, balancing
             // the stack --- presumably for the benefit of bytecode
@@ -4142,19 +4147,22 @@ EmitAssignment(ExclusiveContext *cx, Byt
             return false;
         break;
       }
       case PNK_CALL:
         /* Do nothing. The JSOP_SETCALL we emitted will always throw. */
         MOZ_ASSERT(lhs->pn_xflags & PNX_SETCALL);
         break;
       case PNK_ELEM:
-        if (Emit1(cx, bce, JSOP_SETELEM) < 0)
+      {
+        JSOp setOp = bce->sc->strict ? JSOP_STRICTSETELEM : JSOP_SETELEM;
+        if (Emit1(cx, bce, setOp) < 0)
             return false;
         break;
+      }
       case PNK_ARRAY:
       case PNK_OBJECT:
         if (!EmitDestructuringOps(cx, bce, lhs))
             return false;
         break;
       default:
         MOZ_ASSERT(0);
     }
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1952,16 +1952,22 @@ BaselineCompiler::emit_JSOP_SETELEM()
     // Call IC.
     ICSetElem_Fallback::Compiler stubCompiler(cx);
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
         return false;
 
     return true;
 }
 
+bool
+BaselineCompiler::emit_JSOP_STRICTSETELEM()
+{
+    return emit_JSOP_SETELEM();
+}
+
 typedef bool (*DeleteElementFn)(JSContext *, HandleValue, HandleValue, bool *);
 static const VMFunction DeleteElementStrictInfo = FunctionInfo<DeleteElementFn>(DeleteElement<true>);
 static const VMFunction DeleteElementNonStrictInfo = FunctionInfo<DeleteElementFn>(DeleteElement<false>);
 
 bool
 BaselineCompiler::emit_JSOP_DELELEM()
 {
     // Keep values on the stack for the decompiler.
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -103,16 +103,17 @@ namespace jit {
     _(JSOP_INITELEM_INC)       \
     _(JSOP_MUTATEPROTO)        \
     _(JSOP_INITPROP)           \
     _(JSOP_INITPROP_GETTER)    \
     _(JSOP_INITPROP_SETTER)    \
     _(JSOP_ARRAYPUSH)          \
     _(JSOP_GETELEM)            \
     _(JSOP_SETELEM)            \
+    _(JSOP_STRICTSETELEM)      \
     _(JSOP_CALLELEM)           \
     _(JSOP_DELELEM)            \
     _(JSOP_STRICTDELELEM)      \
     _(JSOP_IN)                 \
     _(JSOP_GETGNAME)           \
     _(JSOP_BINDGNAME)          \
     _(JSOP_SETGNAME)           \
     _(JSOP_STRICTSETGNAME)     \
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -5009,16 +5009,17 @@ DoSetElemFallback(JSContext *cx, Baselin
     DebugModeOSRVolatileStub<ICSetElem_Fallback *> stub(frame, stub_);
 
     RootedScript script(cx, frame->script());
     jsbytecode *pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "SetElem(%s)", js_CodeName[JSOp(*pc)]);
 
     MOZ_ASSERT(op == JSOP_SETELEM ||
+               op == JSOP_STRICTSETELEM ||
                op == JSOP_INITELEM ||
                op == JSOP_INITELEM_ARRAY ||
                op == JSOP_INITELEM_INC);
 
     RootedObject obj(cx, ToObjectFromStack(cx, objv));
     if (!obj)
         return false;
 
@@ -5038,17 +5039,17 @@ DoSetElemFallback(JSContext *cx, Baselin
     } else if (op == JSOP_INITELEM_ARRAY) {
         MOZ_ASSERT(uint32_t(index.toInt32()) == GET_UINT24(pc));
         if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
             return false;
     } else if (op == JSOP_INITELEM_INC) {
         if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
             return false;
     } else {
-        if (!SetObjectElement(cx, obj, index, rhs, script->strict(), script, pc))
+        if (!SetObjectElement(cx, obj, index, rhs, JSOp(*pc) == JSOP_STRICTSETELEM, script, pc))
             return false;
     }
 
     // Overwrite the object on the stack (pushed for the decompiler) with the rhs.
     MOZ_ASSERT(stack[2] == objv);
     stack[2] = rhs;
 
     // Check if debug mode toggling made the stub invalid.
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1733,16 +1733,17 @@ IonBuilder::inspectOpcode(JSOp op)
         // JSOP_LOOPHEAD is handled when processing the loop header.
         MOZ_CRASH("JSOP_LOOPHEAD outside loop");
 
       case JSOP_GETELEM:
       case JSOP_CALLELEM:
         return jsop_getelem();
 
       case JSOP_SETELEM:
+      case JSOP_STRICTSETELEM:
         return jsop_setelem();
 
       case JSOP_LENGTH:
         return jsop_length();
 
       case JSOP_NOT:
         return jsop_not();
 
@@ -8533,17 +8534,18 @@ IonBuilder::setElemTryCache(bool *emitte
 
     // Make sure the object being written to doesn't have copy on write elements.
     object = addMaybeCopyElementsForWrite(object);
 
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(alloc(), object, value));
 
     // Emit SetElementCache.
-    MInstruction *ins = MSetElementCache::New(alloc(), object, index, value, script()->strict(), guardHoles);
+    bool strict = JSOp(*pc) == JSOP_STRICTSETELEM;
+    MInstruction *ins = MSetElementCache::New(alloc(), object, index, value, strict, guardHoles);
     current->add(ins);
     current->push(value);
 
     if (!resumeAfter(ins))
         return false;
 
     *emitted = true;
     return true;
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -655,17 +655,18 @@ IsGetPropPC(jsbytecode *pc)
 }
 
 inline bool
 IsStrictSetPC(jsbytecode *pc)
 {
     JSOp op = JSOp(*pc);
     return op == JSOP_STRICTSETPROP ||
            op == JSOP_STRICTSETNAME ||
-           op == JSOP_STRICTSETGNAME;
+           op == JSOP_STRICTSETGNAME ||
+           op == JSOP_STRICTSETELEM;
 }
 
 inline bool
 IsSetPropPC(jsbytecode *pc)
 {
     JSOp op = JSOp(*pc);
     return op == JSOP_SETPROP || op == JSOP_STRICTSETPROP ||
            op == JSOP_SETNAME || op == JSOP_STRICTSETNAME ||
@@ -678,17 +679,18 @@ IsGetElemPC(jsbytecode *pc)
     JSOp op = JSOp(*pc);
     return op == JSOP_GETELEM || op == JSOP_CALLELEM;
 }
 
 inline bool
 IsSetElemPC(jsbytecode *pc)
 {
     JSOp op = JSOp(*pc);
-    return op == JSOP_SETELEM;
+    return op == JSOP_SETELEM ||
+           op == JSOP_STRICTSETELEM;
 }
 
 inline bool
 IsCallPC(jsbytecode *pc)
 {
     return js_CodeSpec[*pc].format & JOF_INVOKE;
 }
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1588,17 +1588,16 @@ CASE(EnableInterruptsPseudoOpcode)
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_UNUSED2)
 CASE(JSOP_UNUSED50)
 CASE(JSOP_UNUSED51)
 CASE(JSOP_UNUSED52)
-CASE(JSOP_UNUSED57)
 CASE(JSOP_UNUSED83)
 CASE(JSOP_UNUSED92)
 CASE(JSOP_UNUSED103)
 CASE(JSOP_UNUSED104)
 CASE(JSOP_UNUSED105)
 CASE(JSOP_UNUSED107)
 CASE(JSOP_UNUSED124)
 CASE(JSOP_UNUSED125)
@@ -2433,23 +2432,26 @@ CASE(JSOP_CALLELEM)
     }
 
     TypeScript::Monitor(cx, script, REGS.pc, res);
     REGS.sp--;
 }
 END_CASE(JSOP_GETELEM)
 
 CASE(JSOP_SETELEM)
+CASE(JSOP_STRICTSETELEM)
 {
+    static_assert(JSOP_SETELEM_LENGTH == JSOP_STRICTSETELEM_LENGTH,
+                  "setelem and strictsetelem must be the same size");
     RootedObject &obj = rootObject0;
     FETCH_OBJECT(cx, -3, obj);
     RootedId &id = rootId0;
     FETCH_ELEMENT_ID(-2, id);
     Value &value = REGS.sp[-1];
-    if (!SetObjectElementOperation(cx, obj, id, value, script->strict()))
+    if (!SetObjectElementOperation(cx, obj, id, value, *REGS.pc == JSOP_STRICTSETELEM))
         goto error;
     REGS.sp[-3] = value;
     REGS.sp -= 2;
 }
 END_CASE(JSOP_SETELEM)
 
 CASE(JSOP_EVAL)
 {
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -501,18 +501,28 @@ 1234567890123456789012345678901234567890
      * Pops the top three values on the stack as 'val', 'propval' and 'obj',
      * sets 'propval' property of 'obj' as 'val', pushes 'obj' onto the
      * stack.
      *   Category: Literals
      *   Type: Object
      *   Operands:
      *   Stack: obj, propval, val => val
      */ \
-    macro(JSOP_SETELEM,   56, "setelem",    NULL,         1,  3,  1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING) \
-    macro(JSOP_UNUSED57,  57, "unused57",   NULL,         1,  0,  0, JOF_BYTE) \
+    macro(JSOP_SETELEM,   56, "setelem",    NULL,         1,  3,  1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
+    /*
+     * Pops the top three values on the stack as 'val', 'propval' and 'obj',
+     * sets 'propval' property of 'obj' as 'val', pushes 'obj' onto the
+     * stack. Throws a TypeError if the set fails, per strict mode
+     * semantics.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands:
+     *   Stack: obj, propval, val => val
+     */ \
+    macro(JSOP_STRICTSETELEM,   57, "strict-setelem",    NULL,         1,  3,  1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
     /*
      * Invokes 'callee' with 'this' and 'args', pushes return value onto the
      * stack.
      *   Category: Statements
      *   Type: Function
      *   Operands: uint16_t argc
      *   Stack: callee, this, args[0], ..., args[argc-1] => rval
      *   nuses: (argc+2)
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -29,17 +29,17 @@ namespace js {
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  *
  * === GREETINGS, FELLOW SUBTRAHEND INCREMENTER! ===
  * For the time being, please increment the subtrahend by 2 each time it
  * changes, because we have two flavors of bytecode: with JSOP_SYMBOL (in
  * Nightly) and without (all others).  FIXME: Bug 1066322 - Enable ES6 symbols
  * in all builds.
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 214;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 216;
 static_assert(XDR_BYTECODE_VERSION_SUBTRAHEND % 2 == 0, "see the comment above");
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - (XDR_BYTECODE_VERSION_SUBTRAHEND
 #ifdef JS_HAS_SYMBOLS
                                                            + 1
 #endif
                                                               ));