Attachment #8767830: (Plan B) Part 1: Throw if the value returned by iterator.next() is not an object. for bug #1016936

View | Details | Raw Unified | Return to bug 1016936
Collapse All | Expand All

(-)a/js/src/builtin/Utilities.js (+5 lines)
Line     Link Here 
 Lines 90-105   function ToNumber(v) { Link Here 
90
90
91
91
92
// ES6 7.2.1 (previously, ES5 9.10 under the name "CheckObjectCoercible").
92
// ES6 7.2.1 (previously, ES5 9.10 under the name "CheckObjectCoercible").
93
function RequireObjectCoercible(v) {
93
function RequireObjectCoercible(v) {
94
    if (v === undefined || v === null)
94
    if (v === undefined || v === null)
95
        ThrowTypeError(JSMSG_CANT_CONVERT_TO, ToString(v), "object");
95
        ThrowTypeError(JSMSG_CANT_CONVERT_TO, ToString(v), "object");
96
}
96
}
97
97
98
function RequireIsObjectIteratorNext(v) {
99
    if (!IsObject(v))
100
        ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE);
101
}
102
98
/* Spec: ECMAScript Draft, 6 edition May 22, 2014, 7.1.15 */
103
/* Spec: ECMAScript Draft, 6 edition May 22, 2014, 7.1.15 */
99
function ToLength(v) {
104
function ToLength(v) {
100
    v = ToInteger(v);
105
    v = ToInteger(v);
101
106
102
    if (v <= 0)
107
    if (v <= 0)
103
        return 0;
108
        return 0;
104
109
105
    // Math.pow(2, 53) - 1 = 0x1fffffffffffff
110
    // Math.pow(2, 53) - 1 = 0x1fffffffffffff
(-)a/js/src/frontend/BytecodeEmitter.cpp (-14 / +27 lines)
Line     Link Here 
 Lines 4142-4157   BytecodeEmitter::emitIteratorNext(ParseN Link Here 
4142
    if (!emit1(JSOP_DUP))                                 // ... ITER ITER
4142
    if (!emit1(JSOP_DUP))                                 // ... ITER ITER
4143
        return false;
4143
        return false;
4144
    if (!emitAtomOp(cx->names().next, JSOP_CALLPROP))     // ... ITER NEXT
4144
    if (!emitAtomOp(cx->names().next, JSOP_CALLPROP))     // ... ITER NEXT
4145
        return false;
4145
        return false;
4146
    if (!emit1(JSOP_SWAP))                                // ... NEXT ITER
4146
    if (!emit1(JSOP_SWAP))                                // ... NEXT ITER
4147
        return false;
4147
        return false;
4148
    if (!emitCall(JSOP_CALL, 0, pn))                      // ... RESULT
4148
    if (!emitCall(JSOP_CALL, 0, pn))                      // ... RESULT
4149
        return false;
4149
        return false;
4150
    if (!emitRequireIsObjectIteratorNext())               // ... RESULT
4151
        return false;
4150
    checkTypeSet(JSOP_CALL);
4152
    checkTypeSet(JSOP_CALL);
4151
    return true;
4153
    return true;
4152
}
4154
}
4153
4155
4154
bool
4156
bool
4155
BytecodeEmitter::emitDefault(ParseNode* defaultExpr)
4157
BytecodeEmitter::emitDefault(ParseNode* defaultExpr)
4156
{
4158
{
4157
    if (!emit1(JSOP_DUP))                                 // VALUE VALUE
4159
    if (!emit1(JSOP_DUP))                                 // VALUE VALUE
 Lines 5662-5713   BytecodeEmitter::emitWith(ParseNode* pn) Link Here 
5662
    if (!emitTree(pn->pn_right))
5664
    if (!emitTree(pn->pn_right))
5663
        return false;
5665
        return false;
5664
    if (!leaveNestedScope(&stmtInfo))
5666
    if (!leaveNestedScope(&stmtInfo))
5665
        return false;
5667
        return false;
5666
    return true;
5668
    return true;
5667
}
5669
}
5668
5670
5669
bool
5671
bool
5670
BytecodeEmitter::emitRequireObjectCoercible()
5672
BytecodeEmitter::emitCallSelfHosted1AfterDup(JSAtom* func)
5671
{
5673
{
5672
    // For simplicity, handle this in self-hosted code, at cost of 13 bytes of
5673
    // bytecode versus 1 byte for a dedicated opcode.  As more places need this
5674
    // behavior, we may want to reconsider this tradeoff.
5675
5676
#ifdef DEBUG
5674
#ifdef DEBUG
5677
    auto depth = this->stackDepth;
5675
    auto depth = this->stackDepth;
5678
#endif
5676
#endif
5679
    MOZ_ASSERT(depth > 0);                 // VAL
5677
    MOZ_ASSERT(depth > 0);                 // VAL
5680
    if (!emit1(JSOP_DUP))                  // VAL VAL
5678
    if (!emit1(JSOP_DUP))                  // VAL VAL
5681
        return false;
5679
        return false;
5682
5680
5683
    // Note that "intrinsic" is a misnomer: we're calling a *self-hosted*
5681
    // Note that "intrinsic" is a misnomer: we're calling a *self-hosted*
5684
    // function that's not an intrinsic!  But it nonetheless works as desired.
5682
    // function that's not an intrinsic!  But it nonetheless works as desired.
5685
    if (!emitAtomOp(cx->names().RequireObjectCoercible,
5683
    if (!emitAtomOp(func, JSOP_GETINTRINSIC)) // VAL VAL FUNC
5686
                    JSOP_GETINTRINSIC))    // VAL VAL REQUIREOBJECTCOERCIBLE
5684
        return false;
5687
    {
5685
    if (!emit1(JSOP_UNDEFINED))            // VAL VAL FUNC UNDEFINED
5688
        return false;
5686
        return false;
5689
    }
5687
    if (!emit2(JSOP_PICK, 2))              // VAL FUNC UNDEFINED VAL
5690
    if (!emit1(JSOP_UNDEFINED))            // VAL VAL REQUIREOBJECTCOERCIBLE UNDEFINED
5691
        return false;
5692
    if (!emit2(JSOP_PICK, 2))              // VAL REQUIREOBJECTCOERCIBLE UNDEFINED VAL
5693
        return false;
5688
        return false;
5694
    if (!emitCall(JSOP_CALL, 1))           // VAL IGNORED
5689
    if (!emitCall(JSOP_CALL, 1))           // VAL IGNORED
5695
        return false;
5690
        return false;
5696
    checkTypeSet(JSOP_CALL);
5691
    checkTypeSet(JSOP_CALL);
5697
5692
5698
    if (!emit1(JSOP_POP))                  // VAL
5693
    if (!emit1(JSOP_POP))                  // VAL
5699
        return false;
5694
        return false;
5700
5695
5701
    MOZ_ASSERT(depth == this->stackDepth);
5696
    MOZ_ASSERT(depth == this->stackDepth);
5702
    return true;
5697
    return true;
5703
}
5698
}
5704
5699
5705
bool
5700
bool
5701
BytecodeEmitter::emitRequireObjectCoercible()
5702
{
5703
    // For simplicity, handle this in self-hosted code, at cost of 13 bytes of
5704
    // bytecode versus 1 byte for a dedicated opcode.  As more places need this
5705
    // behavior, we may want to reconsider this tradeoff.
5706
5707
    return emitCallSelfHosted1AfterDup(cx->names().RequireObjectCoercible);
5708
}
5709
5710
bool
5711
BytecodeEmitter::emitRequireIsObjectIteratorNext()
5712
{
5713
    return emitCallSelfHosted1AfterDup(cx->names().RequireIsObjectIteratorNext);
5714
}
5715
5716
bool
5706
BytecodeEmitter::emitIterator()
5717
BytecodeEmitter::emitIterator()
5707
{
5718
{
5708
    // Convert iterable to iterator.
5719
    // Convert iterable to iterator.
5709
    if (!emit1(JSOP_DUP))                                         // OBJ OBJ
5720
    if (!emit1(JSOP_DUP))                                         // OBJ OBJ
5710
        return false;
5721
        return false;
5711
    if (!emit2(JSOP_SYMBOL, uint8_t(JS::SymbolCode::iterator)))   // OBJ OBJ @@ITERATOR
5722
    if (!emit2(JSOP_SYMBOL, uint8_t(JS::SymbolCode::iterator)))   // OBJ OBJ @@ITERATOR
5712
        return false;
5723
        return false;
5713
    if (!emitElemOpBase(JSOP_CALLELEM))                           // OBJ ITERFN
5724
    if (!emitElemOpBase(JSOP_CALLELEM))                           // OBJ ITERFN
 Lines 7228-7243   BytecodeEmitter::emitYieldStar(ParseNode Link Here 
7228
    if (!emitAtomOp(cx->names().next, JSOP_CALLPROP))            // RECEIVED ITER ITER NEXT
7239
    if (!emitAtomOp(cx->names().next, JSOP_CALLPROP))            // RECEIVED ITER ITER NEXT
7229
        return false;
7240
        return false;
7230
    if (!emit1(JSOP_SWAP))                                       // RECEIVED ITER NEXT ITER
7241
    if (!emit1(JSOP_SWAP))                                       // RECEIVED ITER NEXT ITER
7231
        return false;
7242
        return false;
7232
    if (!emit2(JSOP_PICK, 3))                                    // ITER NEXT ITER RECEIVED
7243
    if (!emit2(JSOP_PICK, 3))                                    // ITER NEXT ITER RECEIVED
7233
        return false;
7244
        return false;
7234
    if (!emitCall(JSOP_CALL, 1, iter))                           // ITER RESULT
7245
    if (!emitCall(JSOP_CALL, 1, iter))                           // ITER RESULT
7235
        return false;
7246
        return false;
7247
    if (!emitRequireIsObjectIteratorNext())                      // ITER RESULT
7248
        return false;
7236
    checkTypeSet(JSOP_CALL);
7249
    checkTypeSet(JSOP_CALL);
7237
    MOZ_ASSERT(this->stackDepth == depth);
7250
    MOZ_ASSERT(this->stackDepth == depth);
7238
7251
7239
    if (!emitJumpTargetAndPatch(checkResult))                    // checkResult:
7252
    if (!emitJumpTargetAndPatch(checkResult))                    // checkResult:
7240
        return false;
7253
        return false;
7241
7254
7242
    // if (!result.done) goto tryStart;                          // ITER RESULT
7255
    // if (!result.done) goto tryStart;                          // ITER RESULT
7243
    if (!emit1(JSOP_DUP))                                        // ITER RESULT RESULT
7256
    if (!emit1(JSOP_DUP))                                        // ITER RESULT RESULT
(-)a/js/src/frontend/BytecodeEmitter.h (+8 lines)
Line     Link Here 
 Lines 658-677   struct BytecodeEmitter Link Here 
658
    MOZ_MUST_USE bool emitDestructuringDeclsWithEmitter(JSOp prologueOp, ParseNode* pattern);
658
    MOZ_MUST_USE bool emitDestructuringDeclsWithEmitter(JSOp prologueOp, ParseNode* pattern);
659
659
660
    MOZ_MUST_USE bool emitDestructuringDecls(JSOp prologueOp, ParseNode* pattern);
660
    MOZ_MUST_USE bool emitDestructuringDecls(JSOp prologueOp, ParseNode* pattern);
661
661
662
    // Emit code to initialize all destructured names to the value on the top of
662
    // Emit code to initialize all destructured names to the value on the top of
663
    // the stack.
663
    // the stack.
664
    MOZ_MUST_USE bool emitInitializeDestructuringDecls(JSOp prologueOp, ParseNode* pattern);
664
    MOZ_MUST_USE bool emitInitializeDestructuringDecls(JSOp prologueOp, ParseNode* pattern);
665
665
666
    // Helper to emit codes to call self-hosted function with the value on the
667
    // top of the stack.
668
    MOZ_MUST_USE bool emitCallSelfHosted1AfterDup(JSAtom* func);
669
666
    // Throw a TypeError if the value atop the stack isn't convertible to an
670
    // Throw a TypeError if the value atop the stack isn't convertible to an
667
    // object, with no overall effect on the stack.
671
    // object, with no overall effect on the stack.
668
    MOZ_MUST_USE bool emitRequireObjectCoercible();
672
    MOZ_MUST_USE bool emitRequireObjectCoercible();
669
673
674
    // Throw a TypeError if the value atop the stack isn't convertible to an
675
    // object, with no overall effect on the stack.
676
    MOZ_MUST_USE bool emitRequireIsObjectIteratorNext();
677
670
    // emitIterator expects the iterable to already be on the stack.
678
    // emitIterator expects the iterable to already be on the stack.
671
    // It will replace that stack value with the corresponding iterator
679
    // It will replace that stack value with the corresponding iterator
672
    MOZ_MUST_USE bool emitIterator();
680
    MOZ_MUST_USE bool emitIterator();
673
681
674
    // Pops iterator from the top of the stack. Pushes the result of |.next()|
682
    // Pops iterator from the top of the stack. Pushes the result of |.next()|
675
    // onto the stack.
683
    // onto the stack.
676
    MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false);
684
    MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false);
677
685
(-)a/js/src/vm/CommonPropertyNames.h (+1 lines)
Line     Link Here 
 Lines 226-241    Link Here 
226
    macro(fulfilled, fulfilled, "fulfilled") \
226
    macro(fulfilled, fulfilled, "fulfilled") \
227
    macro(rejected, rejected, "rejected") \
227
    macro(rejected, rejected, "rejected") \
228
    macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \
228
    macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \
229
    macro(proto, proto, "__proto__") \
229
    macro(proto, proto, "__proto__") \
230
    macro(prototype, prototype, "prototype") \
230
    macro(prototype, prototype, "prototype") \
231
    macro(proxy, proxy, "proxy") \
231
    macro(proxy, proxy, "proxy") \
232
    macro(reason, reason, "reason") \
232
    macro(reason, reason, "reason") \
233
    macro(Reify, Reify, "Reify") \
233
    macro(Reify, Reify, "Reify") \
234
    macro(RequireIsObjectIteratorNext, RequireIsObjectIteratorNext, "RequireIsObjectIteratorNext") \
234
    macro(RequireObjectCoercible, RequireObjectCoercible, "RequireObjectCoercible") \
235
    macro(RequireObjectCoercible, RequireObjectCoercible, "RequireObjectCoercible") \
235
    macro(resumeGenerator, resumeGenerator, "resumeGenerator") \
236
    macro(resumeGenerator, resumeGenerator, "resumeGenerator") \
236
    macro(return, return_, "return") \
237
    macro(return, return_, "return") \
237
    macro(revoke, revoke, "revoke") \
238
    macro(revoke, revoke, "revoke") \
238
    macro(script, script, "script") \
239
    macro(script, script, "script") \
239
    macro(scripts, scripts, "scripts") \
240
    macro(scripts, scripts, "scripts") \
240
    macro(second, second, "second") \
241
    macro(second, second, "second") \
241
    macro(sensitivity, sensitivity, "sensitivity") \
242
    macro(sensitivity, sensitivity, "sensitivity") \
(-)a/js/src/vm/ForOfIterator.cpp (-2 / +4 lines)
Line     Link Here 
 Lines 130-149   ForOfIterator::next(MutableHandleValue v Link Here 
130
130
131
    RootedValue v(cx_);
131
    RootedValue v(cx_);
132
    if (!GetProperty(cx_, iterator, iterator, cx_->names().next, &v))
132
    if (!GetProperty(cx_, iterator, iterator, cx_->names().next, &v))
133
        return false;
133
        return false;
134
134
135
    if (!js::Call(cx_, v, iterator, &v))
135
    if (!js::Call(cx_, v, iterator, &v))
136
        return false;
136
        return false;
137
137
138
    RootedObject resultObj(cx_, ToObject(cx_, v));
138
    if (!v.isObject()) {
139
    if (!resultObj)
139
        JS_ReportErrorNumber(cx_, GetErrorMessage, nullptr, JSMSG_NEXT_RETURNED_PRIMITIVE);
140
        return false;
140
        return false;
141
    }
141
142
143
    RootedObject resultObj(cx_, &v.toObject());
142
    if (!GetProperty(cx_, resultObj, resultObj, cx_->names().done, &v))
144
    if (!GetProperty(cx_, resultObj, resultObj, cx_->names().done, &v))
143
        return false;
145
        return false;
144
146
145
    *done = ToBoolean(v);
147
    *done = ToBoolean(v);
146
    if (*done) {
148
    if (*done) {
147
        vp.setUndefined();
149
        vp.setUndefined();
148
        return true;
150
        return true;
149
    }
151
    }

Return to bug 1016936