Bug 1234702 - Part 1: Allow opt-in calls to content invoking spread opts in self-hosted code. (r=till)
authorEric Faust <efaustbmo@gmail.com>
Wed, 06 Jan 2016 14:26:14 -0800
changeset 278878 89ba5742703aea86c5d929bc758923feede99876
parent 278877 038f19c1c922c5a704f15228e67d2e03d3885df0
child 278879 e7fac5cfd89a3603e132cbb4279b62f14591e551
push id29860
push usercbook@mozilla.com
push dateThu, 07 Jan 2016 10:51:20 +0000
treeherdermozilla-central@e0bcd16e1d4b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1234702
milestone46.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 1234702 - Part 1: Allow opt-in calls to content invoking spread opts in self-hosted code. (r=till)
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/vm/CommonPropertyNames.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3967,19 +3967,19 @@ BytecodeEmitter::emitDestructuringLHS(Pa
         if (!emit1(JSOP_POP))
             return false;
     }
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitIteratorNext(ParseNode* pn)
-{
-    MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting,
+BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted)
+{
+    MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting,
                ".next() iteration is prohibited in self-hosted code because it "
                "can run user-modifiable iteration code");
 
     if (!emit1(JSOP_DUP))                                 // ... ITER ITER
         return false;
     if (!emitAtomOp(cx->names().next, JSOP_CALLPROP))     // ... ITER NEXT
         return false;
     if (!emit1(JSOP_SWAP))                                // ... NEXT ITER
@@ -5509,17 +5509,17 @@ BytecodeEmitter::emitForInOrOfVariables(
             return false;
     }
     emittingForInit = false;
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn)
+BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn, bool allowSelfHosted)
 {
     MOZ_ASSERT(type == StmtType::FOR_OF_LOOP || type == StmtType::SPREAD);
 #ifdef DEBUG
     if (type == StmtType::FOR_OF_LOOP) {
         MOZ_ASSERT(pn);
         MOZ_ASSERT(pn->pn_left->isKind(PNK_FOROF));
     } else {
         MOZ_ASSERT(!pn);
@@ -5618,17 +5618,17 @@ BytecodeEmitter::emitForOf(StmtType type
         if (!emit1(JSOP_POP))                             // ITER
             return false;
         if (!emit1(JSOP_DUP))                             // ITER ITER
             return false;
     } else {
         if (!emitDupAt(2))                                // ITER ARR I ITER
             return false;
     }
-    if (!emitIteratorNext(forHead))                       // ... RESULT
+    if (!emitIteratorNext(forHead, allowSelfHosted))      // ... RESULT
         return false;
     if (!emit1(JSOP_DUP))                                 // ... RESULT RESULT
         return false;
     if (!emitAtomOp(cx->names().done, JSOP_GETPROP))      // ... RESULT DONE?
         return false;
 
     ptrdiff_t beq;
     if (!emitJump(JSOP_IFEQ, top - offset(), &beq))       // ... RESULT
@@ -7265,16 +7265,28 @@ BytecodeEmitter::emitSelfHostedForceInte
     if (!emit1(JSOP_FORCEINTERPRETER))
         return false;
     if (!emit1(JSOP_UNDEFINED))
         return false;
     return true;
 }
 
 bool
+BytecodeEmitter::emitSelfHostedAllowContentSpread(ParseNode* pn)
+{
+    if (pn->pn_count != 2) {
+        reportError(pn, JSMSG_MORE_ARGS_NEEDED, "allowContentSpread", "1", "");
+        return false;
+    }
+
+    // We're just here as a sentinel. Pass the value through directly.
+    return emitTree(pn->pn_head->pn_next);
+}
+
+bool
 BytecodeEmitter::emitCallOrNew(ParseNode* pn)
 {
     bool callop = pn->isKind(PNK_CALL) || pn->isKind(PNK_TAGGED_TEMPLATE);
     /*
      * Emit callable invocation or operator new (constructor call) code.
      * First, emit code for the left operand to evaluate the callable or
      * constructable object expression.
      *
@@ -7312,16 +7324,18 @@ BytecodeEmitter::emitCallOrNew(ParseNode
                 pn2->name() == cx->names().callContentFunction)
             {
                 return emitSelfHostedCallFunction(pn);
             }
             if (pn2->name() == cx->names().resumeGenerator)
                 return emitSelfHostedResumeGenerator(pn);
             if (pn2->name() == cx->names().forceInterpreter)
                 return emitSelfHostedForceInterpreter(pn);
+            if (pn2->name() == cx->names().allowContentSpread)
+                return emitSelfHostedAllowContentSpread(pn);
             // Fall through.
         }
         if (!emitNameOp(pn2, callop))
             return false;
         break;
       case PNK_DOT:
         if (pn2->as<PropertyAccess>().isSuper()) {
             if (!emitSuperPropOp(pn2, JSOP_GETPROP_SUPER, /* isCall = */ callop))
@@ -7927,19 +7941,19 @@ BytecodeEmitter::emitArrayComp(ParseNode
     if (!emitTree(pn->pn_head))
         return false;
     arrayCompDepth = saveDepth;
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitSpread()
-{
-    return emitForOf(StmtType::SPREAD, nullptr);
+BytecodeEmitter::emitSpread(bool allowSelfHosted)
+{
+    return emitForOf(StmtType::SPREAD, nullptr, allowSelfHosted);
 }
 
 bool
 BytecodeEmitter::emitArrayLiteral(ParseNode* pn)
 {
     if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head) {
         if (checkSingletonContext()) {
             // Bake in the object entirely if it will only be created once.
@@ -8019,32 +8033,46 @@ BytecodeEmitter::emitArray(ParseNode* pn
     for (index = 0; pn2; index++, pn2 = pn2->pn_next) {
         if (!afterSpread && pn2->isKind(PNK_SPREAD)) {
             afterSpread = true;
             if (!emitNumberOp(index))                               // ARRAY INDEX
                 return false;
         }
         if (!updateSourceCoordNotes(pn2->pn_pos.begin))
             return false;
+
+        bool allowSelfHostedSpread = false;
         if (pn2->isKind(PNK_ELISION)) {
             if (!emit1(JSOP_HOLE))
                 return false;
         } else {
-            ParseNode* expr = pn2->isKind(PNK_SPREAD) ? pn2->pn_kid : pn2;
+            ParseNode* expr;
+            if (pn2->isKind(PNK_SPREAD)) {
+                expr = pn2->pn_kid;
+
+                if (emitterMode == BytecodeEmitter::SelfHosting &&
+                    expr->isKind(PNK_CALL) &&
+                    expr->pn_head->name() == cx->names().allowContentSpread)
+                {
+                    allowSelfHostedSpread = true;
+                }
+            } else {
+                expr = pn2;
+            }
             if (!emitTree(expr))                                         // ARRAY INDEX? VALUE
                 return false;
         }
         if (pn2->isKind(PNK_SPREAD)) {
             if (!emitIterator())                                         // ARRAY INDEX ITER
                 return false;
             if (!emit2(JSOP_PICK, 2))                                    // INDEX ITER ARRAY
                 return false;
             if (!emit2(JSOP_PICK, 2))                                    // ITER ARRAY INDEX
                 return false;
-            if (!emitSpread())                                           // ARRAY INDEX
+            if (!emitSpread(allowSelfHostedSpread))                      // ARRAY INDEX
                 return false;
         } else if (afterSpread) {
             if (!emit1(JSOP_INITELEM_INC))
                 return false;
         } else {
             if (!emitUint32Operand(JSOP_INITELEM_ARRAY, index))
                 return false;
         }
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -572,17 +572,17 @@ struct BytecodeEmitter
     bool emitRequireObjectCoercible();
 
     // emitIterator expects the iterable to already be on the stack.
     // It will replace that stack value with the corresponding iterator
     bool emitIterator();
 
     // Pops iterator from the top of the stack. Pushes the result of |.next()|
     // onto the stack.
-    bool emitIteratorNext(ParseNode* pn);
+    bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false);
 
     // Check if the value on top of the stack is "undefined". If so, replace
     // that value on the stack with the value defined by |defaultExpr|.
     bool emitDefault(ParseNode* defaultExpr);
 
     bool emitCallSiteObject(ParseNode* pn);
     bool emitTemplateString(ParseNode* pn);
     bool emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs);
@@ -609,16 +609,17 @@ struct BytecodeEmitter
 
     bool emitConditionalExpression(ConditionalExpression& conditional);
 
     bool emitCallOrNew(ParseNode* pn);
     bool emitDebugOnlyCheckSelfHosted();
     bool emitSelfHostedCallFunction(ParseNode* pn);
     bool emitSelfHostedResumeGenerator(ParseNode* pn);
     bool emitSelfHostedForceInterpreter(ParseNode* pn);
+    bool emitSelfHostedAllowContentSpread(ParseNode* pn);
 
     bool emitComprehensionFor(ParseNode* compFor);
     bool emitComprehensionForIn(ParseNode* pn);
     bool emitComprehensionForInOrOfVariables(ParseNode* pn, bool* letBlockScope);
     bool emitComprehensionForOf(ParseNode* pn);
 
     bool emitDo(ParseNode* pn);
     bool emitFor(ParseNode* pn);
@@ -638,27 +639,27 @@ struct BytecodeEmitter
     bool initializeBlockScopedLocalsFromStack(Handle<StaticBlockObject*> blockObj);
 
     // emitSpread expects the current index (I) of the array, the array itself
     // and the iterator to be on the stack in that order (iterator on the bottom).
     // It will pop the iterator and I, then iterate over the iterator by calling
     // |.next()| and put the results into the I-th element of array with
     // incrementing I, then push the result I (it will be original I +
     // iteration count). The stack after iteration will look like |ARRAY INDEX|.
-    bool emitSpread();
+    bool emitSpread(bool allowSelfHosted = false);
 
     // If type is StmtType::FOR_OF_LOOP, emit bytecode for a for-of loop.
     // pn should be PNK_FOR, and pn->pn_left should be PNK_FOROF.
     //
     // If type is StmtType::SPREAD, emit bytecode for spread operator.
     // pn should be nullptr.
     //
     // Please refer the comment above emitSpread for additional information about
     // stack convention.
-    bool emitForOf(StmtType type, ParseNode* pn);
+    bool emitForOf(StmtType type, ParseNode* pn, bool allowSelfHosted = false);
 
     bool emitClass(ParseNode* pn);
     bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false);
     bool emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall = false);
     bool emitSuperElemOperands(ParseNode* pn, EmitElemOption opts = EmitElemOption::Get);
     bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false);
 };
 
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -8,16 +8,17 @@
 
 #ifndef vm_CommonPropertyNames_h
 #define vm_CommonPropertyNames_h
 
 #include "jsprototypes.h"
 
 #define FOR_EACH_COMMON_PROPERTYNAME(macro) \
     macro(add, add, "add") \
+    macro(allowContentSpread, allowContentSpread, "allowContentSpread") \
     macro(anonymous, anonymous, "anonymous") \
     macro(Any, Any, "Any") \
     macro(apply, apply, "apply") \
     macro(arguments, arguments, "arguments") \
     macro(as, as, "as") \
     macro(ArrayIteratorNext, ArrayIteratorNext, "ArrayIteratorNext") \
     macro(ArrayType, ArrayType, "ArrayType") \
     macro(ArrayValues, ArrayValues, "ArrayValues") \