1 | /* |
2 | * Copyright (C) 2016 Canon, Inc. All rights reserved. |
3 | * Copyright (C) 2016-2017 Apple Inc. All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * 1. Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. |
10 | * 2. Redistributions in binary form must reproduce the above copyright |
11 | * notice, this list of conditions and the following disclaimer in the |
12 | * documentation and/or other materials provided with the distribution. |
13 | * |
14 | * THIS SOFTWARE IS PROVIDED BY CANON INC. ``AS IS'' AND ANY |
15 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
17 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CANON INC. OR |
18 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | */ |
26 | |
27 | #pragma once |
28 | |
29 | #include "JSDOMConvert.h" |
30 | #include <JavaScriptCore/IteratorPrototype.h> |
31 | #include <type_traits> |
32 | |
33 | namespace WebCore { |
34 | |
35 | void addValueIterableMethods(JSC::JSGlobalObject&, JSC::JSObject&); |
36 | |
37 | enum class JSDOMIteratorType { Set, Map }; |
38 | |
39 | // struct IteratorTraits { |
40 | // static constexpr JSDOMIteratorType type = [Map|Set]; |
41 | // using KeyType = [IDLType|void]; |
42 | // using ValueType = [IDLType]; |
43 | // }; |
44 | |
45 | template<typename T, typename U = void> using EnableIfMap = typename std::enable_if<T::type == JSDOMIteratorType::Map, U>::type; |
46 | template<typename T, typename U = void> using EnableIfSet = typename std::enable_if<T::type == JSDOMIteratorType::Set, U>::type; |
47 | |
48 | template<typename JSWrapper, typename IteratorTraits> class JSDOMIteratorPrototype : public JSC::JSNonFinalObject { |
49 | public: |
50 | using Base = JSC::JSNonFinalObject; |
51 | using DOMWrapped = typename JSWrapper::DOMWrapped; |
52 | |
53 | static JSDOMIteratorPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) |
54 | { |
55 | JSDOMIteratorPrototype* prototype = new (NotNull, JSC::allocateCell<JSDOMIteratorPrototype>(vm.heap)) JSDOMIteratorPrototype(vm, structure); |
56 | prototype->finishCreation(vm, globalObject); |
57 | return prototype; |
58 | } |
59 | |
60 | DECLARE_INFO; |
61 | |
62 | static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) |
63 | { |
64 | return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); |
65 | } |
66 | |
67 | static JSC::EncodedJSValue JSC_HOST_CALL next(JSC::ExecState*); |
68 | |
69 | private: |
70 | JSDOMIteratorPrototype(JSC::VM& vm, JSC::Structure* structure) : Base(vm, structure) { } |
71 | |
72 | void finishCreation(JSC::VM&, JSC::JSGlobalObject*); |
73 | }; |
74 | |
75 | enum class IterationKind { Key, Value, KeyValue }; |
76 | |
77 | template<typename JSWrapper, typename IteratorTraits> class JSDOMIterator : public JSDOMObject { |
78 | public: |
79 | using Base = JSDOMObject; |
80 | |
81 | using Wrapper = JSWrapper; |
82 | using Traits = IteratorTraits; |
83 | |
84 | using DOMWrapped = typename Wrapper::DOMWrapped; |
85 | using Prototype = JSDOMIteratorPrototype<Wrapper, Traits>; |
86 | |
87 | DECLARE_INFO; |
88 | |
89 | static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) |
90 | { |
91 | return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); |
92 | } |
93 | |
94 | static JSDOMIterator* create(JSC::VM& vm, JSC::Structure* structure, JSWrapper& iteratedObject, IterationKind kind) |
95 | { |
96 | JSDOMIterator* instance = new (NotNull, JSC::allocateCell<JSDOMIterator>(vm.heap)) JSDOMIterator(structure, iteratedObject, kind); |
97 | instance->finishCreation(vm); |
98 | return instance; |
99 | } |
100 | |
101 | static Prototype* createPrototype(JSC::VM& vm, JSC::JSGlobalObject& globalObject) |
102 | { |
103 | return Prototype::create(vm, &globalObject, Prototype::createStructure(vm, &globalObject, globalObject.iteratorPrototype())); |
104 | } |
105 | |
106 | JSC::JSValue next(JSC::ExecState&); |
107 | |
108 | private: |
109 | JSDOMIterator(JSC::Structure* structure, JSWrapper& iteratedObject, IterationKind kind) |
110 | : Base(structure, *iteratedObject.globalObject()) |
111 | , m_iterator(iteratedObject.wrapped().createIterator()) |
112 | , m_kind(kind) |
113 | { |
114 | } |
115 | |
116 | template<typename IteratorValue, typename T = Traits> EnableIfMap<T, JSC::JSValue> asJS(JSC::ExecState&, IteratorValue&); |
117 | template<typename IteratorValue, typename T = Traits> EnableIfSet<T, JSC::JSValue> asJS(JSC::ExecState&, IteratorValue&); |
118 | |
119 | static void destroy(JSC::JSCell*); |
120 | |
121 | Optional<typename DOMWrapped::Iterator> m_iterator; |
122 | IterationKind m_kind; |
123 | }; |
124 | |
125 | inline JSC::JSValue jsPair(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSC::JSValue value1, JSC::JSValue value2) |
126 | { |
127 | JSC::MarkedArgumentBuffer arguments; |
128 | arguments.append(value1); |
129 | arguments.append(value2); |
130 | ASSERT(!arguments.hasOverflowed()); |
131 | return constructArray(&state, nullptr, &globalObject, arguments); |
132 | } |
133 | |
134 | template<typename FirstType, typename SecondType, typename T, typename U> |
135 | inline JSC::JSValue jsPair(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const T& value1, const U& value2) |
136 | { |
137 | return jsPair(state, globalObject, toJS<FirstType>(state, globalObject, value1), toJS<SecondType>(state, globalObject, value2)); |
138 | } |
139 | |
140 | template<typename JSIterator> JSC::JSValue iteratorCreate(typename JSIterator::Wrapper&, IterationKind); |
141 | template<typename JSIterator> JSC::JSValue iteratorForEach(JSC::ExecState&, typename JSIterator::Wrapper&, JSC::ThrowScope&); |
142 | |
143 | template<typename JSIterator> JSC::JSValue iteratorCreate(typename JSIterator::Wrapper& thisObject, IterationKind kind) |
144 | { |
145 | ASSERT(thisObject.globalObject()); |
146 | JSDOMGlobalObject& globalObject = *thisObject.globalObject(); |
147 | return JSIterator::create(globalObject.vm(), getDOMStructure<JSIterator>(globalObject.vm(), globalObject), thisObject, kind); |
148 | } |
149 | |
150 | template<typename JSWrapper, typename IteratorTraits> |
151 | template<typename IteratorValue, typename T> inline EnableIfMap<T, JSC::JSValue> JSDOMIterator<JSWrapper, IteratorTraits>::asJS(JSC::ExecState& state, IteratorValue& value) |
152 | { |
153 | ASSERT(value); |
154 | |
155 | switch (m_kind) { |
156 | case IterationKind::Key: |
157 | return toJS<typename Traits::KeyType>(state, *globalObject(), value->key); |
158 | case IterationKind::Value: |
159 | return toJS<typename Traits::ValueType>(state, *globalObject(), value->value); |
160 | case IterationKind::KeyValue: |
161 | return jsPair<typename Traits::KeyType, typename Traits::ValueType>(state, *globalObject(), value->key, value->value); |
162 | }; |
163 | |
164 | ASSERT_NOT_REACHED(); |
165 | return { }; |
166 | } |
167 | |
168 | template<typename JSWrapper, typename IteratorTraits> |
169 | template<typename IteratorValue, typename T> inline EnableIfSet<T, JSC::JSValue> JSDOMIterator<JSWrapper, IteratorTraits>::asJS(JSC::ExecState& state, IteratorValue& value) |
170 | { |
171 | ASSERT(value); |
172 | |
173 | auto globalObject = this->globalObject(); |
174 | auto result = toJS<typename Traits::ValueType>(state, *globalObject, value); |
175 | |
176 | switch (m_kind) { |
177 | case IterationKind::Key: |
178 | case IterationKind::Value: |
179 | return result; |
180 | case IterationKind::KeyValue: |
181 | return jsPair(state, *globalObject, result, result); |
182 | }; |
183 | |
184 | ASSERT_NOT_REACHED(); |
185 | return { }; |
186 | } |
187 | |
188 | template<typename JSIterator, typename IteratorValue> EnableIfMap<typename JSIterator::Traits> appendForEachArguments(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSC::MarkedArgumentBuffer& arguments, IteratorValue& value) |
189 | { |
190 | ASSERT(value); |
191 | arguments.append(toJS<typename JSIterator::Traits::ValueType>(state, globalObject, value->value)); |
192 | arguments.append(toJS<typename JSIterator::Traits::KeyType>(state, globalObject, value->key)); |
193 | } |
194 | |
195 | template<typename JSIterator, typename IteratorValue> EnableIfSet<typename JSIterator::Traits> appendForEachArguments(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSC::MarkedArgumentBuffer& arguments, IteratorValue& value) |
196 | { |
197 | ASSERT(value); |
198 | auto argument = toJS<typename JSIterator::Traits::ValueType>(state, globalObject, value); |
199 | arguments.append(argument); |
200 | arguments.append(argument); |
201 | } |
202 | |
203 | template<typename JSIterator> JSC::JSValue iteratorForEach(JSC::ExecState& state, typename JSIterator::Wrapper& thisObject, JSC::ThrowScope& scope) |
204 | { |
205 | JSC::JSValue callback = state.argument(0); |
206 | JSC::JSValue thisValue = state.argument(1); |
207 | |
208 | JSC::CallData callData; |
209 | JSC::CallType callType = JSC::getCallData(state.vm(), callback, callData); |
210 | if (callType == JSC::CallType::None) |
211 | return throwTypeError(&state, scope, "Cannot call callback"_s ); |
212 | |
213 | auto iterator = thisObject.wrapped().createIterator(); |
214 | while (auto value = iterator.next()) { |
215 | JSC::MarkedArgumentBuffer arguments; |
216 | appendForEachArguments<JSIterator>(state, *thisObject.globalObject(), arguments, value); |
217 | arguments.append(&thisObject); |
218 | if (UNLIKELY(arguments.hasOverflowed())) { |
219 | throwOutOfMemoryError(&state, scope); |
220 | return { }; |
221 | } |
222 | JSC::call(&state, callback, callType, callData, thisValue, arguments); |
223 | if (UNLIKELY(scope.exception())) |
224 | break; |
225 | } |
226 | return JSC::jsUndefined(); |
227 | } |
228 | |
229 | template<typename JSWrapper, typename IteratorTraits> |
230 | void JSDOMIterator<JSWrapper, IteratorTraits>::destroy(JSCell* cell) |
231 | { |
232 | JSDOMIterator<JSWrapper, IteratorTraits>* thisObject = static_cast<JSDOMIterator<JSWrapper, IteratorTraits>*>(cell); |
233 | thisObject->JSDOMIterator<JSWrapper, IteratorTraits>::~JSDOMIterator(); |
234 | } |
235 | |
236 | template<typename JSWrapper, typename IteratorTraits> |
237 | JSC::JSValue JSDOMIterator<JSWrapper, IteratorTraits>::next(JSC::ExecState& state) |
238 | { |
239 | if (m_iterator) { |
240 | auto iteratorValue = m_iterator->next(); |
241 | if (iteratorValue) |
242 | return createIteratorResultObject(&state, asJS(state, iteratorValue), false); |
243 | m_iterator = WTF::nullopt; |
244 | } |
245 | return createIteratorResultObject(&state, JSC::jsUndefined(), true); |
246 | } |
247 | |
248 | template<typename JSWrapper, typename IteratorTraits> |
249 | JSC::EncodedJSValue JSC_HOST_CALL JSDOMIteratorPrototype<JSWrapper, IteratorTraits>::next(JSC::ExecState* state) |
250 | { |
251 | JSC::VM& vm = state->vm(); |
252 | auto scope = DECLARE_THROW_SCOPE(vm); |
253 | |
254 | auto iterator = JSC::jsDynamicCast<JSDOMIterator<JSWrapper, IteratorTraits>*>(vm, state->thisValue()); |
255 | if (!iterator) |
256 | return JSC::JSValue::encode(throwTypeError(state, scope, "Cannot call next() on a non-Iterator object"_s )); |
257 | |
258 | return JSC::JSValue::encode(iterator->next(*state)); |
259 | } |
260 | |
261 | template<typename JSWrapper, typename IteratorTraits> |
262 | void JSDOMIteratorPrototype<JSWrapper, IteratorTraits>::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) |
263 | { |
264 | Base::finishCreation(vm); |
265 | ASSERT(inherits(vm, info())); |
266 | |
267 | JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->next, next, 0, 0, JSC::NoIntrinsic); |
268 | } |
269 | |
270 | } |
271 | |