Bug 1169743 - Rework JSOP_CLASSHERITAGE to be jit-friendly. r=shu
authorTed Campbell <tcampbell@mozilla.com>
Tue, 23 May 2017 16:23:53 -0400
changeset 413236 bc64157bced9005d257e6b39d5189e6f2180e647
parent 413235 76d4bd75e8e2d2e11b987ffca7ffd2f702dea742
child 413237 c1ae34baf77ac2a747acc7a3b65a388847c12f8f
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1169743
milestone55.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 1169743 - Rework JSOP_CLASSHERITAGE to be jit-friendly. r=shu Initial patch by Eric Faust [:efaust]. MozReview-Commit-ID: 52I0IY0cMJn
js/src/frontend/BytecodeEmitter.cpp
js/src/js.msg
js/src/jsopcode.cpp
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.h
js/src/vm/Opcodes.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -10660,43 +10660,120 @@ BytecodeEmitter::emitClass(ParseNode* pn
     Maybe<EmitterScope> emitterScope;
     if (names) {
         tdzCache.emplace(this);
         emitterScope.emplace(this);
         if (!emitterScope->enterLexical(this, ScopeKind::Lexical, classNode.scopeBindings()))
             return false;
     }
 
+    // Pseudocode for class declarations:
+    //
+    //     class extends BaseExpression {
+    //       constructor() { ... }
+    //       ...
+    //       }
+    //
+    //
+    //   if defined <BaseExpression> {
+    //     let heritage = BaseExpression;
+    //
+    //     if (heritage !== null) {
+    //       funProto = heritage;
+    //       objProto = heritage.prototype;
+    //     } else {
+    //       funProto = %FunctionPrototype%;
+    //       objProto = null;
+    //     }
+    //   } else {
+    //     objProto = %ObjectPrototype%;
+    //   }
+    //
+    //   let homeObject = ObjectCreate(objProto);
+    //
+    //   if defined <constructor> {
+    //     if defined <BaseExpression> {
+    //       cons = DefineMethod(<constructor>, proto=homeObject, funProto=funProto);
+    //     } else {
+    //       cons = DefineMethod(<constructor>, proto=homeObject);
+    //     }
+    //   } else {
+    //     if defined <BaseExpression> {
+    //       cons = DefaultDerivedConstructor(proto=homeObject, funProto=funProto);
+    //     } else {
+    //       cons = DefaultConstructor(proto=homeObject);
+    //     }
+    //   }
+    //
+    //   cons.prototype = homeObject;
+    //   homeObject.constructor = cons;
+    //
+    //   EmitPropertyList(...)
+
     // This is kind of silly. In order to the get the home object defined on
     // the constructor, we have to make it second, but we want the prototype
     // on top for EmitPropertyList, because we expect static properties to be
     // rarer. The result is a few more swaps than we would like. Such is life.
     if (heritageExpression) {
-        if (!emitTree(heritageExpression))
-            return false;
-        if (!emit1(JSOP_CLASSHERITAGE))
-            return false;
-        if (!emit1(JSOP_OBJWITHPROTO))
-            return false;
-
-        // JSOP_CLASSHERITAGE leaves both protos on the stack. After
-        // creating the prototype, swap it to the bottom to make the
-        // constructor.
-        if (!emit1(JSOP_SWAP))
+        IfThenElseEmitter ifThenElse(this);
+
+        if (!emitTree(heritageExpression))                      // ... HERITAGE
+            return false;
+
+        // Heritage must be null or a non-generator constructor
+        if (!emit1(JSOP_CHECKCLASSHERITAGE))                    // ... HERITAGE
+            return false;
+
+        // [IF] (heritage !== null)
+        if (!emit1(JSOP_DUP))                                   // ... HERITAGE HERITAGE
+            return false;
+        if (!emit1(JSOP_NULL))                                  // ... HERITAGE HERITAGE NULL
+            return false;
+        if (!emit1(JSOP_STRICTNE))                              // ... HERITAGE NE
+            return false;
+
+        // [THEN] funProto = heritage, objProto = heritage.prototype
+        if (!ifThenElse.emitIfElse())
+            return false;
+        if (!emit1(JSOP_DUP))                                   // ... HERITAGE HERITAGE
+            return false;
+        if (!emitAtomOp(cx->names().prototype, JSOP_GETPROP))   // ... HERITAGE PROTO
+            return false;
+
+        // [ELSE] funProto = %FunctionPrototype%, objProto = null
+        if (!ifThenElse.emitElse())
+            return false;
+        if (!emit1(JSOP_POP))                                   // ...
+            return false;
+        if (!emit2(JSOP_BUILTINPROTO, JSProto_Function))        // ... PROTO
+            return false;
+        if (!emit1(JSOP_NULL))                                  // ... PROTO NULL
+            return false;
+
+        // [ENDIF]
+        if (!ifThenElse.emitEnd())
+            return false;
+
+        if (!emit1(JSOP_OBJWITHPROTO))                          // ... HERITAGE HOMEOBJ
+            return false;
+        if (!emit1(JSOP_SWAP))                                  // ... HOMEOBJ HERITAGE
             return false;
     } else {
-        if (!emitNewInit(JSProto_Object))
-            return false;
-    }
+        if (!emitNewInit(JSProto_Object))                       // ... HOMEOBJ
+            return false;
+    }
+
+    // Stack currently has HOMEOBJ followed by optional HERITAGE. When HERITAGE
+    // is not used, an implicit value of %FunctionPrototype% is implied.
 
     if (constructor) {
-        if (!emitFunction(constructor, !!heritageExpression))
+        if (!emitFunction(constructor, !!heritageExpression))   // ... HOMEOBJ CONSTRUCTOR
             return false;
         if (constructor->pn_funbox->needsHomeObject()) {
-            if (!emit2(JSOP_INITHOMEOBJECT, 0))
+            if (!emit2(JSOP_INITHOMEOBJECT, 0))                 // ... HOMEOBJ CONSTRUCTOR
                 return false;
         }
     } else {
         // In the case of default class constructors, emit the start and end
         // offsets in the source buffer as source notes so that when we
         // actually make the constructor during execution, we can give it the
         // correct toString output.
         //
@@ -10707,61 +10784,63 @@ BytecodeEmitter::emitClass(ParseNode* pn
                                tokenStream().options().sourceStartColumn;
         ptrdiff_t classEnd = ptrdiff_t(pn->pn_pos.end) -
                              tokenStream().options().sourceStartColumn;
         if (!newSrcNote3(SRC_CLASS_SPAN, classStart, classEnd))
             return false;
 
         JSAtom *name = names ? names->innerBinding()->pn_atom : cx->names().empty;
         if (heritageExpression) {
-            if (!emitAtomOp(name, JSOP_DERIVEDCONSTRUCTOR))
+            if (!emitAtomOp(name, JSOP_DERIVEDCONSTRUCTOR))     // ... HOMEOBJ CONSTRUCTOR
                 return false;
         } else {
-            if (!emitAtomOp(name, JSOP_CLASSCONSTRUCTOR))
-                return false;
-        }
-    }
-
-    if (!emit1(JSOP_SWAP))
-        return false;
-
-    if (!emit1(JSOP_DUP2))
-        return false;
-    if (!emitAtomOp(cx->names().prototype, JSOP_INITLOCKEDPROP))
-        return false;
-    if (!emitAtomOp(cx->names().constructor, JSOP_INITHIDDENPROP))
+            if (!emitAtomOp(name, JSOP_CLASSCONSTRUCTOR))       // ... HOMEOBJ CONSTRUCTOR
+                return false;
+        }
+    }
+
+    if (!emit1(JSOP_SWAP))                                      // ... CONSTRUCTOR HOMEOBJ
+        return false;
+
+    if (!emit1(JSOP_DUP2))                                          // ... CONSTRUCTOR HOMEOBJ CONSTRUCTOR HOMEOBJ
+        return false;
+    if (!emitAtomOp(cx->names().prototype, JSOP_INITLOCKEDPROP))    // ... CONSTRUCTOR HOMEOBJ CONSTRUCTOR
+        return false;
+    if (!emitAtomOp(cx->names().constructor, JSOP_INITHIDDENPROP))  // ... CONSTRUCTOR HOMEOBJ
         return false;
 
     RootedPlainObject obj(cx);
-    if (!emitPropertyList(classMethods, &obj, ClassBody))
-        return false;
-
-    if (!emit1(JSOP_POP))
+    if (!emitPropertyList(classMethods, &obj, ClassBody))       // ... CONSTRUCTOR HOMEOBJ
+        return false;
+
+    if (!emit1(JSOP_POP))                                       // ... CONSTRUCTOR
         return false;
 
     if (names) {
         ParseNode* innerName = names->innerBinding();
-        if (!emitLexicalInitialization(innerName))
+        if (!emitLexicalInitialization(innerName))              // ... CONSTRUCTOR
             return false;
 
         // Pop the inner scope.
         if (!emitterScope->leave(this))
             return false;
         emitterScope.reset();
 
         ParseNode* outerName = names->outerBinding();
         if (outerName) {
-            if (!emitLexicalInitialization(outerName))
+            if (!emitLexicalInitialization(outerName))          // ... CONSTRUCTOR
                 return false;
             // Only class statements make outer bindings, and they do not leave
             // themselves on the stack.
-            if (!emit1(JSOP_POP))
-                return false;
-        }
-    }
+            if (!emit1(JSOP_POP))                               // ...
+                return false;
+        }
+    }
+
+    // The CONSTRUCTOR is left on stack if this is an expression.
 
     MOZ_ALWAYS_TRUE(sc->setLocalStrictMode(savedStrictness));
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */,
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -97,21 +97,22 @@ MSG_DEF(JSMSG_NOT_ITERATOR,            1
 MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA,      2, JSEXN_WARN, "{0} is being assigned a {1}, but already has one")
 MSG_DEF(JSMSG_GET_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.iterator]() returned a non-object value")
 MSG_DEF(JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, 1, JSEXN_TYPEERR, "iterator.{0}() returned a non-object value")
 MSG_DEF(JSMSG_CANT_SET_PROTO,          0, JSEXN_TYPEERR, "can't set prototype of this object")
 MSG_DEF(JSMSG_CANT_SET_PROTO_OF,       1, JSEXN_TYPEERR, "can't set prototype of {0}")
 MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE,    0, JSEXN_TYPEERR, "can't set prototype: it would cause a prototype chain cycle")
 MSG_DEF(JSMSG_INVALID_ARG_TYPE,        3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
 MSG_DEF(JSMSG_TERMINATED,              1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
-MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL,     1, JSEXN_TYPEERR, "{0}.prototype is not an object or null")
 MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
 MSG_DEF(JSMSG_UNINITIALIZED_THIS,      1, JSEXN_REFERENCEERR, "|this| used uninitialized in {0} class constructor")
 MSG_DEF(JSMSG_UNINITIALIZED_THIS_ARROW, 0, JSEXN_REFERENCEERR, "|this| used uninitialized in arrow function in class constructor")
 MSG_DEF(JSMSG_BAD_DERIVED_RETURN,      1, JSEXN_TYPEERR, "derived class constructor returned invalid value {0}")
+MSG_DEF(JSMSG_BAD_HERITAGE,            2, JSEXN_TYPEERR, "class heritage {0} is {1}")
+MSG_DEF(JSMSG_NOT_OBJORNULL,           1, JSEXN_TYPEERR, "{0} is not an object or null")
 
 // JSON
 MSG_DEF(JSMSG_JSON_BAD_PARSE,          3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
 MSG_DEF(JSMSG_JSON_CYCLIC_VALUE,       0, JSEXN_TYPEERR, "cyclic object value")
 
 // Runtime errors
 MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS,      1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}")
 MSG_DEF(JSMSG_BAD_LEFTSIDE_OF_ASS,     0, JSEXN_REFERENCEERR, "invalid assignment left-hand side")
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -724,16 +724,17 @@ BytecodeParser::simulateOp(JSOp op, uint
       }
 
       case JSOP_AND:
       case JSOP_CHECKISOBJ:
       case JSOP_CHECKISCALLABLE:
       case JSOP_CHECKOBJCOERCIBLE:
       case JSOP_CHECKTHIS:
       case JSOP_CHECKTHISREINIT:
+      case JSOP_CHECKCLASSHERITAGE:
       case JSOP_DEBUGCHECKSELFHOSTED:
       case JSOP_INITGLEXICAL:
       case JSOP_INITLEXICAL:
       case JSOP_OR:
       case JSOP_SETALIASEDVAR:
       case JSOP_SETARG:
       case JSOP_SETINTRINSIC:
       case JSOP_SETLOCAL:
@@ -1965,22 +1966,16 @@ ExpressionDecompiler::decompilePC(jsbyte
 
           case JSOP_CALLSITEOBJ:
             return write("OBJ");
 
           case JSOP_CLASSCONSTRUCTOR:
           case JSOP_DERIVEDCONSTRUCTOR:
             return write("CONSTRUCTOR");
 
-          case JSOP_CLASSHERITAGE:
-            if (defIndex == 0)
-                return write("FUNCPROTO");
-            MOZ_ASSERT(defIndex == 1);
-            return write("OBJPROTO");
-
           case JSOP_DOUBLE:
             return sprinter.printf("%lf", script->getConst(GET_UINT32_INDEX(pc)).toDouble());
 
           case JSOP_EXCEPTION:
             return write("EXCEPTION");
 
           case JSOP_FINALLY:
             if (defIndex == 0)
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -966,16 +966,53 @@ js::TypeOfValue(const Value& v)
     if (v.isObject())
         return TypeOfObject(&v.toObject());
     if (v.isBoolean())
         return JSTYPE_BOOLEAN;
     MOZ_ASSERT(v.isSymbol());
     return JSTYPE_SYMBOL;
 }
 
+bool
+js::CheckClassHeritageOperation(JSContext* cx, HandleValue heritage)
+{
+    if (IsConstructor(heritage))
+        return true;
+
+    if (heritage.isNull())
+        return true;
+
+    if (heritage.isObject()) {
+        ReportIsNotFunction(cx, heritage, 0, CONSTRUCT);
+        return false;
+    }
+
+    ReportValueError2(cx, JSMSG_BAD_HERITAGE, -1, heritage, nullptr, "not an object or null");
+    return false;
+}
+
+JSObject*
+js::ObjectWithProtoOperation(JSContext* cx, HandleValue val)
+{
+    if (!val.isObjectOrNull()) {
+        ReportValueError(cx, JSMSG_NOT_OBJORNULL, -1, val, nullptr);
+        return nullptr;
+    }
+
+    RootedObject proto(cx, val.toObjectOrNull());
+    return NewObjectWithGivenProto<PlainObject>(cx, proto);
+}
+
+JSObject*
+js::FunWithProtoOperation(JSContext* cx, HandleFunction fun, HandleObject parent,
+                          HandleObject proto)
+{
+    return CloneFunctionObjectIfNotSingleton(cx, fun, parent, proto);
+}
+
 /*
  * Enter the new with environment using an object at sp[-1] and associate the
  * depth of the with block with sp + stackIndex.
  */
 bool
 js::EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val,
                        Handle<WithScope*> scope)
 {
@@ -1937,17 +1974,16 @@ CASE(EnableInterruptsPseudoOpcode)
     SANITY_CHECKS();
     DISPATCH_TO(op);
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_NOP_DESTRUCTURING)
 CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE)
-CASE(JSOP_UNUSED221)
 CASE(JSOP_UNUSED222)
 CASE(JSOP_UNUSED223)
 CASE(JSOP_CONDSWITCH)
 {
     MOZ_ASSERT(CodeSpec[*REGS.pc].length == 1);
     ADVANCE_AND_DISPATCH(1);
 }
 
@@ -4094,69 +4130,54 @@ CASE(JSOP_ARRAYPUSH)
 {
     ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-1].toObject());
     if (!NewbornArrayPush(cx, obj, REGS.sp[-2]))
         goto error;
     REGS.sp -= 2;
 }
 END_CASE(JSOP_ARRAYPUSH)
 
-CASE(JSOP_CLASSHERITAGE)
-{
-    ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
-
-    ReservedRooted<Value> objProto(&rootValue1);
-    ReservedRooted<JSObject*> funcProto(&rootObject0);
-    if (val.isNull()) {
-        objProto = NullValue();
-        if (!GetBuiltinPrototype(cx, JSProto_Function, &funcProto))
-            goto error;
-    } else {
-        if (!IsConstructor(val)) {
-            ReportIsNotFunction(cx, val, 0, CONSTRUCT);
-            goto error;
-        }
-
-        funcProto = &val.toObject();
-
-        if (!GetProperty(cx, funcProto, funcProto, cx->names().prototype, &objProto))
-            goto error;
-
-        if (!objProto.isObjectOrNull()) {
-            ReportValueError(cx, JSMSG_PROTO_NOT_OBJORNULL, -1, objProto, nullptr);
-            goto error;
-        }
-    }
-
-    REGS.sp[-1].setObject(*funcProto);
-    PUSH_COPY(objProto);
-}
-END_CASE(JSOP_CLASSHERITAGE)
+CASE(JSOP_CHECKCLASSHERITAGE)
+{
+    HandleValue heritage = REGS.stackHandleAt(-1);
+
+    if (!CheckClassHeritageOperation(cx, heritage))
+        goto error;
+}
+END_CASE(JSOP_CHECKCLASSHERITAGE)
+
+CASE(JSOP_BUILTINPROTO)
+{
+    ReservedRooted<JSObject*> builtin(&rootObject0);
+    MOZ_ASSERT(GET_UINT8(REGS.pc) < JSProto_LIMIT);
+    JSProtoKey key = static_cast<JSProtoKey>(GET_UINT8(REGS.pc));
+    if (!GetBuiltinPrototype(cx, key, &builtin))
+        goto error;
+    PUSH_OBJECT(*builtin);
+}
+END_CASE(JSOP_BUILTINPROTO)
 
 CASE(JSOP_FUNWITHPROTO)
 {
     ReservedRooted<JSObject*> proto(&rootObject1, &REGS.sp[-1].toObject());
 
     /* Load the specified function object literal. */
     ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
 
-    JSObject* obj = CloneFunctionObjectIfNotSingleton(cx, fun, REGS.fp()->environmentChain(),
-                                                      proto, GenericObject);
+    JSObject* obj = FunWithProtoOperation(cx, fun, REGS.fp()->environmentChain(), proto);
     if (!obj)
         goto error;
 
     REGS.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_FUNWITHPROTO)
 
 CASE(JSOP_OBJWITHPROTO)
 {
-    ReservedRooted<JSObject*> proto(&rootObject0, REGS.sp[-1].toObjectOrNull());
-
-    JSObject* obj = NewObjectWithGivenProto<PlainObject>(cx, proto);
+    JSObject* obj = ObjectWithProtoOperation(cx, REGS.stackHandleAt(-1));
     if (!obj)
         goto error;
 
     REGS.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_OBJWITHPROTO)
 
 CASE(JSOP_INITHOMEOBJECT)
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -572,11 +572,20 @@ bool
 ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame);
 
 bool
 DefaultClassConstructor(JSContext* cx, unsigned argc, Value* vp);
 
 bool
 Debug_CheckSelfHosted(JSContext* cx, HandleValue v);
 
+bool
+CheckClassHeritageOperation(JSContext* cx, HandleValue heritage);
+
+JSObject*
+ObjectWithProtoOperation(JSContext* cx, HandleValue proto);
+
+JSObject*
+FunWithProtoOperation(JSContext* cx, HandleFunction fun, HandleObject parent, HandleObject proto);
+
 }  /* namespace js */
 
 #endif /* vm_Interpreter_h */
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -486,24 +486,23 @@ 1234567890123456789012345678901234567890
      * with 'this' and 'args', and pushes return value onto the stack.
      *   Category: Statements
      *   Type: Function
      *   Operands:
      *   Stack: callee, this, args => rval
      */ \
     macro(JSOP_STRICTSPREADEVAL,      50, "strict-spreadeval", NULL,         1,  3,  1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET|JOF_CHECKSTRICT) \
     /*
-     * Writes the [[Prototype]] objects for both a class and its .prototype to
-     * the stack, given the result of a heritage expression.
+     * Ensures the result of a class's heritage expression is either null or a constructor.
      *   Category: Literals
      *   Type: Object
      *   Operands:
-     *   Stack: heritage => funcProto, objProto
+     *   Stack: heritage => heritage
      */ \
-    macro(JSOP_CLASSHERITAGE,  51, "classheritage",   NULL,         1,  1,  2,  JOF_BYTE) \
+    macro(JSOP_CHECKCLASSHERITAGE,  51, "checkclassheritage",   NULL, 1,  1,  1,  JOF_BYTE) \
     /*
      * Pushes a clone of a function with a given [[Prototype]] onto the stack.
      *   Category: Statements
      *   Type: Function
      *   Operands: uint32_t funcIndex
      *   Stack: proto => obj
      */ \
     macro(JSOP_FUNWITHPROTO,   52, "funwithproto",    NULL,         5,  1,  1,  JOF_OBJECT) \
@@ -2256,17 +2255,25 @@ 1234567890123456789012345678901234567890
      * No-op used by the exception unwinder to determine the correct
      * environment to unwind to when performing IteratorClose due to
      * destructuring.
      *   Category: Other
      *   Operands:
      *   Stack: =>
      */ \
     macro(JSOP_TRY_DESTRUCTURING_ITERCLOSE, 220, "try-destructuring-iterclose", NULL, 1, 0, 0, JOF_BYTE) \
-    macro(JSOP_UNUSED221,     221,"unused221",     NULL,  1,  0,  0,  JOF_BYTE) \
+    \
+    /*
+     * Pushes the current global's builtin prototype for a given proto key
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands: uint8_t kind
+     *   Stack: => %BuiltinPrototype%
+     */ \
+    macro(JSOP_BUILTINPROTO, 221, "builtinproto", NULL, 2,  0,  1,  JOF_UINT8) \
     macro(JSOP_UNUSED222,     222,"unused222",     NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED223,     223,"unused223",     NULL,  1,  0,  0,  JOF_BYTE) \
     \
     /*
      * Creates rest parameter array for current function call, and pushes it
      * onto the stack.
      *   Category: Variables and Scopes
      *   Type: Arguments