blob: b711c289715c747f3adafeb1ebde5429f4bc468e [file]
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
namespace iterator {
macro NewJSValidIteratorWrapper(implicit context: Context)(
underlying: IteratorRecord): JSValidIteratorWrapper {
return new JSValidIteratorWrapper{
map: *NativeContextSlot(ContextSlot::VALID_ITERATOR_WRAPPER_MAP_INDEX),
properties_or_hash: kEmptyFixedArray,
elements: kEmptyFixedArray,
underlying: underlying
};
}
// https://tc39.es/proposal-iterator-helpers/#sec-getiteratorflattenable
//
// Currently never used with the async hint, so only the sync path is
// implemented.
transitioning macro GetIteratorFlattenable(implicit context: Context)(
obj: JSReceiver): IteratorRecord {
try {
// 1. If obj is not an Object, throw a TypeError exception.
// (Done by caller.)
// 2. Let alreadyAsync be false.
//
// (Unimplemented because the async path is unused.)
// 3. Let method be undefined.
//
// (Done below.)
// 4. If hint is async, then
// a. Set method to ? Get(obj, @@asyncIterator).
// b. Set alreadyAsync to true.
//
// (Unimplemented because unused.)
// 5. If IsCallable(method) is false, then
// a. Set method to ? Get(obj, @@iterator).
const method = GetProperty(obj, IteratorSymbolConstant());
// b. Set alreadyAsync to false.
//
// (Unimplemented because unused.)
let iterator: JSAny;
// 6. If IsCallable(method) is false, then
if (!Is<Callable>(method)) {
// a. Let iterator be obj.
iterator = obj;
// b. Set alreadyAsync to true.
//
// (Unimplemented because unused.)
} else {
// 7. Else,
// a. Let iterator be ? Call(method, obj).
iterator = Call(context, UnsafeCast<Callable>(method), obj);
}
// 8. If iterator is not an Object, throw a TypeError exception.
const iteratorObj = Cast<JSReceiver>(iterator)
otherwise goto IteratorNotObject(obj, method);
// 9. Let nextMethod be ? Get(iterator, "next").
const nextMethod = GetProperty(iteratorObj, kNextString);
// 10. If IsCallable(nextMethod) is false, throw a TypeError exception.
if (!Is<Callable>(nextMethod)) {
ThrowTypeError(
MessageTemplate::kPropertyNotFunction, nextMethod, kNextString, obj);
}
// 11. Let iteratorRecord be the Iterator Record { [[Iterator]]: iterator,
// [[NextMethod]]: nextMethod, [[Done]]: false }.
const iteratorRecord =
IteratorRecord{object: iteratorObj, next: nextMethod};
// 12. If hint is async and alreadyAsync is false, then
// a. Return CreateAsyncFromSyncIterator(iteratorRecord).
//
// (Unimplemented because unused.)
// 13. Return iteratorRecord.
return iteratorRecord;
} label IteratorNotObject(obj: JSAny, method: JSAny) deferred {
if (Is<Callable>(method)) {
ThrowTypeError(MessageTemplate::kSymbolIteratorInvalid);
} else {
ThrowTypeError(MessageTemplate::kNotIterable, obj);
}
}
}
// https://tc39.es/proposal-iterator-helpers/#sec-iterator.from
transitioning javascript builtin IteratorFrom(
js-implicit context: NativeContext,
receiver: JSAny)(objArg: JSAny): JSReceiver {
// 1. If O is a String, set O to ! ToObject(O).
let obj: JSReceiver;
typeswitch (objArg) {
case (o: String): {
obj = ToObject_Inline(context, o);
}
case (o: JSReceiver): {
obj = o;
}
case (JSAny): {
ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'Iterator.from');
}
}
// 2. Let iteratorRecord be ? GetIteratorFlattenable(O, sync).
const iteratorRecord = GetIteratorFlattenable(obj);
// 3. Let hasInstance be ? OrdinaryHasInstance(%Iterator%,
// iteratorRecord.[[Iterator]]).
const hasInstance = function::OrdinaryHasInstance(
context, GetIteratorFunction(), iteratorRecord.object);
// 4. If hasInstance is true, then
if (hasInstance == True) {
// a. Return iteratorRecord.[[Iterator]].
return iteratorRecord.object;
}
// 5. Let wrapper be OrdinaryObjectCreate(%WrapForValidIteratorPrototype%, «
// [[Iterated]] »).
// 6. Set wrapper.[[Iterated]] to iteratorRecord.
// 7. Return wrapper.
return NewJSValidIteratorWrapper(iteratorRecord);
}
// https://tc39.es/proposal-iterator-helpers/#sec-wrapforvaliditeratorprototype.next
transitioning javascript builtin WrapForValidIteratorPrototypeNext(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
// 1. Let O be this value.
// 2. Perform ? RequireInternalSlot(O, [[Iterated]]).
const o = Cast<JSValidIteratorWrapper>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver,
'%WrapForValidIteratorPrototype%.next', receiver);
// 3. Let iteratorRecord be O.[[Iterated]].
const iteratorRecord = o.underlying;
// 4. Return ? Call(iteratorRecord.[[NextMethod]],
// iteratorRecord.[[Iterator]]).
return Call(context, iteratorRecord.next, iteratorRecord.object);
}
// https://tc39.es/proposal-iterator-helpers/#sec-wrapforvaliditeratorprototype.return
transitioning javascript builtin WrapForValidIteratorPrototypeReturn(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
try {
// 1. Let O be this value.
// 2. Perform ? RequireInternalSlot(O, [[Iterated]]).
const o = Cast<JSValidIteratorWrapper>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver,
'%WrapForValidIteratorPrototype%.return', receiver);
// 3. Let iterator be O.[[Iterated]].[[Iterator]].
const iterator = o.underlying.object;
// 4. Assert: iterator is an Object.
// 5. Let returnMethod be ? GetMethod(iterator, "return").
const returnMethod =
GetMethod(iterator, kReturnString) otherwise ReturnMethodUndefined;
// 7. Return ? Call(returnMethod, iterator).
return Call(context, returnMethod, iterator);
} label ReturnMethodUndefined {
// 6. If returnMethod is undefined, then
// a. Return CreateIterResultObject(undefined, true).
return AllocateJSIteratorResult(Undefined, True);
}
}
} // namespace iterator