/* * Copyright (C) 2016 Canon, Inc. All rights reserved. * Copyright (C) 2016 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 : public JSC::JSNonFinalObject { public: using Base = JSC::JSNonFinalObject; using DOMWrapped = typename JSWrapper::DOMWrapped; static JSDOMIteratorPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) { JSDOMIteratorPrototype* prototype = new (NotNull, JSC::allocateCell(vm.heap)) 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 next(JSC::ExecState*); private: JSDOMIteratorPrototype(JSC::VM& vm, JSC::Structure* structure) : Base(vm, structure) { } void finishCreation(JSC::VM&, JSC::JSGlobalObject*); }; enum class IterationKind { Key, Value, KeyValue }; template class JSDOMIterator : public JSDOMObject { public: using Base = JSDOMObject; using Wrapper = JSWrapper; using Traits = IteratorTraits; using DOMWrapped = typename Wrapper::DOMWrapped; using Prototype = JSDOMIteratorPrototype; 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 JSDOMIterator* create(JSC::VM& vm, JSC::Structure* structure, JSWrapper& iteratedObject, IterationKind kind) { JSDOMIterator* instance = new (NotNull, JSC::allocateCell(vm.heap)) JSDOMIterator(structure, iteratedObject, kind); instance->finishCreation(vm); return instance; } static Prototype* createPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject) { return Prototype::create(vm, globalObject, Prototype::createStructure(vm, globalObject, globalObject->iteratorPrototype())); } JSC::JSValue next(JSC::ExecState&); private: JSDOMIterator(JSC::Structure* structure, JSWrapper& iteratedObject, IterationKind kind) : Base(structure, *iteratedObject.globalObject()) , m_iterator(iteratedObject.wrapped().createIterator()) , m_kind(kind) { } template EnableIfMap asJS(JSC::ExecState&, IteratorValue&); template EnableIfSet asJS(JSC::ExecState&, IteratorValue&); static void destroy(JSC::JSCell*); std::optional m_iterator; IterationKind m_kind; }; inline JSC::JSValue jsPair(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSC::JSValue value1, JSC::JSValue value2) { JSC::MarkedArgumentBuffer arguments; arguments.append(value1); arguments.append(value2); return constructArray(&state, nullptr, &globalObject, arguments); } template inline JSC::JSValue jsPair(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const T& value1, const U& value2) { return jsPair(state, globalObject, toJS(state, globalObject, value1), toJS(state, globalObject, value2)); } template JSC::JSValue iteratorCreate(typename JSIterator::Wrapper&, IterationKind); template JSC::JSValue iteratorForEach(JSC::ExecState&, typename JSIterator::Wrapper&, JSC::ThrowScope&); 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 JSDOMIterator::asJS(JSC::ExecState& state, IteratorValue& value) { ASSERT(value); switch (m_kind) { case IterationKind::Key: return toJS(state, *globalObject(), value->key); case IterationKind::Value: return toJS(state, *globalObject(), value->value); case IterationKind::KeyValue: return jsPair(state, *globalObject(), value->key, value->value); }; ASSERT_NOT_REACHED(); return { }; } template template inline EnableIfSet JSDOMIterator::asJS(JSC::ExecState& state, IteratorValue& value) { ASSERT(value); auto globalObject = this->globalObject(); auto result = toJS(state, *globalObject, value); switch (m_kind) { case IterationKind::Key: case IterationKind::Value: return result; case IterationKind::KeyValue: return jsPair(state, *globalObject, result, result); }; ASSERT_NOT_REACHED(); return { }; } template EnableIfMap appendForEachArguments(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSC::MarkedArgumentBuffer& arguments, IteratorValue& value) { ASSERT(value); arguments.append(toJS(state, globalObject, value->value)); arguments.append(toJS(state, globalObject, value->key)); } template EnableIfSet appendForEachArguments(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSC::MarkedArgumentBuffer& arguments, IteratorValue& value) { ASSERT(value); auto argument = toJS(state, globalObject, value); arguments.append(argument); arguments.append(argument); } template JSC::JSValue iteratorForEach(JSC::ExecState& state, typename JSIterator::Wrapper& thisObject, JSC::ThrowScope& scope) { JSC::JSValue callback = state.argument(0); JSC::JSValue thisValue = state.argument(1); JSC::CallData callData; JSC::CallType callType = JSC::getCallData(callback, callData); if (callType == JSC::CallType::None) return throwTypeError(&state, scope, ASCIILiteral("Cannot call callback")); auto iterator = thisObject.wrapped().createIterator(); while (auto value = iterator.next()) { JSC::MarkedArgumentBuffer arguments; appendForEachArguments(state, *thisObject.globalObject(), arguments, value); arguments.append(&thisObject); JSC::call(&state, callback, callType, callData, thisValue, arguments); if (UNLIKELY(scope.exception())) break; } return JSC::jsUndefined(); } template void JSDOMIterator::destroy(JSCell* cell) { JSDOMIterator* thisObject = static_cast*>(cell); thisObject->JSDOMIterator::~JSDOMIterator(); } template JSC::JSValue JSDOMIterator::next(JSC::ExecState& state) { if (m_iterator) { auto iteratorValue = m_iterator->next(); if (iteratorValue) return createIteratorResultObject(&state, asJS(state, iteratorValue), false); m_iterator = std::nullopt; } return createIteratorResultObject(&state, JSC::jsUndefined(), true); } template JSC::EncodedJSValue JSC_HOST_CALL JSDOMIteratorPrototype::next(JSC::ExecState* state) { JSC::VM& vm = state->vm(); auto scope = DECLARE_THROW_SCOPE(vm); auto iterator = jsDynamicDowncast*>(vm, state->thisValue()); if (!iterator) return JSC::JSValue::encode(throwTypeError(state, scope, ASCIILiteral("Cannot call next() on a non-Iterator object"))); return JSC::JSValue::encode(iterator->next(*state)); } template void JSDOMIteratorPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) { Base::finishCreation(vm); ASSERT(inherits(vm, info())); JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->next, next, 0, 0, JSC::NoIntrinsic); } }