/* * Copyright (C) 2016 Canon, Inc. All rights reserved. * Copyright (C) 2016-2022 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY CANON INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CANON INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include "JSDOMConvert.h" #include #include #include namespace WebCore { void addValueIterableMethods(JSC::JSGlobalObject&, JSC::JSObject&); enum class JSDOMIteratorType { Set, Map }; // struct IteratorTraits { // static constexpr JSDOMIteratorType type = [Map|Set]; // using KeyType = [IDLType|void]; // using ValueType = [IDLType]; // }; template using EnableIfMap = typename std::enable_if::type; template using EnableIfSet = typename std::enable_if::type; template class JSDOMIteratorPrototype final : public JSC::JSNonFinalObject { public: using Base = JSC::JSNonFinalObject; using DOMWrapped = typename JSWrapper::DOMWrapped; template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) { STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSDOMIteratorPrototype, Base); return &vm.plainObjectSpace(); } static JSDOMIteratorPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) { STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSDOMIteratorPrototype, JSDOMIteratorPrototype::Base); JSDOMIteratorPrototype* prototype = new (NotNull, JSC::allocateCell(vm)) JSDOMIteratorPrototype(vm, structure); prototype->finishCreation(vm, globalObject); return prototype; } DECLARE_INFO; static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) { return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); } static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES next(JSC::JSGlobalObject*, JSC::CallFrame*); private: JSDOMIteratorPrototype(JSC::VM& vm, JSC::Structure* structure) : Base(vm, structure) { } void finishCreation(JSC::VM&, JSC::JSGlobalObject*); }; using IterationKind = JSC::IterationKind; template class JSDOMIteratorBase : public JSDOMObject { public: using Base = JSDOMObject; using Wrapper = JSWrapper; using Traits = IteratorTraits; using DOMWrapped = typename Wrapper::DOMWrapped; using Prototype = JSDOMIteratorPrototype; DECLARE_INFO; static Prototype* createPrototype(JSC::VM& vm, JSC::JSGlobalObject& globalObject) { return Prototype::create(vm, &globalObject, Prototype::createStructure(vm, &globalObject, globalObject.iteratorPrototype())); } JSC::JSValue next(JSC::JSGlobalObject&); static void createStructure(JSC::VM&, JSC::JSGlobalObject*, JSC::JSValue); // Make use of createStructure for this compile-error. protected: JSDOMIteratorBase(JSC::Structure* structure, JSWrapper& iteratedObject, IterationKind kind) : Base(structure, *iteratedObject.globalObject()) , m_iterator(iteratedObject.wrapped().createIterator()) , m_kind(kind) { } template EnableIfMap asJS(JSC::JSGlobalObject&, IteratorValue&); template EnableIfSet asJS(JSC::JSGlobalObject&, IteratorValue&); static void destroy(JSC::JSCell*); std::optional m_iterator; IterationKind m_kind; }; inline JSC::JSValue jsPair(JSC::JSGlobalObject&, JSDOMGlobalObject& globalObject, JSC::JSValue value1, JSC::JSValue value2) { JSC::MarkedArgumentBuffer arguments; arguments.append(value1); arguments.append(value2); ASSERT(!arguments.hasOverflowed()); return constructArray(&globalObject, static_cast(nullptr), arguments); } template inline JSC::JSValue jsPair(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject, const T& value1, const U& value2) { return jsPair(lexicalGlobalObject, globalObject, toJS(lexicalGlobalObject, globalObject, value1), toJS(lexicalGlobalObject, globalObject, value2)); } template JSC::JSValue iteratorCreate(typename JSIterator::Wrapper&, IterationKind); template JSC::JSValue iteratorForEach(JSC::JSGlobalObject&, JSC::CallFrame&, typename JSIterator::Wrapper&); template JSC::JSValue iteratorCreate(typename JSIterator::Wrapper& thisObject, IterationKind kind) { ASSERT(thisObject.globalObject()); JSDOMGlobalObject& globalObject = *thisObject.globalObject(); return JSIterator::create(globalObject.vm(), getDOMStructure(globalObject.vm(), globalObject), thisObject, kind); } template template inline EnableIfMap JSDOMIteratorBase::asJS(JSC::JSGlobalObject& lexicalGlobalObject, IteratorValue& value) { ASSERT(value); switch (m_kind) { case IterationKind::Keys: return toJS(lexicalGlobalObject, *globalObject(), value->key); case IterationKind::Values: return toJS(lexicalGlobalObject, *globalObject(), value->value); case IterationKind::Entries: return jsPair(lexicalGlobalObject, *globalObject(), value->key, value->value); }; ASSERT_NOT_REACHED(); return {}; } template template inline EnableIfSet JSDOMIteratorBase::asJS(JSC::JSGlobalObject& lexicalGlobalObject, IteratorValue& value) { ASSERT(value); auto globalObject = this->globalObject(); auto result = toJS(lexicalGlobalObject, *globalObject, value); switch (m_kind) { case IterationKind::Keys: case IterationKind::Values: return result; case IterationKind::Entries: return jsPair(lexicalGlobalObject, *globalObject, result, result); }; ASSERT_NOT_REACHED(); return {}; } template EnableIfMap appendForEachArguments(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject, JSC::MarkedArgumentBuffer& arguments, IteratorValue& value) { ASSERT(value); arguments.append(toJS(lexicalGlobalObject, globalObject, value->value)); arguments.append(toJS(lexicalGlobalObject, globalObject, value->key)); } template EnableIfSet appendForEachArguments(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject, JSC::MarkedArgumentBuffer& arguments, IteratorValue& value) { ASSERT(value); auto argument = toJS(lexicalGlobalObject, globalObject, value); arguments.append(argument); arguments.append(argument); } template JSC::JSValue iteratorForEach(JSC::JSGlobalObject& lexicalGlobalObject, JSC::CallFrame& callFrame, typename JSIterator::Wrapper& thisObject) { auto& vm = JSC::getVM(&lexicalGlobalObject); auto scope = DECLARE_THROW_SCOPE(vm); JSC::JSValue callback = callFrame.argument(0); JSC::JSValue thisValue = callFrame.argument(1); auto callData = JSC::getCallData(callback); if (callData.type == JSC::CallData::Type::None) return throwTypeError(&lexicalGlobalObject, scope, "Cannot call callback"_s); auto iterator = thisObject.wrapped().createIterator(); while (auto value = iterator.next()) { JSC::MarkedArgumentBuffer arguments; appendForEachArguments(lexicalGlobalObject, *thisObject.globalObject(), arguments, value); arguments.append(&thisObject); if (UNLIKELY(arguments.hasOverflowed())) { throwOutOfMemoryError(&lexicalGlobalObject, scope); return {}; } JSC::call(&lexicalGlobalObject, callback, callData, thisValue, arguments); if (UNLIKELY(scope.exception())) break; } return JSC::jsUndefined(); } template void JSDOMIteratorBase::destroy(JSCell* cell) { JSDOMIteratorBase* thisObject = static_cast*>(cell); thisObject->JSDOMIteratorBase::~JSDOMIteratorBase(); } template JSC::JSValue JSDOMIteratorBase::next(JSC::JSGlobalObject& lexicalGlobalObject) { if (m_iterator) { auto iteratorValue = m_iterator->next(); if (iteratorValue) return createIteratorResultObject(&lexicalGlobalObject, asJS(lexicalGlobalObject, iteratorValue), false); m_iterator = std::nullopt; } return createIteratorResultObject(&lexicalGlobalObject, JSC::jsUndefined(), true); } template JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSDOMIteratorPrototype::next(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) { JSC::VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); auto iterator = JSC::jsDynamicCast*>(callFrame->thisValue()); if (!iterator) return JSC::JSValue::encode(throwTypeError(globalObject, scope, "Cannot call next() on a non-Iterator object"_s)); return JSC::JSValue::encode(iterator->next(*globalObject)); } template void JSDOMIteratorPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) { Base::finishCreation(vm); ASSERT(inherits(info())); JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->next, next, 0, 0, ImplementationVisibility::Public, JSC::NoIntrinsic); JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); } }