Bug 1021835 - Part 1: Emit JSOP_CHECKISOBJ after GetIterator in byte code. r=evilpie
authorTooru Fujisawa <arai_a@mac.com>
Sun, 13 Nov 2016 00:40:28 +0900
changeset 352381 d27d2fec192e62ec05267147c60769b6379bf2e4
parent 352380 d96967030071dd928ed0460c1ec7edcc213b6419
child 352382 9b186ff95a5ef9ff00237fa72b19a0aa2d620a6d
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpie
bugs1021835
milestone52.0a1
Bug 1021835 - Part 1: Emit JSOP_CHECKISOBJ after GetIterator in byte code. r=evilpie
js/src/frontend/BytecodeEmitter.cpp
js/src/js.msg
js/src/tests/ecma_6/Destructuring/iterator-primitive.js
js/src/tests/ecma_6/Function/spread-iterator-primitive.js
js/src/tests/ecma_6/Generators/yield-star-iterator-primitive.js
js/src/tests/ecma_6/Statements/for-of-iterator-primitive.js
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5981,16 +5981,18 @@ BytecodeEmitter::emitIterator()
         return false;
     if (!emitElemOpBase(JSOP_CALLELEM))                           // OBJ ITERFN
         return false;
     if (!emit1(JSOP_SWAP))                                        // ITERFN OBJ
         return false;
     if (!emitCall(JSOP_CALLITER, 0))                              // ITER
         return false;
     checkTypeSet(JSOP_CALLITER);
+    if (!emitCheckIsObj(CheckIsObjectKind::GetIterator))          // ITER
+        return false;
     return true;
 }
 
 bool
 BytecodeEmitter::emitSpread(bool allowSelfHosted)
 {
     LoopControl loopInfo(this, StatementKind::Spread);
 
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -88,16 +88,17 @@ MSG_DEF(JSMSG_CANT_DEFINE_PROP_OBJECT_NO
 MSG_DEF(JSMSG_CANT_REDEFINE_PROP,      1, JSEXN_TYPEERR, "can't redefine non-configurable property {0}")
 MSG_DEF(JSMSG_CANT_REDEFINE_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't redefine array length")
 MSG_DEF(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't define array index property past the end of an array with non-writable length")
 MSG_DEF(JSMSG_BAD_GET_SET_FIELD,       1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function")
 MSG_DEF(JSMSG_THROW_TYPE_ERROR,        0, JSEXN_TYPEERR, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them")
 MSG_DEF(JSMSG_NOT_EXPECTED_TYPE,       3, JSEXN_TYPEERR, "{0}: expected {1}, got {2}")
 MSG_DEF(JSMSG_NOT_ITERABLE,            1, JSEXN_TYPEERR, "{0} is not iterable")
 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_NEXT_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "iterator.next() 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|")
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Destructuring/iterator-primitive.js
@@ -0,0 +1,36 @@
+var BUGNUMBER = 1021835;
+var summary = "Returning non-object from @@iterator should throw";
+
+print(BUGNUMBER + ": " + summary);
+
+let primitives = [
+    1,
+    true,
+    undefined,
+    null,
+    "foo",
+    Symbol.iterator
+];
+
+function f([]) {
+}
+
+for (let primitive of primitives) {
+    let obj = {
+        [Symbol.iterator]() {
+            return primitive;
+        }
+    };
+    assertThrowsInstanceOf(() => {
+        let [] = obj;
+    }, TypeError);
+    assertThrowsInstanceOf(() => {
+        [] = obj;
+    }, TypeError);
+    assertThrowsInstanceOf(() => {
+        f(obj);
+    }, TypeError);
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/spread-iterator-primitive.js
@@ -0,0 +1,28 @@
+var BUGNUMBER = 1021835;
+var summary = "Returning non-object from @@iterator should throw";
+
+print(BUGNUMBER + ": " + summary);
+
+let primitives = [
+    1,
+    true,
+    undefined,
+    null,
+    "foo",
+    Symbol.iterator
+];
+
+function f() {
+}
+
+for (let primitive of primitives) {
+    let arg = {
+        [Symbol.iterator]() {
+            return primitive;
+        }
+    };
+    assertThrowsInstanceOf(() => f(...arg), TypeError);
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Generators/yield-star-iterator-primitive.js
@@ -0,0 +1,31 @@
+var BUGNUMBER = 1021835;
+var summary = "Returning non-object from @@iterator should throw";
+
+print(BUGNUMBER + ": " + summary);
+
+let primitives = [
+    1,
+    true,
+    undefined,
+    null,
+    "foo",
+    Symbol.iterator
+];
+
+for (let primitive of primitives) {
+    let obj = {
+        [Symbol.iterator]() {
+            return primitive;
+        }
+    };
+    assertThrowsInstanceOf(() => {
+        function* g() {
+            yield* obj;
+        }
+        for (let x of g()) {
+        }
+    }, TypeError);
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Statements/for-of-iterator-primitive.js
@@ -0,0 +1,28 @@
+var BUGNUMBER = 1021835;
+var summary = "Returning non-object from @@iterator should throw";
+
+print(BUGNUMBER + ": " + summary);
+
+let primitives = [
+    1,
+    true,
+    undefined,
+    null,
+    "foo",
+    Symbol.iterator
+];
+
+for (let primitive of primitives) {
+    let obj = {
+        [Symbol.iterator]() {
+            return primitive;
+        }
+    };
+    assertThrowsInstanceOf(() => {
+        for (let x of obj) {
+        }
+    }, TypeError);
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(0, 0);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -5028,16 +5028,19 @@ js::ReportRuntimeRedeclaration(JSContext
 
 bool
 js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind)
 {
     switch (kind) {
       case CheckIsObjectKind::IteratorNext:
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NEXT_RETURNED_PRIMITIVE);
         break;
+      case CheckIsObjectKind::GetIterator:
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_GET_ITER_RETURNED_PRIMITIVE);
+        break;
       default:
         MOZ_CRASH("Unknown kind");
     }
     return false;
 }
 
 bool
 js::ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame)
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -556,17 +556,18 @@ ReportRuntimeLexicalError(JSContext* cx,
 
 // The parser only reports redeclarations that occurs within a single
 // script. Due to the extensibility of the global lexical scope, we also check
 // for redeclarations during runtime in JSOP_DEF{VAR,LET,CONST}.
 void
 ReportRuntimeRedeclaration(JSContext* cx, HandlePropertyName name, const char* redeclKind);
 
 enum class CheckIsObjectKind : uint8_t {
-    IteratorNext
+    IteratorNext,
+    GetIterator
 };
 
 bool
 ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind);
 
 bool
 ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame);