diff --git a/js/src/jit-test/tests/collections/Map-constructor-4.js b/js/src/jit-test/tests/collections/Map-constructor-4.js index 67b1329b5a5..8bd34c3fd38 100644 --- a/js/src/jit-test/tests/collections/Map-constructor-4.js +++ b/js/src/jit-test/tests/collections/Map-constructor-4.js @@ -1,6 +1,6 @@ // Map(x) throws if x is not iterable (unless x is undefined). load(libdir + "asserts.js"); -var nonIterables = [null, true, 1, -0, 3.14, NaN, {}, Math, this]; +var nonIterables = [null, true, 1, -0, 3.14, NaN, "", "xyzzy", {}, Math, this]; for (let k of nonIterables) assertThrowsInstanceOf(function () { Map(k); }, TypeError); diff --git a/js/src/jit-test/tests/for-of/arguments-1.js b/js/src/jit-test/tests/for-of/arguments-1.js index d9c6b526a37..945c6b221c7 100644 --- a/js/src/jit-test/tests/for-of/arguments-1.js +++ b/js/src/jit-test/tests/for-of/arguments-1.js @@ -1,9 +1,5 @@ // for-of can iterate arguments objects. -// Arguments objects do not have a .iterator() method by default. -// Install one on Object.prototype. -Object.prototype.iterator = Array.prototype.iterator; - var s; function test() { for (var v of arguments) diff --git a/js/src/jit-test/tests/for-of/arguments-2.js b/js/src/jit-test/tests/for-of/arguments-2.js index 534b396d8f3..f1781c91f2e 100644 --- a/js/src/jit-test/tests/for-of/arguments-2.js +++ b/js/src/jit-test/tests/for-of/arguments-2.js @@ -6,7 +6,6 @@ function f() { var s = ''; var args = f('a', 'b', 'c'); -Object.prototype.iterator = Array.prototype.iterator; for (var v of args) s += v; assertEq(s, 'abc'); diff --git a/js/src/jit-test/tests/for-of/arguments-3.js b/js/src/jit-test/tests/for-of/arguments-3.js index 6bb221c23c7..8550eff347a 100644 --- a/js/src/jit-test/tests/for-of/arguments-3.js +++ b/js/src/jit-test/tests/for-of/arguments-3.js @@ -1,7 +1,5 @@ // for-of can iterate strict arguments objects. -Object.prototype.iterator = Array.prototype.iterator; - var s; function test() { "use strict"; diff --git a/js/src/jit-test/tests/for-of/arguments-4.js b/js/src/jit-test/tests/for-of/arguments-4.js index 8e8b7d31e3e..08266ba905a 100644 --- a/js/src/jit-test/tests/for-of/arguments-4.js +++ b/js/src/jit-test/tests/for-of/arguments-4.js @@ -1,7 +1,5 @@ // for-of can iterate arguments objects for other active frames. -Object.prototype.iterator = Array.prototype.iterator; - var s; function g(obj) { for (var v of obj) diff --git a/js/src/jit-test/tests/for-of/arguments-5.js b/js/src/jit-test/tests/for-of/arguments-5.js index 49a13ac4051..fc88020d6ab 100644 --- a/js/src/jit-test/tests/for-of/arguments-5.js +++ b/js/src/jit-test/tests/for-of/arguments-5.js @@ -1,7 +1,5 @@ // for-of can iterate strict arguments objects in non-strict code. -Object.prototype.iterator = Array.prototype.iterator; - var s; function g(obj) { for (var v of obj) diff --git a/js/src/jit-test/tests/for-of/arguments-6.js b/js/src/jit-test/tests/for-of/arguments-6.js index 3d0bf1d8f1d..a5aed1e35de 100644 --- a/js/src/jit-test/tests/for-of/arguments-6.js +++ b/js/src/jit-test/tests/for-of/arguments-6.js @@ -1,7 +1,5 @@ // Changing arguments.length affects a for-of loop iterating over arguments. -Object.prototype.iterator = Array.prototype.iterator; - var s; function f() { arguments.length = 2; diff --git a/js/src/jit-test/tests/for-of/arguments-7.js b/js/src/jit-test/tests/for-of/arguments-7.js index b6871346a4e..5f6488f8a28 100644 --- a/js/src/jit-test/tests/for-of/arguments-7.js +++ b/js/src/jit-test/tests/for-of/arguments-7.js @@ -1,7 +1,5 @@ // Changing arguments.length during a for-of loop iterating over arguments affects the loop. -Object.prototype.iterator = Array.prototype.iterator; - var s; function f() { for (var v of arguments) { diff --git a/js/src/jit-test/tests/for-of/array-holes-4.js b/js/src/jit-test/tests/for-of/array-holes-4.js index 8c6696ab9a1..865111f3eb3 100644 --- a/js/src/jit-test/tests/for-of/array-holes-4.js +++ b/js/src/jit-test/tests/for-of/array-holes-4.js @@ -4,7 +4,6 @@ var m = {1: 'peek'}; var a = [0, , 2, 3]; a.__proto__ = m; var log = []; -Object.prototype.iterator = Array.prototype.iterator; for (var x of a) log.push(x); assertEq(log[1], 'peek'); diff --git a/js/src/jit-test/tests/for-of/array-iterator-gc.js b/js/src/jit-test/tests/for-of/array-iterator-gc.js deleted file mode 100644 index c8e99f242a2..00000000000 --- a/js/src/jit-test/tests/for-of/array-iterator-gc.js +++ /dev/null @@ -1,12 +0,0 @@ -// Array iterators keep the underlying array, arraylike object, or string alive. - -load(libdir + "referencesVia.js"); - -function test(obj) { - var it = Array.prototype.iterator.call(obj); - assertEq(referencesVia(it, "**UNKNOWN SLOT 0**", obj), true); -} - -test([]); -test([1, 2, 3, 4]); -test({}); diff --git a/js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js b/js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js deleted file mode 100644 index 6d09c80ef84..00000000000 --- a/js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js +++ /dev/null @@ -1,10 +0,0 @@ -// Superficial tests of the Array.prototype.iterator builtin function and its workalikes. - -var constructors = [Array, String, Uint8Array, Uint8ClampedArray]; -for (var c of constructors) { - assertEq(c.prototype.iterator.length, 0); - var desc = Object.getOwnPropertyDescriptor(c.prototype, "iterator"); - assertEq(desc.configurable, true); - assertEq(desc.enumerable, false); - assertEq(desc.writable, true); -} diff --git a/js/src/jit-test/tests/for-of/non-iterable.js b/js/src/jit-test/tests/for-of/non-iterable.js index f62ad4a43a4..36b3b123ac8 100644 --- a/js/src/jit-test/tests/for-of/non-iterable.js +++ b/js/src/jit-test/tests/for-of/non-iterable.js @@ -2,15 +2,13 @@ load(libdir + "asserts.js"); -function argsobj() { return arguments; } - var misc = [ {}, {x: 1}, Math, isNaN, Object.create(null), - argsobj(0, 1, 2), + Object.create(Array.prototype), null, undefined, - true, 0, 3.1416, - new Boolean(true), new Number(0)]; + true, 0, 3.1416, "", "ponies", + new Boolean(true), new Number(0), new String("ponies")]; for (var i = 0; i < misc.length; i++) { let v = misc[i]; diff --git a/js/src/jit-test/tests/for-of/proxy-1.js b/js/src/jit-test/tests/for-of/proxy-1.js index 4e0214a1091..bbe5cd117db 100644 --- a/js/src/jit-test/tests/for-of/proxy-1.js +++ b/js/src/jit-test/tests/for-of/proxy-1.js @@ -1,25 +1,25 @@ // Basic for-of test with Proxy. -function iterableProxy(arr) { - return Proxy.create({ - getPropertyDescriptor: function (name) { - for (var obj = arr; obj; obj = Object.getPrototypeOf(obj)) { - var desc = Object.getOwnPropertyDescriptor(obj, name); - if (desc) - return desc; - } - return undefined; +function iter(arr) { + var i = 0; + return { + next: function () { + if (i < arr.length) + return arr[i++]; + throw StopIteration; } - }); + }; +} + +function iterableProxy(arr) { + return Proxy.create({iterate: function () { return iter(arr); }}); } var s = ''; var arr = ['a', 'b', 'c', 'd']; var p = iterableProxy(arr); -// Test the same proxy twice. Each time through the loop, the proxy handler's -// getPropertyDescriptor method will be called 10 times (once for 'iterator', -// five times for 'length', and once for each of the four elements). +// Test the same proxy twice. Its iterate method should be called each time. for (var i = 0; i < 2; i++) { var j = 0; for (var x of p) diff --git a/js/src/jit-test/tests/for-of/proxy-2.js b/js/src/jit-test/tests/for-of/proxy-2.js index 488ad3f1690..0d487598e8c 100644 --- a/js/src/jit-test/tests/for-of/proxy-2.js +++ b/js/src/jit-test/tests/for-of/proxy-2.js @@ -1,27 +1,10 @@ -// Basic for-of test with Proxy whose iterator method is a generator. +// Basic for-of test with Proxy whose iterate method is a generator. var arr = ['a', 'b', 'c', 'd']; var proxy = Proxy.create({ - getPropertyDescriptor: function (name) { - if (name == 'iterator') { - return { - configurable: false, - enumerable: false, - writeable: false, - value: function () { - for (var i = 0; i < arr.length; i++) - yield arr[i]; - } - }; - } - - // Otherwise, inherit the property from arr. - for (var obj = arr; obj; obj = Object.getPrototypeOf(obj)) { - var desc = Object.getOwnPropertyDescriptor(obj, name); - if (desc) - return desc; - } - return undefined; + iterate: function () { + for (var i = 0; i < arr.length; i++) + yield arr[i]; } }); diff --git a/js/src/jit-test/tests/for-of/proxy-3.js b/js/src/jit-test/tests/for-of/proxy-3.js index b8062d94136..aefe678348c 100644 --- a/js/src/jit-test/tests/for-of/proxy-3.js +++ b/js/src/jit-test/tests/for-of/proxy-3.js @@ -1,12 +1,6 @@ -// An exception thrown from a proxy trap while getting the .iterator method is propagated. +// An exception thrown from an iterate trap is propagated. load(libdir + "asserts.js"); -var p = Proxy.create({ - getPropertyDescriptor: function (name) { - if (name == "iterator") - throw "fit"; - return undefined; - } -}); +var p = Proxy.create({iterate: function () { throw "fit"; }}); assertThrowsValue(function () { for (var v of p) {} }, "fit"); diff --git a/js/src/jit-test/tests/for-of/semantics-01.js b/js/src/jit-test/tests/for-of/semantics-01.js deleted file mode 100644 index d873129b5be..00000000000 --- a/js/src/jit-test/tests/for-of/semantics-01.js +++ /dev/null @@ -1,11 +0,0 @@ -// for-of is defined in terms of basic operations on objects, particularly -// [[Get]] for properties named "iterator" and "next", and [[Call]]. These -// "semantics" tests check that for-of really does appear to be implemented in -// terms of those more basic operations, as required by the spec, even in -// unusual cases. - -// Deleting Array.prototype.iterator makes for-of stop working on arrays. - -load(libdir + "asserts.js"); -delete Array.prototype.iterator; -assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError); diff --git a/js/src/jit-test/tests/for-of/semantics-02.js b/js/src/jit-test/tests/for-of/semantics-02.js deleted file mode 100644 index 297f7cc594c..00000000000 --- a/js/src/jit-test/tests/for-of/semantics-02.js +++ /dev/null @@ -1,10 +0,0 @@ -// Replacing Array.prototype.iterator with something non-callable makes for-of throw. - -load(libdir + "asserts.js"); -function test(v) { - Array.prototype.iterator = v; - assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError); -} -test(undefined); -test(null); -test({}); diff --git a/js/src/jit-test/tests/for-of/semantics-03.js b/js/src/jit-test/tests/for-of/semantics-03.js deleted file mode 100644 index 0383d02f9af..00000000000 --- a/js/src/jit-test/tests/for-of/semantics-03.js +++ /dev/null @@ -1,11 +0,0 @@ -// Replacing Array.prototype.iterator with a generator affects for-of behavior. - -Array.prototype.iterator = function () { - for (var i = this.length; --i >= 0; ) - yield this[i]; -}; - -var s = ''; -for (var v of ['a', 'b', 'c', 'd']) - s += v; -assertEq(s, 'dcba'); diff --git a/js/src/jit-test/tests/for-of/semantics-04.js b/js/src/jit-test/tests/for-of/semantics-04.js deleted file mode 100644 index a86b38bd8a5..00000000000 --- a/js/src/jit-test/tests/for-of/semantics-04.js +++ /dev/null @@ -1,15 +0,0 @@ -// Giving an Array an own .iterator property affects for-of. - -var a = []; -a.iterator = function () { - yield 'o'; - yield 'k'; -}; -var s = ''; -for (var v of a) - s += v; -assertEq(s, 'ok'); - -load(libdir + "asserts.js"); -a.iterator = undefined; -assertThrowsInstanceOf(function () { for (var v of a) ; }, TypeError); diff --git a/js/src/jit-test/tests/for-of/semantics-05.js b/js/src/jit-test/tests/for-of/semantics-05.js deleted file mode 100644 index 9adccbe6305..00000000000 --- a/js/src/jit-test/tests/for-of/semantics-05.js +++ /dev/null @@ -1,6 +0,0 @@ -// Deleting String.prototype.iterator makes for-of stop working on strings. - -load(libdir + "asserts.js"); -delete String.prototype.iterator; -assertThrowsInstanceOf(function () { for (var v of "abc") ; }, TypeError); -assertThrowsInstanceOf(function () { for (var v of new String("abc")) ; }, TypeError); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 09e1e620c91..b6752eed173 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4408,19 +4408,22 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) return JS_TRUE; } -JS_PUBLIC_API(JSBool) -JS_ArrayIterator(JSContext *cx, unsigned argc, jsval *vp) +JS_PUBLIC_API(JSObject *) +JS_NewElementIterator(JSContext *cx, JSObject *obj_) { - CallArgs args = CallArgsFromVp(argc, vp); - JSObject *target = NonNullObject(cx, args.thisv()); - if (!target) - return false; - Rooted iterobj(cx, target); - iterobj = ElementIteratorObject::create(cx, iterobj); - if (!iterobj) - return false; - vp->setObject(*iterobj); - return true; + AssertNoGC(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, obj_); + + Rooted obj(cx, obj_); + return ElementIteratorObject::create(cx, obj); +} + +JS_PUBLIC_API(JSObject *) +JS_ElementIteratorStub(JSContext *cx, JSHandleObject obj, JSBool keysonly) +{ + JS_ASSERT(!keysonly); + return JS_NewElementIterator(cx, obj); } JS_PUBLIC_API(jsval) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 97c2a6f8490..0d9069b0cd4 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3752,20 +3752,26 @@ struct JSClass { #define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \ JSCLASS_RESERVED_SLOTS_WIDTH) -#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) -#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1)) -#define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2)) -#define JSCLASS_INTERNAL_FLAG3 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3)) +/* + * Call the iteratorObject hook only to iterate over contents (for-of), not to + * enumerate properties (for-in, for-each, Object.keys, etc.) + */ +#define JSCLASS_FOR_OF_ITERATION (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) + +#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1)) +#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2)) +#define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3)) +#define JSCLASS_INTERNAL_FLAG3 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4)) /* Indicate whether the proto or ctor should be frozen. */ -#define JSCLASS_FREEZE_PROTO (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4)) -#define JSCLASS_FREEZE_CTOR (1<<(JSCLASS_HIGH_FLAGS_SHIFT+5)) +#define JSCLASS_FREEZE_PROTO (1<<(JSCLASS_HIGH_FLAGS_SHIFT+5)) +#define JSCLASS_FREEZE_CTOR (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6)) -#define JSCLASS_XPCONNECT_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6)) +#define JSCLASS_XPCONNECT_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+7)) /* Reserved for embeddings. */ -#define JSCLASS_USERBIT2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+7)) -#define JSCLASS_USERBIT3 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+8)) +#define JSCLASS_USERBIT2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+8)) +#define JSCLASS_USERBIT3 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+9)) /* * Bits 26 through 31 are reserved for the CACHED_PROTO_KEY mechanism, see @@ -4353,13 +4359,19 @@ extern JS_PUBLIC_API(JSBool) JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp); /* - * A JSNative that creates and returns a new iterator that iterates over the - * elements of |this|, up to |this.length|, in index order. This can be used to - * make any array-like object iterable. Just give the object an obj.iterator() - * method using this JSNative as the implementation. + * Create an object to iterate over the elements of obj in for-of order. This + * can be used to implement the iteratorObject hook for an array-like Class. */ -extern JS_PUBLIC_API(JSBool) -JS_ArrayIterator(JSContext *cx, unsigned argc, jsval *vp); +extern JS_PUBLIC_API(JSObject *) +JS_NewElementIterator(JSContext *cx, JSObject *obj); + +/* + * To make your array-like class iterable using the for-of loop, set the + * JSCLASS_FOR_OF_ITERATION bit in the class's flags field and set its + * .ext.iteratorObject hook to this function. + */ +extern JS_PUBLIC_API(JSObject *) +JS_ElementIteratorStub(JSContext *cx, JSHandleObject obj, JSBool keysonly); extern JS_PUBLIC_API(JSBool) JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 1e3cc9dfc33..b71f05c061a 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1190,7 +1190,7 @@ array_trace(JSTracer *trc, JSObject *obj) Class js::ArrayClass = { "Array", - Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array), + Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION, JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */ @@ -1208,7 +1208,7 @@ Class js::ArrayClass = { NULL, /* equality */ NULL, /* outerObject */ NULL, /* innerObject */ - NULL, /* iteratorObject */ + JS_ElementIteratorStub, NULL, /* unused */ false, /* isWrappedNative */ }, @@ -1251,7 +1251,7 @@ Class js::ArrayClass = { Class js::SlowArrayClass = { "Array", - JSCLASS_HAS_CACHED_PROTO(JSProto_Array), + JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION, slowarray_addProperty, JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */ @@ -1269,7 +1269,7 @@ Class js::SlowArrayClass = { NULL, /* equality */ NULL, /* outerObject */ NULL, /* innerObject */ - NULL, /* iteratorObject */ + JS_ElementIteratorStub, NULL, /* unused */ false, /* isWrappedNative */ } @@ -3621,7 +3621,6 @@ static JSFunctionSpec array_methods[] = { JS_FN("some", array_some, 1,JSFUN_GENERIC_NATIVE), JS_FN("every", array_every, 1,JSFUN_GENERIC_NATIVE), - JS_FN("iterator", JS_ArrayIterator, 0,0), JS_FS_END }; diff --git a/js/src/jsatom.tbl b/js/src/jsatom.tbl index 92f45d08fff..3c2e47f4503 100644 --- a/js/src/jsatom.tbl +++ b/js/src/jsatom.tbl @@ -53,8 +53,7 @@ DEFINE_ATOM(ignoreCase, "ignoreCase") DEFINE_ATOM(index, "index") DEFINE_ATOM(input, "input") DEFINE_ATOM(toISOString, "toISOString") -DEFINE_ATOM(iterator, "iterator") -DEFINE_ATOM(iteratorIntrinsic, "__iterator__") +DEFINE_ATOM(iterator, "__iterator__") DEFINE_ATOM(join, "join") DEFINE_ATOM(lastIndex, "lastIndex") DEFINE_ATOM(length, "length") diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 990e2c325b3..fe2fe184bf0 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -3917,10 +3917,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, return false; } - if (GET_UINT8(pc) == JSITER_ENUMERATE) - state.forTypes->addType(cx, Type::StringType()); - else + if (GET_UINT8(pc) & JSITER_FOREACH) state.forTypes->addType(cx, Type::UnknownType()); + else + state.forTypes->addType(cx, Type::StringType()); break; } diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 73344176fa5..f4a43587fc4 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -52,6 +52,8 @@ using namespace mozilla; using namespace js; using namespace js::gc; +static JSObject *iterator_iterator(JSContext *cx, HandleObject obj, JSBool keysonly); + Class js::ElementIteratorClass = { "ElementIterator", JSCLASS_HAS_RESERVED_SLOTS(ElementIteratorObject::NumSlots), @@ -72,7 +74,7 @@ Class js::ElementIteratorClass = { NULL, /* equality */ NULL, /* outerObject */ NULL, /* innerObject */ - NULL, /* iteratorObject */ + iterator_iterator, NULL /* unused */ } }; @@ -367,8 +369,18 @@ GetCustomIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp) { JS_CHECK_RECURSION(cx, return false); + /* + * for-of iteration does not fall back on __iterator__ or property + * enumeration. This is more conservative than the current proposed spec. + */ + if (flags == JSITER_FOR_OF) { + js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NOT_ITERABLE, + JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL, NULL, NULL); + return false; + } + /* Check whether we have a valid __iterator__ method. */ - PropertyName *name = cx->runtime->atomState.iteratorIntrinsicAtom; + PropertyName *name = cx->runtime->atomState.iteratorAtom; if (!GetMethod(cx, obj, name, 0, vp)) return false; @@ -594,42 +606,30 @@ UpdateNativeIterator(NativeIterator *ni, JSObject *obj) bool GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp) { - if (flags == JSITER_FOR_OF) { - // for-of loop. The iterator is simply |obj.iterator()|. - Value method; - if (!obj->getProperty(cx, obj, cx->runtime->atomState.iteratorAtom, &method)) - return false; - - // Throw if obj.iterator isn't callable. js::Invoke is about to check - // for this kind of error anyway, but it would throw an inscrutable - // error message about |method| rather than this nice one about |obj|. - if (!method.isObject() || !method.toObject().isCallable()) { - char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, ObjectOrNullValue(obj), NULL); - if (!bytes) - return false; - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_ITERABLE, bytes); - cx->free_(bytes); - return false; - } - - if (!Invoke(cx, ObjectOrNullValue(obj), method, 0, NULL, vp)) - return false; - return true; - } - Vector shapes(cx); uint32_t key = 0; bool keysOnly = (flags == JSITER_ENUMERATE); if (obj) { + /* Enumerate Iterator.prototype directly. */ if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) { - JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH)); - if (!iterobj) - return false; - vp->setObject(*iterobj); - types::MarkIteratorUnknown(cx); - return true; + /* + * Arrays and other classes representing iterable collections have + * the JSCLASS_FOR_OF_ITERATION flag. This flag means that the + * object responds to all other kinds of enumeration (for-in, + * for-each, Object.keys, Object.getOwnPropertyNames, etc.) in the + * default way, ignoring the hook. The hook is used only when + * iterating in the style of a for-of loop. + */ + if (!(obj->getClass()->flags & JSCLASS_FOR_OF_ITERATION) || flags == JSITER_FOR_OF) { + JSObject *iterobj = op(cx, obj, !(flags & (JSITER_FOREACH | JSITER_FOR_OF))); + if (!iterobj) + return false; + vp->setObject(*iterobj); + types::MarkIteratorUnknown(cx); + return true; + } } if (keysOnly) { @@ -738,6 +738,12 @@ GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp) } +static JSObject * +iterator_iterator(JSContext *cx, HandleObject obj, JSBool keysonly) +{ + return obj; +} + static JSBool Iterator(JSContext *cx, unsigned argc, Value *vp) { @@ -769,14 +775,6 @@ js_ThrowStopIteration(JSContext *cx) return JS_FALSE; } -static JSBool -iterator_iterator(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - args.rval() = args.thisv(); - return true; -} - static JSBool iterator_next(JSContext *cx, unsigned argc, Value *vp) { @@ -802,18 +800,13 @@ iterator_next(JSContext *cx, unsigned argc, Value *vp) return js_IteratorNext(cx, thisObj, &args.rval()); } +#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT) + static JSFunctionSpec iterator_methods[] = { - JS_FN("iterator", iterator_iterator, 0, 0), - JS_FN("next", iterator_next, 0, 0), + JS_FN(js_next_str, iterator_next, 0,JSPROP_ROPERM), JS_FS_END }; -JSObject * -iterator_iteratorObject(JSContext *cx, HandleObject obj, JSBool keysonly) -{ - return obj; -} - void PropertyIteratorObject::trace(JSTracer *trc, JSObject *obj) { @@ -852,7 +845,7 @@ Class PropertyIteratorObject::class_ = { NULL, /* equality */ NULL, /* outerObject */ NULL, /* innerObject */ - iterator_iteratorObject, + iterator_iterator, NULL /* unused */ } }; @@ -862,7 +855,11 @@ static JSBool CloseGenerator(JSContext *cx, JSObject *genobj); #endif -bool +/* + * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists. + * Otherwise construct the default iterator. + */ +JSBool js::ValueToIterator(JSContext *cx, unsigned flags, Value *vp) { /* JSITER_KEYVALUE must always come with JSITER_FOREACH */ @@ -1293,7 +1290,7 @@ stopiter_hasInstance(JSContext *cx, HandleObject obj, const Value *v, JSBool *bp } Class js::StopIterationClass = { - "StopIteration", + js_StopIteration_str, JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration) | JSCLASS_FREEZE_PROTO, JS_PropertyStub, /* addProperty */ @@ -1408,7 +1405,7 @@ Class js::GeneratorClass = { NULL, /* equality */ NULL, /* outerObject */ NULL, /* innerObject */ - iterator_iteratorObject, + iterator_iterator, NULL /* unused */ } }; @@ -1691,14 +1688,11 @@ generator_close(JSContext *cx, unsigned argc, Value *vp) return generator_op(cx, generator_close, JSGENOP_CLOSE, vp, argc); } -#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT) - static JSFunctionSpec generator_methods[] = { - JS_FN("iterator", iterator_iterator, 0, 0), - JS_FN("next", generator_next, 0,JSPROP_ROPERM), - JS_FN("send", generator_send, 1,JSPROP_ROPERM), - JS_FN("throw", generator_throw, 1,JSPROP_ROPERM), - JS_FN("close", generator_close, 0,JSPROP_ROPERM), + JS_FN(js_next_str, generator_next, 0,JSPROP_ROPERM), + JS_FN(js_send_str, generator_send, 1,JSPROP_ROPERM), + JS_FN(js_throw_str, generator_throw, 1,JSPROP_ROPERM), + JS_FN(js_close_str, generator_close, 0,JSPROP_ROPERM), JS_FS_END }; diff --git a/js/src/jsiter.h b/js/src/jsiter.h index 6d5c0deee3c..4e4dc4dbd3b 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -170,16 +170,16 @@ EnumeratedIdVectorToIterator(JSContext *cx, HandleObject obj, unsigned flags, Au * for-in semantics are required, and when the caller can guarantee that the * iterator will never be exposed to scripts. */ -bool +extern JSBool ValueToIterator(JSContext *cx, unsigned flags, Value *vp); -bool +extern bool CloseIterator(JSContext *cx, JSObject *iterObj); -bool +extern bool UnwindIteratorForException(JSContext *cx, JSObject *obj); -void +extern void UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj); } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 05441165303..4f11119ec83 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -5123,7 +5123,7 @@ js_GetPropertyHelperInline(JSContext *cx, HandleObject obj, HandleObject receive * XXX do not warn about missing __iterator__ as the function * may be called from JS_GetMethodById. See bug 355145. */ - if (JSID_IS_ATOM(id, cx->runtime->atomState.iteratorIntrinsicAtom)) + if (JSID_IS_ATOM(id, cx->runtime->atomState.iteratorAtom)) return JS_TRUE; /* Do not warn about tests like (obj[prop] == undefined). */ diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index ff080bd8665..63ba02d0e07 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -2975,7 +2975,6 @@ static JSFunctionSpec string_methods[] = { JS_FN("sub", str_sub, 0,0), #endif - JS_FN("iterator", JS_ArrayIterator, 0,0), JS_FS_END }; diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 0d1d185c057..b9ed01b7cab 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -2882,7 +2882,6 @@ JSFunctionSpec ArrayBufferObject::jsfuncs[] = { #define IMPL_TYPED_ARRAY_STATICS(_typedArray) \ JSFunctionSpec _typedArray::jsfuncs[] = { \ - JS_FN("iterator", JS_ArrayIterator, 0, 0), \ JS_FN("subarray", _typedArray::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \ JS_FN("set", _typedArray::fun_set, 2, JSFUN_GENERIC_NATIVE), \ JS_FN("move", _typedArray::fun_move, 3, JSFUN_GENERIC_NATIVE), \ @@ -2948,6 +2947,7 @@ IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double) JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \ JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \ JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray) | \ + JSCLASS_FOR_OF_ITERATION | \ Class::NON_NATIVE, \ JS_PropertyStub, /* addProperty */ \ JS_PropertyStub, /* delProperty */ \ @@ -2966,7 +2966,7 @@ IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double) NULL, /* equality */ \ NULL, /* outerObject */ \ NULL, /* innerObject */ \ - NULL, /* iteratorObject */ \ + JS_ElementIteratorStub, \ NULL, /* unused */ \ false, /* isWrappedNative */ \ }, \ diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index 06ce9be79fe..8e6759b0129 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -421,7 +421,8 @@ Class js::NormalArgumentsObjectClass = { "Arguments", JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), + JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | + JSCLASS_FOR_OF_ITERATION, JS_PropertyStub, /* addProperty */ args_delProperty, JS_PropertyStub, /* getProperty */ @@ -439,7 +440,7 @@ Class js::NormalArgumentsObjectClass = { NULL, /* equality */ NULL, /* outerObject */ NULL, /* innerObject */ - NULL, /* iteratorObject */ + JS_ElementIteratorStub, NULL, /* unused */ false, /* isWrappedNative */ } @@ -454,7 +455,8 @@ Class js::StrictArgumentsObjectClass = { "Arguments", JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), + JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | + JSCLASS_FOR_OF_ITERATION, JS_PropertyStub, /* addProperty */ args_delProperty, JS_PropertyStub, /* getProperty */ @@ -472,7 +474,7 @@ Class js::StrictArgumentsObjectClass = { NULL, /* equality */ NULL, /* outerObject */ NULL, /* innerObject */ - NULL, /* iteratorObject */ + JS_ElementIteratorStub, NULL, /* unused */ false, /* isWrappedNative */ }