Bug 1641359 - Add Iterator.from method. r=jorendorff

Implement Iterator.from static method from the Iterator Helpers proposal.
Involves adding a WrapForValidIterator object and prototype that is used
to wrap iterators returned by `Iterator.from`.

Differential Revision: https://phabricator.services.mozilla.com/D77178
This commit is contained in:
Adam Vandolder 2020-06-03 14:18:01 +00:00
Родитель c5a5efa985
Коммит 28738d7480
32 изменённых файлов: 746 добавлений и 3 удалений

Просмотреть файл

@ -700,7 +700,7 @@ static const uint32_t JSCLASS_FOREGROUND_FINALIZE =
// application.
static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 25;
JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 26;
static constexpr uint32_t JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(uint32_t n) {
return JSCLASS_IS_GLOBAL |

Просмотреть файл

@ -3,5 +3,116 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function IteratorIdentity() {
return this;
return this;
}
/* Iterator Helpers proposal 1.1.1 */
function GetIteratorDirect(obj) {
// Step 1.
if (!IsObject(obj))
ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, obj));
// Step 2.
const nextMethod = obj.next;
// Step 3.
if (!IsCallable(nextMethod))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, nextMethod));
// Steps 4-5.
return {
iterator: obj,
nextMethod,
done: false,
};
}
/* Iterator Helpers proposal 2.1.3.3.1 */
function IteratorFrom(O) {
// Step 1.
const usingIterator = O[std_iterator];
let iteratorRecord;
// Step 2.
if (usingIterator !== undefined && usingIterator !== null) {
// Step a.
// Inline call to GetIterator.
const iterator = callContentFunction(usingIterator, O);
iteratorRecord = GetIteratorDirect(iterator);
// Step b-c.
if (iteratorRecord.iterator instanceof GetBuiltinConstructor("Iterator"))
return iteratorRecord.iterator;
} else {
// Step 3.
iteratorRecord = GetIteratorDirect(O);
}
// Step 4.
const wrapper = NewWrapForValidIterator();
// Step 5.
UnsafeSetReservedSlot(wrapper, ITERATED_SLOT, iteratorRecord);
// Step 6.
return wrapper;
}
/* Iterator Helpers proposal 2.1.3.3.1.1.1 */
function WrapForValidIteratorNext(value) {
// Step 1-2.
let O;
if (!IsObject(this) || (O = GuardToWrapForValidIterator(this)) === null)
ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, O));
const iterated = UnsafeGetReservedSlot(O, ITERATED_SLOT);
// Step 3.
let result;
if (arguments.length === 0) {
result = callContentFunction(iterated.nextMethod, iterated.iterator);
} else { // Step 4.
result = callContentFunction(iterated.nextMethod, iterated.iterator, value);
}
// Inlined from IteratorNext.
if (!IsObject(result))
ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, result));
return result;
}
/* Iterator Helpers proposal 2.1.3.3.1.1.2 */
function WrapForValidIteratorReturn(value) {
// Step 1-2.
let O;
if (!IsObject(this) || (O = GuardToWrapForValidIterator(this)) === null)
ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, O));
const iterated = UnsafeGetReservedSlot(O, ITERATED_SLOT);
// Step 3.
// Inline call to IteratorClose.
const iterator = iterated.iterator;
const returnMethod = iterator.return;
if (returnMethod !== undefined && returnMethod !== null) {
let innerResult = callContentFunction(returnMethod, iterator);
if (!IsObject(innerResult))
ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, innerResult));
}
// Step 4.
return {
done: true,
value,
};
}
/* Iterator Helpers proposal 2.1.3.3.1.1.3 */
function WrapForValidIteratorThrow(value) {
// Step 1-2.
let O;
if (!IsObject(this) || (O = GuardToWrapForValidIterator(this)) === null)
ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, O));
const iterated = UnsafeGetReservedSlot(O, ITERATED_SLOT);
// Step 3.
const iterator = iterated.iterator;
// Step 4.
const throwMethod = iterator.throw;
// Step 5.
if (throwMethod === undefined || throwMethod === null) {
throw value;
}
// Step 6.
return callContentFunction(throwMethod, iterator, value);
}

Просмотреть файл

@ -134,4 +134,6 @@
#define TYPEDARRAY_KIND_BIGINT64 9
#define TYPEDARRAY_KIND_BIGUINT64 10
#define ITERATED_SLOT 0
#endif

Просмотреть файл

@ -152,6 +152,7 @@
_(IntrinsicGuardToSetIterator) \
_(IntrinsicGuardToStringIterator) \
_(IntrinsicGuardToRegExpStringIterator) \
_(IntrinsicGuardToWrapForValidIterator) \
\
_(IntrinsicGuardToMapObject) \
_(IntrinsicGetNextMapEntryForIterator) \

Просмотреть файл

@ -152,6 +152,7 @@ static bool CanInlineCrossRealm(InlinableNative native) {
case InlinableNative::IntrinsicGuardToSetIterator:
case InlinableNative::IntrinsicGuardToStringIterator:
case InlinableNative::IntrinsicGuardToRegExpStringIterator:
case InlinableNative::IntrinsicGuardToWrapForValidIterator:
case InlinableNative::IntrinsicObjectHasPrototype:
case InlinableNative::IntrinsicFinishBoundFunctionInit:
case InlinableNative::IntrinsicIsPackedArray:
@ -574,6 +575,8 @@ IonBuilder::InliningResult IonBuilder::inlineNativeCall(CallInfo& callInfo,
return inlineGuardToClass(callInfo, &StringIteratorObject::class_);
case InlinableNative::IntrinsicGuardToRegExpStringIterator:
return inlineGuardToClass(callInfo, &RegExpStringIteratorObject::class_);
case InlinableNative::IntrinsicGuardToWrapForValidIterator:
return inlineGuardToClass(callInfo, &WrapForValidIteratorObject::class_);
case InlinableNative::IntrinsicObjectHasPrototype:
return inlineObjectHasPrototype(callInfo);
case InlinableNative::IntrinsicFinishBoundFunctionInit:

Просмотреть файл

@ -728,3 +728,25 @@ shell-option(--enable-iterator-helpers) script non262/Iterator/constructor-subcl
shell-option(--enable-iterator-helpers) script non262/Iterator/constructor-throw-when-called-directly.js
shell-option(--enable-iterator-helpers) script non262/Iterator/constructor-throw-without-new.js
shell-option(--enable-iterator-helpers) script non262/Iterator/toStringTag.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/call-from-with-different-this.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/Iterator.from-descriptor.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/Iterator.from-length.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/Iterator.from-name.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/iterator-not-callable-throws.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/modify-next.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/modify-return.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/modify-throw.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/o-not-object-throws.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/proxy-not-wrapped.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/proxy-wrap-next.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/proxy-wrap-return.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/proxy-wrap-throw.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/return-iterator-if-iterable.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/return-wrapper-if-not-iterable.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/return-wrapper-if-not-iterator-instance.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/wrap-method-with-non-wrap-this-throws.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/wrap-next-forwards-value.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/wrap-next-not-object-throws.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/wrap-new-global.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/wrap-return-closes-iterator.js
shell-option(--enable-iterator-helpers) script non262/Iterator/from/wrap-throw.js

Просмотреть файл

@ -0,0 +1,12 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
/*---
Descriptor property of Iterator.from
---*/
const propDesc = Reflect.getOwnPropertyDescriptor(Iterator, 'from');
assertEq(propDesc.writable, true);
assertEq(propDesc.enumerable, false);
assertEq(propDesc.configurable, true);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,17 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
/*---
The `length` property of Iterator.from.
info: |
ES7 section 17: Unless otherwise specified, the length property of a built-in
Function object has the attributes { [[Writable]]: false, [[Enumerable]]:
false, [[Configurable]]: true }.
---*/
const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.from, 'length');
assertEq(propDesc.value, 1);
assertEq(propDesc.writable, false);
assertEq(propDesc.enumerable, false);
assertEq(propDesc.configurable, true);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,13 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
/*---
`name` property of Iterator.from.
---*/
const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.from, 'name');
assertEq(propDesc.value, 'from');
assertEq(propDesc.writable, false);
assertEq(propDesc.enumerable, false);
assertEq(propDesc.configurable, true);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,18 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
const iter = {
next: () => ({done: false, value: 0}),
};
const wrap = Iterator.from.call(undefined, iter);
const result = wrap.next();
assertEq(result.done, false);
assertEq(result.value, 0);
const returnResult = wrap.return(1);
assertEq(returnResult.done, true);
assertEq(returnResult.value, 1);
assertThrowsInstanceOf(() => wrap.throw(new Error()), Error);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,13 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
/*---
Iterator.from throws when called with an object with a non-callable @@iterator property.
---*/
assertThrowsInstanceOf(() => Iterator.from({ [Symbol.iterator]: 0 }), TypeError);
assertThrowsInstanceOf(() => Iterator.from({ [Symbol.iterator]: false }), TypeError);
assertThrowsInstanceOf(() => Iterator.from({ [Symbol.iterator]: "" }), TypeError);
assertThrowsInstanceOf(() => Iterator.from({ [Symbol.iterator]: {} }), TypeError);
assertThrowsInstanceOf(() => Iterator.from({ [Symbol.iterator]: Symbol('') }), TypeError);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,15 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
const iter = {
next: () => ({ done: false, value: 0 }),
};
const wrap = Iterator.from(iter);
iter.next = () => ({ done: true, value: undefined });
let {done, value} = wrap.next();
assertEq(done, false);
assertEq(value, 0);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,22 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
const iter = {
next: () => ({ done: false, value: 0 }),
return: (value) => ({ done: true, value }),
};
const wrap = Iterator.from(iter);
let {done, value} = wrap.return(1);
assertEq(done, true);
assertEq(value, 1);
iter.return = () => { throw new Error(); };
assertThrowsInstanceOf(wrap.return, Error);
iter.return = null;
let nullResult = wrap.return(2);
assertEq(nullResult.done, true);
assertEq(nullResult.value, 2);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,18 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
const iter = {
next: () => ({ done: false, value: 0 }),
throw: (value) => ({ done: true, value }),
};
const wrap = Iterator.from(iter);
let {done, value} = wrap.throw(0);
assertEq(done, true);
assertEq(value, 0);
class TestError extends Error {}
iter.throw = () => { throw new TestError(); };
assertThrowsInstanceOf(() => wrap.throw(), TestError);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,13 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
/*---
Iterator.from throws when called with a non-object.
---*/
assertThrowsInstanceOf(() => Iterator.from(undefined), TypeError);
assertThrowsInstanceOf(() => Iterator.from(null), TypeError);
assertThrowsInstanceOf(() => Iterator.from(0), TypeError);
assertThrowsInstanceOf(() => Iterator.from(false), TypeError);
assertThrowsInstanceOf(() => Iterator.from(Symbol('')), TypeError);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,34 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
const log = [];
const handlerProxy = new Proxy({}, {
get: (target, key, receiver) => (...args) => {
log.push(`${key}: ${args[1]?.toString()}`);
const item = Reflect[key](...args);
if (typeof item === 'function')
return (...args) => new Proxy(item.apply(receiver, args), handlerProxy);
return item;
},
});
class Iter extends Iterator {
[Symbol.iterator]() {
return this;
}
next() {
return { done: false, value: 0 };
}
}
const iter = new Iter();
const proxy = new Proxy(iter, handlerProxy);
const wrap = Iterator.from(proxy);
assertEq(
log.join('\n'),
`get: Symbol(Symbol.iterator)
get: next
getPrototypeOf: undefined`
);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,30 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
const log = [];
const handlerProxy = new Proxy({}, {
get: (target, key, receiver) => (...args) => {
log.push(`${key}: ${args[1].toString()}`);
const item = Reflect[key](...args);
if (typeof item === 'function')
return item.bind(receiver);
return item;
},
});
const iter = new Proxy({
next: () => ({ done: false, value: 0 }),
}, handlerProxy);
const wrap = Iterator.from(iter);
// Call next multiple times. Should not call `get` on proxy.
wrap.next();
wrap.next();
wrap.next();
assertEq(
log.join('\n'),
`get: Symbol(Symbol.iterator)
get: next`
);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,31 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
const log = [];
const handlerProxy = new Proxy({}, {
get: (target, key, receiver) => (...args) => {
log.push(`${key}: ${args[1].toString()}`);
const item = Reflect[key](...args);
if (typeof item === 'function')
return item.bind(receiver);
return item;
},
});
const iter = new Proxy({
next: () => ({ done: false, value: 0 }),
return: (value) => ({ done: true, value }),
}, handlerProxy);
const wrap = Iterator.from(iter);
wrap.return();
wrap.return();
assertEq(
log.join('\n'),
`get: Symbol(Symbol.iterator)
get: next
get: return
get: return`
);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,31 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
const log = [];
const handlerProxy = new Proxy({}, {
get: (target, key, receiver) => (...args) => {
log.push(`${key}: ${args[1].toString()}`);
const item = Reflect[key](...args);
if (typeof item === 'function')
return item.bind(receiver);
return item;
},
});
const iter = new Proxy({
next: () => ({ done: false, value: 0 }),
throw: (value) => ({ done: true, value }),
}, handlerProxy);
const wrap = Iterator.from(iter);
wrap.throw();
wrap.throw();
assertEq(
log.join('\n'),
`get: Symbol(Symbol.iterator)
get: next
get: throw
get: throw`
);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,25 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
/*---
Iterator.from returns O if it is iterable, an iterator, and an instance of Iterator.
---*/
class TestIterator extends Iterator {
[Symbol.iterator]() {
return this;
}
next() {
return { done: false, value: this.value++ };
}
value = 0;
}
const iter = new TestIterator();
assertEq(iter, Iterator.from(iter));
const arrayIter = [1, 2, 3][Symbol.iterator]();
assertEq(arrayIter, Iterator.from(arrayIter));
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,28 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
/*---
Iterator.from returns an iterator wrapper if O is not an iterable.
---*/
class TestIterator {
next() {
return { done: false, value: 0 };
}
}
const iter = new TestIterator();
assertEq(
Symbol.iterator in iter,
false,
'iter is not an iterable.'
);
const wrapper = Iterator.from(iter);
assertEq(iter !== wrapper, true);
assertEq(
Symbol.iterator in wrapper,
true,
'wrapper is an iterable.'
);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,24 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
/*---
Iterator.from returns an iterator wrapper if O is not an instance of Iterator.
---*/
class TestIterator {
[Symbol.iterator]() {
return this;
}
next() {
return { done: false, value: 0 };
}
}
const iter = new TestIterator();
assertEq(iter instanceof Iterator, false);
const wrapper = Iterator.from(iter);
assertEq(iter !== wrapper, true);
assertEq(wrapper instanceof Iterator, true);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,42 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
// All methods on %WrapForValidIteratorPrototype% require an [[Iterated]]
// internal slot on the `this` object.
class TestIterator {
next() {
return {
done: false,
value: 0,
};
}
}
const nextMethod = Iterator.from(new TestIterator()).next;
assertThrowsInstanceOf(() => nextMethod.call(undefined), TypeError);
assertThrowsInstanceOf(() => nextMethod.call(null), TypeError);
assertThrowsInstanceOf(() => nextMethod.call(0), TypeError);
assertThrowsInstanceOf(() => nextMethod.call(false), TypeError);
assertThrowsInstanceOf(() => nextMethod.call('test'), TypeError);
assertThrowsInstanceOf(() => nextMethod.call(Object(1)), TypeError);
assertThrowsInstanceOf(() => nextMethod.call({}), TypeError);
const returnMethod = Iterator.from(new TestIterator()).next;
assertThrowsInstanceOf(() => returnMethod.call(undefined), TypeError);
assertThrowsInstanceOf(() => returnMethod.call(null), TypeError);
assertThrowsInstanceOf(() => returnMethod.call(0), TypeError);
assertThrowsInstanceOf(() => returnMethod.call(false), TypeError);
assertThrowsInstanceOf(() => returnMethod.call('test'), TypeError);
assertThrowsInstanceOf(() => returnMethod.call(Object(1)), TypeError);
assertThrowsInstanceOf(() => returnMethod.call({}), TypeError);
const throwMethod = Iterator.from(new TestIterator()).next;
assertThrowsInstanceOf(() => throwMethod.call(undefined), TypeError);
assertThrowsInstanceOf(() => throwMethod.call(null), TypeError);
assertThrowsInstanceOf(() => throwMethod.call(0), TypeError);
assertThrowsInstanceOf(() => throwMethod.call(false), TypeError);
assertThrowsInstanceOf(() => throwMethod.call('test'), TypeError);
assertThrowsInstanceOf(() => throwMethod.call(Object(1)), TypeError);
assertThrowsInstanceOf(() => throwMethod.call({}), TypeError);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,9 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
const otherGlobal = newGlobal({newCompartment: true});
const iter = [1, 2, 3].values();
assertEq(iter, Iterator.from(iter));
assertEq(iter !== otherGlobal.Iterator.from(iter), true);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,18 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
class Iter {
next(value) {
this.v = value;
return { done: false, value };
}
}
const iter = new Iter();
const wrap = Iterator.from(iter);
assertEq(iter !== wrap, true);
assertEq(iter.v, undefined);
wrap.next(1);
assertEq(iter.v, 1);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,14 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
const iter = (value) => Iterator.from({
next: () => value,
});
assertThrowsInstanceOf(() => iter(undefined).next(), TypeError);
assertThrowsInstanceOf(() => iter(null).next(), TypeError);
assertThrowsInstanceOf(() => iter(0).next(), TypeError);
assertThrowsInstanceOf(() => iter(false).next(), TypeError);
assertThrowsInstanceOf(() => iter('test').next(), TypeError);
assertThrowsInstanceOf(() => iter(Symbol('')).next(), TypeError);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,33 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
class Iter {
next() {
if (this.closed)
return { done: true, value: undefined };
return { done: false, value: 0 };
}
return(value) {
this.closed = true;
return { done: true, value };
}
}
const iter = new Iter();
const wrap = Iterator.from(iter);
assertEq(iter.closed, undefined);
let result = wrap.next();
assertEq(result.done, false);
assertEq(result.value, 0);
result = wrap.return(1);
assertEq(result.done, true);
assertEq(result.value, 1);
assertEq(iter.closed, true);
result = wrap.next();
assertEq(result.done, true);
assertEq(result.value, undefined);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -0,0 +1,45 @@
// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally
class Iter {
next() {
return { done: false, value: 0 };
}
}
const iter = new Iter();
const wrap = Iterator.from(iter);
assertThrowsInstanceOf(() => wrap.throw(new Error()), Error);
assertThrows(() => wrap.throw());
assertThrows(() => wrap.throw(1));
class IterThrowNull {
next() {
return { done: false, value: 0 };
}
throw = null;
}
const iterNull = new IterThrowNull();
const wrapNull = Iterator.from(iter);
assertThrowsInstanceOf(() => wrapNull.throw(new Error()), Error);
assertThrows(() => wrapNull.throw());
assertThrows(() => wrapNull.throw(1));
class IterWithThrow {
next() {
return { done: false, value: 0 };
}
throw(value) {
return value;
}
}
const iterWithThrow = new IterWithThrow();
const wrapWithThrow = Iterator.from(iterWithThrow);
assertEq(wrapWithThrow.throw(1), 1);
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -111,6 +111,7 @@ class GlobalObject : public NativeObject {
ASYNC_GENERATOR_PROTO,
MAP_ITERATOR_PROTO,
SET_ITERATOR_PROTO,
WRAP_FOR_VALID_ITERATOR_PROTO,
MODULE_PROTO,
IMPORT_ENTRY_PROTO,
EXPORT_ENTRY_PROTO,
@ -726,6 +727,13 @@ class GlobalObject : public NativeObject {
return &global->getConstructor(JSProto_Promise).toObject();
}
static NativeObject* getOrCreateWrapForValidIteratorPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
return MaybeNativeObject(getOrCreateObject(cx, global,
WRAP_FOR_VALID_ITERATOR_PROTO,
initWrapForValidIteratorProto));
}
static NativeObject* getIntrinsicsHolder(JSContext* cx,
Handle<GlobalObject*> global);
@ -830,6 +838,8 @@ class GlobalObject : public NativeObject {
Handle<GlobalObject*> global);
static bool initRegExpStringIteratorProto(JSContext* cx,
Handle<GlobalObject*> global);
static bool initWrapForValidIteratorProto(JSContext*,
Handle<GlobalObject*> global);
// Implemented in vm/AsyncIteration.cpp.
static bool initAsyncIteratorProto(JSContext* cx,

Просмотреть файл

@ -1505,7 +1505,8 @@ bool js::SuppressDeletedElement(JSContext* cx, HandleObject obj,
return SuppressDeletedPropertyHelper(cx, obj, str);
}
static const JSFunctionSpec iterator_static_methods[] = {JS_FS_END};
static const JSFunctionSpec iterator_static_methods[] = {
JS_SELF_HOSTED_FN("from", "IteratorFrom", 1, 0), JS_FS_END};
static const JSFunctionSpec iterator_proto_methods[] = {
JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0), JS_FS_END};
@ -1670,3 +1671,54 @@ const JSClass IteratorObject::protoClass_ = {
JS_NULL_CLASS_OPS,
&IteratorObjectClassSpec,
};
// Set up WrapForValidIteratorObject class and its prototype.
static const JSFunctionSpec wrap_for_valid_iterator_methods[] = {
JS_SELF_HOSTED_FN("next", "WrapForValidIteratorNext", 1, 0),
JS_SELF_HOSTED_FN("return", "WrapForValidIteratorReturn", 1, 0),
JS_SELF_HOSTED_FN("throw", "WrapForValidIteratorThrow", 1, 0),
JS_FS_END,
};
static const JSClass WrapForValidIteratorPrototypeClass = {
"Wrap For Valid Iterator", 0};
const JSClass WrapForValidIteratorObject::class_ = {
"Wrap For Valid Iterator",
JSCLASS_HAS_RESERVED_SLOTS(WrapForValidIteratorObject::SlotCount),
};
/* static */
bool GlobalObject::initWrapForValidIteratorProto(JSContext* cx,
Handle<GlobalObject*> global) {
if (global->getReservedSlot(WRAP_FOR_VALID_ITERATOR_PROTO).isObject()) {
return true;
}
RootedObject iteratorProto(
cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
if (!iteratorProto) {
return false;
}
const JSClass* cls = &WrapForValidIteratorPrototypeClass;
RootedObject proto(
cx, GlobalObject::createBlankPrototypeInheriting(cx, cls, iteratorProto));
if (!proto || !DefinePropertiesAndFunctions(
cx, proto, nullptr, wrap_for_valid_iterator_methods)) {
return false;
}
global->setReservedSlot(WRAP_FOR_VALID_ITERATOR_PROTO, ObjectValue(*proto));
return true;
}
WrapForValidIteratorObject* js::NewWrapForValidIterator(JSContext* cx) {
RootedObject proto(cx, GlobalObject::getOrCreateWrapForValidIteratorPrototype(
cx, cx->global()));
if (!proto) {
return nullptr;
}
return NewObjectWithGivenProto<WrapForValidIteratorObject>(cx, proto);
}

Просмотреть файл

@ -14,6 +14,7 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/MemoryReporting.h"
#include "builtin/SelfHostingDefines.h"
#include "gc/Barrier.h"
#include "vm/ReceiverGuard.h"
#include "vm/Stack.h"
@ -442,6 +443,23 @@ class IteratorObject : public NativeObject {
static const JSClass protoClass_;
};
/*
* Wrapper for iterators created via Iterator.from.
* Iterator Helpers proposal 2.1.3.3.1.1.
*/
class WrapForValidIteratorObject : public NativeObject {
public:
static const JSClass class_;
enum { IteratedSlot, SlotCount };
static_assert(
IteratedSlot == ITERATED_SLOT,
"IteratedSlot must match self-hosting define for iterated object slot.");
};
WrapForValidIteratorObject* NewWrapForValidIterator(JSContext* cx);
} /* namespace js */
#endif /* vm_Iteration_h */

Просмотреть файл

@ -2060,6 +2060,20 @@ static bool intrinsic_ToBigInt(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
static bool intrinsic_NewWrapForValidIterator(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0);
JSObject* obj = NewWrapForValidIterator(cx);
if (!obj) {
return false;
}
args.rval().setObject(*obj);
return true;
}
// The self-hosting global isn't initialized with the normal set of builtins.
// Instead, individual C++-implemented functions that're required by
// self-hosted code are defined as global functions. Accessing these
@ -2209,6 +2223,9 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("GuardToRegExpStringIterator",
intrinsic_GuardToBuiltin<RegExpStringIteratorObject>, 1, 0,
IntrinsicGuardToRegExpStringIterator),
JS_INLINABLE_FN("GuardToWrapForValidIterator",
intrinsic_GuardToBuiltin<WrapForValidIteratorObject>, 1, 0,
IntrinsicGuardToWrapForValidIterator),
JS_FN("_CreateMapIterationResultPair",
intrinsic_CreateMapIterationResultPair, 0, 0),
@ -2516,6 +2533,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("ToBigInt", intrinsic_ToBigInt, 1, 0),
JS_FN("NewWrapForValidIterator", intrinsic_NewWrapForValidIterator, 0, 0),
JS_FS_END};
void js::FillSelfHostingCompileOptions(CompileOptions& options) {