From 2ec730bed4751a8957ae6d7e872e1b81895f176c Mon Sep 17 00:00:00 2001 From: Seonghyun Kim Date: Wed, 7 Aug 2024 18:24:16 +0900 Subject: [PATCH] Implement basic of Iterator helper and Iterator.prototype.map Signed-off-by: Seonghyun Kim --- src/builtins/BuiltinIterator.cpp | 279 ++++++++++++++++++++++++ src/runtime/GlobalObject.cpp | 34 --- src/runtime/GlobalObject.h | 7 +- src/runtime/IteratorObject.cpp | 74 +++++++ src/runtime/IteratorObject.h | 64 ++++++ src/runtime/PointerValue.h | 17 ++ src/runtime/StaticStrings.h | 1 + test/vendortest | 2 +- tools/test/test262/excludelist.orig.xml | 34 --- tools/test/v8/v8.mjsunit.status | 2 + 10 files changed, 444 insertions(+), 70 deletions(-) create mode 100644 src/builtins/BuiltinIterator.cpp diff --git a/src/builtins/BuiltinIterator.cpp b/src/builtins/BuiltinIterator.cpp new file mode 100644 index 000000000..d4cc13184 --- /dev/null +++ b/src/builtins/BuiltinIterator.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2024-present Samsung Electronics Co., Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "Escargot.h" +#include "runtime/GlobalObject.h" +#include "runtime/Context.h" +#include "runtime/VMInstance.h" +#include "runtime/NativeFunctionObject.h" +#include "runtime/IteratorObject.h" + +namespace Escargot { + +// https://tc39.es/proposal-iterator-helpers/#sec-iterator-constructor +static Value builtinIteratorConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional newTarget) +{ + // If NewTarget is undefined or the active function object, throw a TypeError exception. + if (!newTarget) { + ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_ConstructorRequiresNew); + } + if (newTarget->isFunctionObject() && newTarget->asFunctionObject()->codeBlock()->context()->globalObject()->iterator() == newTarget.value()) { + ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Abstract class Iterator is not constructable"); + } + + // Return ? OrdinaryCreateFromConstructor(NewTarget, "%Iterator.prototype%"). + Object* proto = Object::getPrototypeFromConstructor(state, newTarget.value(), [](ExecutionState& state, Context* constructorRealm) -> Object* { + return constructorRealm->globalObject()->iteratorPrototype(); + }); + return new Object(state, proto); +} + +#define RESOLVE_THIS_BINDING_TO_WRAP_FOR_VALID_ITERATOR(NAME, OBJ, BUILT_IN_METHOD) \ + if (!thisValue.isObject() || !thisValue.asObject()->isWrapForValidIteratorObject()) { \ + ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().OBJ.string(), true, state.context()->staticStrings().BUILT_IN_METHOD.string(), ErrorObject::Messages::GlobalObject_CalledOnIncompatibleReceiver); \ + } \ + WrapForValidIteratorObject* NAME = thisValue.asObject()->asWrapForValidIteratorObject(); + +// https://tc39.es/proposal-iterator-helpers/#sec-wrapforvaliditeratorprototype.next +static Value builtinWrapForValidIteratorPrototypeNext(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional newTarget) +{ + // Let O be this value. + // Perform ? RequireInternalSlot(O, [[Iterated]]). + RESOLVE_THIS_BINDING_TO_WRAP_FOR_VALID_ITERATOR(O, Iterator, next); + + // Let iteratorRecord be O.[[Iterated]]. + IteratorRecord* iteratorRecord = O->iterated(); + // Return ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]). + return Object::call(state, iteratorRecord->m_nextMethod, iteratorRecord->m_iterator, 0, nullptr); +} + +// https://tc39.es/proposal-iterator-helpers/#sec-wrapforvaliditeratorprototype.return +static Value builtinWrapForValidIteratorPrototypeReturn(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional newTarget) +{ + // Let O be this value. + // Perform ? RequireInternalSlot(O, [[Iterated]]). + RESOLVE_THIS_BINDING_TO_WRAP_FOR_VALID_ITERATOR(O, Iterator, stringReturn); + + // Let iterator be O.[[Iterated]].[[Iterator]]. + // Assert: iterator is an Object. + auto iterator = O->iterated()->m_iterator; + + // Let returnMethod be ? GetMethod(iterator, "return"). + auto returnMethod = Object::getMethod(state, iterator, state.context()->staticStrings().stringReturn); + + // If returnMethod is undefined, then + if (returnMethod.isUndefined()) { + // Return CreateIterResultObject(undefined, true). + return IteratorObject::createIterResultObject(state, Value(), true); + } + // Return ? Call(returnMethod, iterator). + return Object::call(state, returnMethod, iterator, 0, nullptr); +} + +#define RESOLVE_THIS_BINDING_TO_ITERATOR_HELPER(NAME, OBJ, BUILT_IN_METHOD) \ + if (!thisValue.isObject() || !thisValue.asObject()->isIteratorHelperObject()) { \ + ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().OBJ.string(), true, state.context()->staticStrings().BUILT_IN_METHOD.string(), ErrorObject::Messages::GlobalObject_CalledOnIncompatibleReceiver); \ + } \ + IteratorHelperObject* NAME = thisValue.asObject()->asIteratorObject()->asIteratorHelperObject(); + +// https://tc39.es/proposal-iterator-helpers/#sec-%iteratorhelperprototype%.next +static Value builtinIteratorHelperPrototypeNext(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional newTarget) +{ + // Return ? GeneratorResume(this value, undefined, "Iterator Helper"). + RESOLVE_THIS_BINDING_TO_ITERATOR_HELPER(obj, Iterator, next); + return obj->next(state); +} + +// https://tc39.es/proposal-iterator-helpers/#sec-wrapforvaliditeratorprototype.return +static Value builtinIteratorHelperPrototypeReturn(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional newTarget) +{ + // Let O be this value. + // Perform ? RequireInternalSlot(O, [[UnderlyingIterator]]). + RESOLVE_THIS_BINDING_TO_ITERATOR_HELPER(O, Iterator, stringReturn); + // Assert: O has a [[GeneratorState]] slot. + // If O.[[GeneratorState]] is suspended-start, then + if (!O->underlyingIterator()->m_done) { + // Set O.[[GeneratorState]] to completed. + O->underlyingIterator()->m_done = true; + // NOTE: Once a generator enters the completed state it never leaves it and its associated execution context is never resumed. Any execution state associated with O can be discarded at this point. + // Perform ? IteratorClose(O.[[UnderlyingIterator]], NormalCompletion(unused)). + IteratorObject::iteratorClose(state, O->underlyingIterator(), Value(), false); + // Return CreateIterResultObject(undefined, true). + return IteratorObject::createIterResultObject(state, Value(), true); + } + // Let C be Completion { [[Type]]: return, [[Value]]: undefined, [[Target]]: empty }. + // Return ? GeneratorResumeAbrupt(O, C, "Iterator Helper"). + return IteratorObject::createIterResultObject(state, Value(), true); +} + +// https://tc39.es/proposal-iterator-helpers/#sec-getiteratordirect +IteratorRecord* getIteratorDirect(ExecutionState& state, Object* obj) +{ + // Let nextMethod be ? Get(obj, "next"). + Value nextMethod = obj->get(state, ObjectPropertyName(state.context()->staticStrings().next)).value(state, obj); + // Let iteratorRecord be Record { [[Iterator]]: obj, [[NextMethod]]: nextMethod, [[Done]]: false }. + IteratorRecord* record = new IteratorRecord(obj, nextMethod, false); + // Return iteratorRecord. + return record; +} + +struct IteratorMapData : public gc { + StorePositiveNumberAsOddNumber counter; + Value mapper; + + IteratorMapData(Value mapper) + : counter(0) + , mapper(mapper) + { + } +}; + +static std::pair iteratorMapClosure(ExecutionState& state, IteratorHelperObject* obj, void* data) +{ + // Let closure be a new Abstract Closure with no parameters that captures iterated and mapper and performs the following steps when called: + // Let counter be 0. + // Repeat, + // Let value be ? IteratorStepValue(iterated). + // If value is done, return undefined. + // Let mapped be Completion(Call(mapper, undefined, « value, 𝔽(counter) »)). + // IfAbruptCloseIterator(mapped, iterated). + // Let completion be Completion(Yield(mapped)). + // IfAbruptCloseIterator(completion, iterated). + // Set counter to counter + 1. + IteratorMapData* closureData = reinterpret_cast(data); + IteratorRecord* iterated = obj->underlyingIterator(); + size_t counter = closureData->counter; + auto value = IteratorObject::iteratorStepValue(state, iterated); + if (!value) { + iterated->m_done = true; + return std::make_pair(Value(), true); + } + Value argv[2] = { value.value(), Value(closureData->counter) }; + Value mapped; + try { + mapped = Object::call(state, closureData->mapper, Value(), 2, argv); + } catch (const Value& e) { + IteratorObject::iteratorClose(state, iterated, e, true); + } + closureData->counter = StorePositiveNumberAsOddNumber(counter + 1); + return std::make_pair(mapped, false); +} + +// https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.map +static Value builtinIteratorMap(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional newTarget) +{ + // Let O be the this value. + const Value& O = thisValue; + // If O is not an Object, throw a TypeError exception. + if (!O.isObject()) { + ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "this value is not Object"); + } + // If IsCallable(mapper) is false, throw a TypeError exception. + const Value& mapper = argv[0]; + if (!mapper.isCallable()) { + ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "mapper is not callable"); + } + + // Let iterated be ? GetIteratorDirect(O). + IteratorRecord* iterated = getIteratorDirect(state, O.asObject()); + // Let result be CreateIteratorFromClosure(closure, "Iterator Helper", %IteratorHelperPrototype%, « [[UnderlyingIterator]] »). + // Set result.[[UnderlyingIterator]] to iterated. + IteratorHelperObject* result = new IteratorHelperObject(state, iteratorMapClosure, iterated, new IteratorMapData(mapper)); + // Return result. + return result; +} + +static Value builtinGenericIteratorNext(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional newTarget) +{ + if (!thisValue.isObject() || !thisValue.asObject()->isGenericIteratorObject()) { + ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, String::fromASCII("Iterator"), true, state.context()->staticStrings().next.string(), ErrorObject::Messages::GlobalObject_CalledOnIncompatibleReceiver); + } + + IteratorObject* iter = thisValue.asObject()->asIteratorObject(); + return iter->next(state); +} + +void GlobalObject::initializeIterator(ExecutionState& state) +{ + installIterator(state); +} + +void GlobalObject::installIterator(ExecutionState& state) +{ + const StaticStrings* strings = &state.context()->staticStrings(); + + m_asyncIteratorPrototype = new PrototypeObject(state); + m_asyncIteratorPrototype->setGlobalIntrinsicObject(state, true); + // https://www.ecma-international.org/ecma-262/10.0/index.html#sec-asynciteratorprototype-asynciterator + m_asyncIteratorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().asyncIterator), + ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(AtomicString(state, String::fromASCII("[Symbol.asyncIterator]")), builtinSpeciesGetter, 0, NativeFunctionInfo::Strict)), + (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); + + m_iteratorPrototype = new PrototypeObject(state); + m_iteratorPrototype->setGlobalIntrinsicObject(state, true); + // https://www.ecma-international.org/ecma-262/10.0/index.html#sec-%iteratorprototype%-@@iterator + m_iteratorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().iterator), + ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(AtomicString(state, String::fromASCII("[Symbol.iterator]")), builtinSpeciesGetter, 0, NativeFunctionInfo::Strict)), + (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); + m_iteratorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, Value(state.context()->vmInstance()->globalSymbols().toStringTag)), + ObjectPropertyDescriptor(Value(strings->Iterator.string()), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); + + m_genericIteratorPrototype = new PrototypeObject(state, m_iteratorPrototype); + m_genericIteratorPrototype->setGlobalIntrinsicObject(state, true); + + m_genericIteratorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->next), + ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->next, builtinGenericIteratorNext, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); + m_genericIteratorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().toStringTag), + ObjectPropertyDescriptor(Value(String::fromASCII("Iterator")), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent))); + + m_iterator = new NativeFunctionObject(state, NativeFunctionInfo(strings->Iterator, builtinIteratorConstructor, 0), NativeFunctionObject::__ForBuiltinConstructor__); + m_iterator->setGlobalIntrinsicObject(state); + + // https://tc39.es/proposal-iterator-helpers/#sec-iterator.prototype + m_iterator->setFunctionPrototype(state, m_iteratorPrototype); + + // https://tc39.es/proposal-iterator-helpers/#sec-wrapforvaliditeratorprototype-object + m_wrapForValidIteratorPrototype = new Object(state, m_iteratorPrototype); + m_wrapForValidIteratorPrototype->setGlobalIntrinsicObject(state, true); + + m_wrapForValidIteratorPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->next), + ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->next, builtinWrapForValidIteratorPrototypeNext, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); + + m_wrapForValidIteratorPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->stringReturn), + ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->stringReturn, builtinWrapForValidIteratorPrototypeReturn, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); + + m_iteratorHelperPrototype = new Object(state, m_iteratorPrototype); + m_iteratorHelperPrototype->setGlobalIntrinsicObject(state, true); + + m_iteratorHelperPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().toStringTag), + ObjectPropertyDescriptor(String::fromASCII("Iterator Helper"), ObjectPropertyDescriptor::ConfigurablePresent)); + + m_iteratorHelperPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->next), + ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->next, builtinIteratorHelperPrototypeNext, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); + + m_iteratorHelperPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->stringReturn), + ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->stringReturn, builtinIteratorHelperPrototypeReturn, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); + + m_iteratorPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->map), + ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->map, builtinIteratorMap, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); + + directDefineOwnProperty(state, ObjectPropertyName(strings->Iterator), + ObjectPropertyDescriptor(m_iterator, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); +} +} // namespace Escargot diff --git a/src/runtime/GlobalObject.cpp b/src/runtime/GlobalObject.cpp index ca91e6976..93975ca1f 100644 --- a/src/runtime/GlobalObject.cpp +++ b/src/runtime/GlobalObject.cpp @@ -834,16 +834,6 @@ static Value builtinArrayToString(ExecutionState& state, Value thisValue, size_t return Object::call(state, toString, thisObject, 0, nullptr); } -static Value builtinGenericIteratorNext(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional newTarget) -{ - if (!thisValue.isObject() || !thisValue.asObject()->isGenericIteratorObject()) { - ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, String::fromASCII("Iterator"), true, state.context()->staticStrings().next.string(), ErrorObject::Messages::GlobalObject_CalledOnIncompatibleReceiver); - } - - IteratorObject* iter = thisValue.asObject()->asIteratorObject(); - return iter->next(state); -} - void GlobalObject::initializeOthers(ExecutionState& state) { // Other prerequisite builtins should be installed at the start time @@ -904,7 +894,6 @@ void GlobalObject::installOthers(ExecutionState& state) NativeFunctionInfo(strings->unescape, builtinUnescape, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); - // shared builtin methods m_parseInt = new NativeFunctionObject(state, NativeFunctionInfo(strings->parseInt, builtinParseInt, 2, NativeFunctionInfo::Strict)); defineOwnProperty(state, ObjectPropertyName(strings->parseInt), ObjectPropertyDescriptor(m_parseInt, @@ -917,29 +906,6 @@ void GlobalObject::installOthers(ExecutionState& state) m_arrayToString = new NativeFunctionObject(state, NativeFunctionInfo(strings->toString, builtinArrayToString, 0, NativeFunctionInfo::Strict)); - // shared builtin objects - m_asyncIteratorPrototype = new PrototypeObject(state); - m_asyncIteratorPrototype->setGlobalIntrinsicObject(state, true); - // https://www.ecma-international.org/ecma-262/10.0/index.html#sec-asynciteratorprototype-asynciterator - m_asyncIteratorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().asyncIterator), - ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(AtomicString(state, String::fromASCII("[Symbol.asyncIterator]")), builtinSpeciesGetter, 0, NativeFunctionInfo::Strict)), - (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); - - m_iteratorPrototype = new PrototypeObject(state); - m_iteratorPrototype->setGlobalIntrinsicObject(state, true); - // https://www.ecma-international.org/ecma-262/10.0/index.html#sec-%iteratorprototype%-@@iterator - m_iteratorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().iterator), - ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(AtomicString(state, String::fromASCII("[Symbol.iterator]")), builtinSpeciesGetter, 0, NativeFunctionInfo::Strict)), - (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); - - m_genericIteratorPrototype = new PrototypeObject(state, m_iteratorPrototype); - m_genericIteratorPrototype->setGlobalIntrinsicObject(state, true); - - m_genericIteratorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().next), - ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().next, builtinGenericIteratorNext, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); - m_genericIteratorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().toStringTag), - ObjectPropertyDescriptor(Value(String::fromASCII("Iterator")), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent))); - #if defined(ESCARGOT_ENABLE_TEST) AtomicString isFunctionAllocatedOnStackFunctionName(state, "isFunctionAllocatedOnStack"); defineOwnProperty(state, ObjectPropertyName(isFunctionAllocatedOnStackFunctionName), diff --git a/src/runtime/GlobalObject.h b/src/runtime/GlobalObject.h index 5f740b83d..00bb64b9f 100644 --- a/src/runtime/GlobalObject.h +++ b/src/runtime/GlobalObject.h @@ -242,6 +242,10 @@ class FunctionObject; F(finalizationRegistryPrototype, Object, objName) #define GLOBALOBJECT_BUILTIN_TEMPORAL(F, objName) \ F(temporal, Object, objName) +#define GLOBALOBJECT_BUILTIN_ITERATOR(F, objName) \ + F(iterator, FunctionObject, objName) \ + F(wrapForValidIteratorPrototype, Object, objName) \ + F(iteratorHelperPrototype, Object, objName) #if defined(ENABLE_THREADING) #define GLOBALOBJECT_BUILTIN_ATOMICS(F, objName) \ @@ -282,9 +286,11 @@ class FunctionObject; F(DATAVIEW, DataView, ARG) \ F(DATE, Date, ARG) \ F(ERROR, Error, ARG) \ + F(FINALIZATIONREGISTRY, FinalizationRegistry, ARG) \ F(FUNCTION, Function, ARG) \ F(GENERATOR, Generator, ARG) \ F(INTL, Intl, ARG) \ + F(ITERATOR, Iterator, ARG) \ F(JSON, JSON, ARG) \ F(MAP, Map, ARG) \ F(MATH, Math, ARG) \ @@ -305,7 +311,6 @@ class FunctionObject; F(WEAKMAP, WeakMap, ARG) \ F(WEAKSET, WeakSet, ARG) \ F(WEAKREF, WeakRef, ARG) \ - F(FINALIZATIONREGISTRY, FinalizationRegistry, ARG) \ F(WASM, WebAssembly, ARG) diff --git a/src/runtime/IteratorObject.cpp b/src/runtime/IteratorObject.cpp index ba6e93c0e..8eb39ae8f 100644 --- a/src/runtime/IteratorObject.cpp +++ b/src/runtime/IteratorObject.cpp @@ -135,6 +135,31 @@ Optional IteratorObject::iteratorStep(ExecutionState& state, IteratorRe return done ? nullptr : result; } +// https://tc39.es/ecma262/#sec-iteratorstepvalue +Optional IteratorObject::iteratorStepValue(ExecutionState& state, IteratorRecord* iteratorRecord) +{ + // Let result be ? IteratorStep(iteratorRecord). + auto result = iteratorStep(state, iteratorRecord); + // If result is done, then + if (!result) { + // Return done. + return nullptr; + } + + Value value; + // Let value be Completion(IteratorValue(result)). + try { + value = iteratorValue(state, result.value()); + } catch (const Value& e) { + // If value is a throw completion, then + // Set iteratorRecord.[[Done]] to true. + iteratorRecord->m_done = true; + throw e; + } + // Return ? value. + return value; +} + // https://www.ecma-international.org/ecma-262/10.0/#sec-iteratorclose Value IteratorObject::iteratorClose(ExecutionState& state, IteratorRecord* iteratorRecord, const Value& completionValue, bool hasThrowOnCompletionType) { @@ -326,4 +351,53 @@ IteratorObject::KeyedGroupVector IteratorObject::groupBy(ExecutionState& state, } } +IteratorHelperObject::IteratorHelperObject(ExecutionState& state, IteratorHelperObjectCallback callback, + IteratorRecord* underlyingIterator, void* data) + : IteratorObject(state, state.context()->globalObject()->iteratorHelperPrototype()) + , m_isRunning(false) + , m_callback(callback) + , m_underlyingIterator(underlyingIterator) + , m_data(data) +{ +} + +void* IteratorHelperObject::operator new(size_t size) +{ + static MAY_THREAD_LOCAL bool typeInited = false; + static MAY_THREAD_LOCAL GC_descr descr; + if (!typeInited) { + GC_word obj_bitmap[GC_BITMAP_SIZE(IteratorHelperObject)] = { 0 }; + IteratorObject::fillGCDescriptor(obj_bitmap); + GC_set_bit(obj_bitmap, GC_WORD_OFFSET(IteratorHelperObject, m_data)); + GC_set_bit(obj_bitmap, GC_WORD_OFFSET(IteratorHelperObject, m_underlyingIterator)); + descr = GC_make_descriptor(obj_bitmap, GC_WORD_LEN(IteratorHelperObject)); + typeInited = true; + } + return GC_MALLOC_EXPLICITLY_TYPED(size, descr); +} + +std::pair IteratorHelperObject::advance(ExecutionState& state) +{ + struct IteratorHelperObjectRunningStateChanger { + IteratorHelperObject& obj; + IteratorHelperObjectRunningStateChanger(IteratorHelperObject& obj) + : obj(obj) + { + ASSERT(!obj.m_isRunning); + obj.m_isRunning = true; + } + ~IteratorHelperObjectRunningStateChanger() + { + obj.m_isRunning = false; + } + }; + + if (m_isRunning) { + ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "You cannot call Iterator helper advance function recursively"); + } + + IteratorHelperObjectRunningStateChanger changeRunningState(*this); + return m_callback(state, this, m_data); +} + } // namespace Escargot diff --git a/src/runtime/IteratorObject.h b/src/runtime/IteratorObject.h index 66f77a44d..1f75372a9 100644 --- a/src/runtime/IteratorObject.h +++ b/src/runtime/IteratorObject.h @@ -29,6 +29,7 @@ class StringIteratorObject; class RegExpStringIteratorObject; class MapIteratorObject; class SetIteratorObject; +class IteratorHelperObject; class IteratorObject; class IteratorRecord : public PointerValue { @@ -90,6 +91,12 @@ class IteratorObject : public DerivedObject { return (SetIteratorObject*)this; } + IteratorHelperObject* asIteratorHelperObject() + { + ASSERT(isIteratorHelperObject()); + return (IteratorHelperObject*)this; + } + Value next(ExecutionState& state); virtual std::pair advance(ExecutionState& state) = 0; @@ -100,6 +107,8 @@ class IteratorObject : public DerivedObject { static Value iteratorValue(ExecutionState& state, Object* iterResult); // this function return empty Optional value instead of Value(false) static Optional iteratorStep(ExecutionState& state, IteratorRecord* iteratorRecord); + // return null option value when iterator done + static Optional iteratorStepValue(ExecutionState& state, IteratorRecord* iteratorRecord); static Value iteratorClose(ExecutionState& state, IteratorRecord* iteratorRecord, const Value& completionValue, bool hasThrowOnCompletionType); static Object* createIterResultObject(ExecutionState& state, const Value& value, bool done); // https://www.ecma-international.org/ecma-262/10.0/#sec-iterabletolist @@ -117,6 +126,61 @@ class IteratorObject : public DerivedObject { typedef Vector> KeyedGroupVector; static KeyedGroupVector groupBy(ExecutionState& state, const Value& items, const Value& callbackfn, GroupByKeyCoercion keyCoercion); }; + +class IteratorHelperObject : public IteratorObject { +public: + typedef std::pair (*IteratorHelperObjectCallback)(ExecutionState& state, IteratorHelperObject* obj, void* data); + IteratorHelperObject(ExecutionState& state, IteratorHelperObjectCallback callback, IteratorRecord* underlyingIterator, void* data); + + virtual bool isIteratorHelperObject() const override + { + return true; + } + + virtual std::pair advance(ExecutionState& state) override; + + void* operator new(size_t size); + void* operator new[](size_t size) = delete; + + bool isRunning() const + { + return m_isRunning; + } + + IteratorRecord* underlyingIterator() const + { + return m_underlyingIterator; + } + +private: + bool m_isRunning; + IteratorHelperObjectCallback m_callback; + IteratorRecord* m_underlyingIterator; + void* m_data; +}; + +class WrapForValidIteratorObject : public DerivedObject { +public: + explicit WrapForValidIteratorObject(ExecutionState& state, Object* proto, IteratorRecord* iterated) + : DerivedObject(state, proto) + , m_iterated(iterated) + { + } + + virtual bool isWrapForValidIteratorObject() const override + { + return true; + } + + IteratorRecord* iterated() const + { + return m_iterated; + } + +private: + IteratorRecord* m_iterated; +}; + } // namespace Escargot #endif diff --git a/src/runtime/PointerValue.h b/src/runtime/PointerValue.h index 0b0e57f8d..650e46030 100644 --- a/src/runtime/PointerValue.h +++ b/src/runtime/PointerValue.h @@ -59,6 +59,7 @@ class DoubleInEncodedValue; class JSGetterSetter; class IteratorRecord; class IteratorObject; +class WrapForValidIteratorObject; class GenericIteratorObject; class MapObject; class SetObject; @@ -471,11 +472,21 @@ class PointerValue : public gc { return false; } + virtual bool isWrapForValidIteratorObject() const + { + return false; + } + virtual bool isGenericIteratorObject() const { return false; } + virtual bool isIteratorHelperObject() const + { + return false; + } + virtual bool isEnumerateObject() const { return false; @@ -919,6 +930,12 @@ class PointerValue : public gc { return (IteratorObject*)this; } + WrapForValidIteratorObject* asWrapForValidIteratorObject() + { + ASSERT(isWrapForValidIteratorObject()); + return (WrapForValidIteratorObject*)this; + } + GenericIteratorObject* asGenericIteratorObject() { ASSERT(isGenericIteratorObject()); diff --git a/src/runtime/StaticStrings.h b/src/runtime/StaticStrings.h index b9b8862b3..bae8d48ef 100644 --- a/src/runtime/StaticStrings.h +++ b/src/runtime/StaticStrings.h @@ -69,6 +69,7 @@ namespace Escargot { F(Int32Array) \ F(Int8Array) \ F(Intl) \ + F(Iterator) \ F(JSON) \ F(LN10) \ F(LN2) \ diff --git a/test/vendortest b/test/vendortest index 61cecb1e9..6226beb94 160000 --- a/test/vendortest +++ b/test/vendortest @@ -1 +1 @@ -Subproject commit 61cecb1e9f5ef104582c15823e6dac943a96d866 +Subproject commit 6226beb943358ca644b2e8655c2cd6fe8633ff03 diff --git a/tools/test/test262/excludelist.orig.xml b/tools/test/test262/excludelist.orig.xml index c450126e6..f58a37215 100644 --- a/tools/test/test262/excludelist.orig.xml +++ b/tools/test/test262/excludelist.orig.xml @@ -381,40 +381,6 @@ TODO TODO TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO TODO TODO TODO diff --git a/tools/test/v8/v8.mjsunit.status b/tools/test/v8/v8.mjsunit.status index 86032fc0b..151c416d3 100644 --- a/tools/test/v8/v8.mjsunit.status +++ b/tools/test/v8/v8.mjsunit.status @@ -778,6 +778,8 @@ # outdated 'invalid-lhs': [SKIP], + 'es6/string-iterator': [SKIP], + 'es6/iterator-prototype': [SKIP], # can't set prototype of this object 'getter-in-prototype': [SKIP],