зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
c5a5efa985
Коммит
28738d7480
|
@ -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) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче