1 | /* |
2 | * Copyright (C) 2016 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #pragma once |
27 | |
28 | #include "IDLTypes.h" |
29 | #include "JSDOMBinding.h" |
30 | #include "JSDOMConvertBase.h" |
31 | #include "JSDOMConvertBufferSource.h" |
32 | #include "JSDOMConvertInterface.h" |
33 | #include "JSDOMConvertNull.h" |
34 | #include <JavaScriptCore/IteratorOperations.h> |
35 | #include <wtf/Variant.h> |
36 | |
37 | namespace WebCore { |
38 | |
39 | template<typename ReturnType, bool enabled> |
40 | struct ConditionalReturner; |
41 | |
42 | template<typename ReturnType> |
43 | struct ConditionalReturner<ReturnType, true> { |
44 | template<typename T> |
45 | static Optional<ReturnType> get(T&& value) |
46 | { |
47 | return ReturnType(std::forward<T>(value)); |
48 | } |
49 | }; |
50 | |
51 | template<typename ReturnType> |
52 | struct ConditionalReturner<ReturnType, false> { |
53 | template<typename T> |
54 | static Optional<ReturnType> get(T&&) |
55 | { |
56 | return WTF::nullopt; |
57 | } |
58 | }; |
59 | |
60 | template<typename ReturnType, typename T, bool enabled> |
61 | struct ConditionalConverter; |
62 | |
63 | template<typename ReturnType, typename T> |
64 | struct ConditionalConverter<ReturnType, T, true> { |
65 | static Optional<ReturnType> convert(JSC::ExecState& state, JSC::JSValue value) |
66 | { |
67 | return ReturnType(Converter<T>::convert(state, value)); |
68 | } |
69 | }; |
70 | |
71 | template<typename ReturnType, typename T> |
72 | struct ConditionalConverter<ReturnType, T, false> { |
73 | static Optional<ReturnType> convert(JSC::ExecState&, JSC::JSValue) |
74 | { |
75 | return WTF::nullopt; |
76 | } |
77 | }; |
78 | |
79 | template<typename ReturnType, typename T, bool enabled> |
80 | struct ConditionalSequenceConverter; |
81 | |
82 | template<typename ReturnType, typename T> |
83 | struct ConditionalSequenceConverter<ReturnType, T, true> { |
84 | static Optional<ReturnType> convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method) |
85 | { |
86 | return ReturnType(Converter<T>::convert(state, object, method)); |
87 | } |
88 | }; |
89 | |
90 | template<typename ReturnType, typename T> |
91 | struct ConditionalSequenceConverter<ReturnType, T, false> { |
92 | static Optional<ReturnType> convert(JSC::ExecState&, JSC::JSObject*, JSC::JSValue) |
93 | { |
94 | return WTF::nullopt; |
95 | } |
96 | }; |
97 | |
98 | namespace Detail { |
99 | |
100 | template<typename List, bool condition> |
101 | struct ConditionalFront; |
102 | |
103 | template<typename List> |
104 | struct ConditionalFront<List, true> { |
105 | using type = brigand::front<List>; |
106 | }; |
107 | |
108 | template<typename List> |
109 | struct ConditionalFront<List, false> { |
110 | using type = void; |
111 | }; |
112 | |
113 | } |
114 | |
115 | template<typename List, bool condition> |
116 | using ConditionalFront = typename Detail::ConditionalFront<List, condition>::type; |
117 | |
118 | template<typename... T> struct Converter<IDLUnion<T...>> : DefaultConverter<IDLUnion<T...>> { |
119 | using Type = IDLUnion<T...>; |
120 | using TypeList = typename Type::TypeList; |
121 | using ReturnType = typename Type::ImplementationType; |
122 | |
123 | using NumericTypeList = brigand::filter<TypeList, IsIDLNumber<brigand::_1>>; |
124 | static constexpr size_t numberOfNumericTypes = brigand::size<NumericTypeList>::value; |
125 | static_assert(numberOfNumericTypes == 0 || numberOfNumericTypes == 1, "There can be 0 or 1 numeric types in an IDLUnion." ); |
126 | using NumericType = ConditionalFront<NumericTypeList, numberOfNumericTypes != 0>; |
127 | |
128 | using StringTypeList = brigand::filter<TypeList, IsIDLStringOrEnumeration<brigand::_1>>; |
129 | static constexpr size_t numberOfStringTypes = brigand::size<StringTypeList>::value; |
130 | static_assert(numberOfStringTypes == 0 || numberOfStringTypes == 1, "There can be 0 or 1 string types in an IDLUnion." ); |
131 | using StringType = ConditionalFront<StringTypeList, numberOfStringTypes != 0>; |
132 | |
133 | using SequenceTypeList = brigand::filter<TypeList, IsIDLSequence<brigand::_1>>; |
134 | static constexpr size_t numberOfSequenceTypes = brigand::size<SequenceTypeList>::value; |
135 | static_assert(numberOfSequenceTypes == 0 || numberOfSequenceTypes == 1, "There can be 0 or 1 sequence types in an IDLUnion." ); |
136 | using SequenceType = ConditionalFront<SequenceTypeList, numberOfSequenceTypes != 0>; |
137 | |
138 | using FrozenArrayTypeList = brigand::filter<TypeList, IsIDLFrozenArray<brigand::_1>>; |
139 | static constexpr size_t numberOfFrozenArrayTypes = brigand::size<FrozenArrayTypeList>::value; |
140 | static_assert(numberOfFrozenArrayTypes == 0 || numberOfFrozenArrayTypes == 1, "There can be 0 or 1 FrozenArray types in an IDLUnion." ); |
141 | using FrozenArrayType = ConditionalFront<FrozenArrayTypeList, numberOfFrozenArrayTypes != 0>; |
142 | |
143 | using DictionaryTypeList = brigand::filter<TypeList, IsIDLDictionary<brigand::_1>>; |
144 | static constexpr size_t numberOfDictionaryTypes = brigand::size<DictionaryTypeList>::value; |
145 | static_assert(numberOfDictionaryTypes == 0 || numberOfDictionaryTypes == 1, "There can be 0 or 1 dictionary types in an IDLUnion." ); |
146 | static constexpr bool hasDictionaryType = numberOfDictionaryTypes != 0; |
147 | using DictionaryType = ConditionalFront<DictionaryTypeList, hasDictionaryType>; |
148 | |
149 | using RecordTypeList = brigand::filter<TypeList, IsIDLRecord<brigand::_1>>; |
150 | static constexpr size_t numberOfRecordTypes = brigand::size<RecordTypeList>::value; |
151 | static_assert(numberOfRecordTypes == 0 || numberOfRecordTypes == 1, "There can be 0 or 1 record types in an IDLUnion." ); |
152 | static constexpr bool hasRecordType = numberOfRecordTypes != 0; |
153 | using RecordType = ConditionalFront<RecordTypeList, hasRecordType>; |
154 | |
155 | using ObjectTypeList = brigand::filter<TypeList, std::is_same<IDLObject, brigand::_1>>; |
156 | static constexpr size_t numberOfObjectTypes = brigand::size<ObjectTypeList>::value; |
157 | static_assert(numberOfObjectTypes == 0 || numberOfObjectTypes == 1, "There can be 0 or 1 object types in an IDLUnion." ); |
158 | static constexpr bool hasObjectType = numberOfObjectTypes != 0; |
159 | using ObjectType = ConditionalFront<ObjectTypeList, hasObjectType>; |
160 | |
161 | static constexpr bool hasAnyObjectType = (numberOfSequenceTypes + numberOfFrozenArrayTypes + numberOfDictionaryTypes + numberOfRecordTypes + numberOfObjectTypes) > 0; |
162 | |
163 | using InterfaceTypeList = brigand::filter<TypeList, IsIDLInterface<brigand::_1>>; |
164 | using TypedArrayTypeList = brigand::filter<TypeList, IsIDLTypedArray<brigand::_1>>; |
165 | |
166 | static ReturnType convert(JSC::ExecState& state, JSC::JSValue value) |
167 | { |
168 | JSC::VM& vm = state.vm(); |
169 | auto scope = DECLARE_THROW_SCOPE(vm); |
170 | |
171 | // 1. If the union type includes a nullable type and V is null or undefined, then return the IDL value null. |
172 | constexpr bool hasNullType = brigand::any<TypeList, std::is_same<IDLNull, brigand::_1>>::value; |
173 | if (hasNullType) { |
174 | if (value.isUndefinedOrNull()) |
175 | return ConditionalConverter<ReturnType, IDLNull, hasNullType>::convert(state, value).value(); |
176 | } |
177 | |
178 | // 2. Let types be the flattened member types of the union type. |
179 | // NOTE: Union is expected to be pre-flattented. |
180 | |
181 | // 3. If V is null or undefined then: |
182 | if (hasDictionaryType) { |
183 | if (value.isUndefinedOrNull()) { |
184 | // 1. If types includes a dictionary type, then return the result of converting V to that dictionary type. |
185 | return ConditionalConverter<ReturnType, DictionaryType, hasDictionaryType>::convert(state, value).value(); |
186 | } |
187 | } |
188 | |
189 | // 4. If V is a platform object, then: |
190 | // 1. If types includes an interface type that V implements, then return the IDL value that is a reference to the object V. |
191 | // 2. If types includes object, then return the IDL value that is a reference to the object V. |
192 | // (FIXME: Add support for object and step 4.2) |
193 | if (brigand::any<TypeList, IsIDLInterface<brigand::_1>>::value) { |
194 | Optional<ReturnType> returnValue; |
195 | brigand::for_each<InterfaceTypeList>([&](auto&& type) { |
196 | if (returnValue) |
197 | return; |
198 | |
199 | using Type = typename WTF::RemoveCVAndReference<decltype(type)>::type::type; |
200 | using ImplementationType = typename Type::ImplementationType; |
201 | using RawType = typename Type::RawType; |
202 | |
203 | auto castedValue = JSToWrappedOverloader<RawType>::toWrapped(state, value); |
204 | if (!castedValue) |
205 | return; |
206 | |
207 | returnValue = ReturnType(ImplementationType(castedValue)); |
208 | }); |
209 | |
210 | if (returnValue) |
211 | return WTFMove(returnValue.value()); |
212 | } |
213 | |
214 | // FIXME: Add support for steps 5 & 6. |
215 | // |
216 | // 5. If V is a DOMException platform object, then: |
217 | // 1. If types includes DOMException or Error, then return the result of converting V to that type. |
218 | // 2 If types includes object, then return the IDL value that is a reference to the object V. |
219 | // |
220 | // 6. If Type(V) is Object and V has an [[ErrorData]] internal slot), then: |
221 | // 1. If types includes Error, then return the result of converting V to Error. |
222 | // 2. If types includes object, then return the IDL value that is a reference to the object V. |
223 | |
224 | |
225 | // 7. If Type(V) is Object and V has an [[ArrayBufferData]] internal slot, then: |
226 | // 1. If types includes ArrayBuffer, then return the result of converting V to ArrayBuffer. |
227 | // 2. If types includes object, then return the IDL value that is a reference to the object V. |
228 | constexpr bool hasArrayBufferType = brigand::any<TypeList, std::is_same<IDLArrayBuffer, brigand::_1>>::value; |
229 | if (hasArrayBufferType || hasObjectType) { |
230 | auto arrayBuffer = JSC::JSArrayBuffer::toWrapped(vm, value); |
231 | if (arrayBuffer) { |
232 | if (hasArrayBufferType) |
233 | return ConditionalReturner<ReturnType, hasArrayBufferType>::get(WTFMove(arrayBuffer)).value(); |
234 | return ConditionalConverter<ReturnType, ObjectType, hasObjectType>::convert(state, value).value(); |
235 | } |
236 | } |
237 | |
238 | constexpr bool hasArrayBufferViewType = brigand::any<TypeList, std::is_same<IDLArrayBufferView, brigand::_1>>::value; |
239 | if (hasArrayBufferViewType || hasObjectType) { |
240 | auto arrayBufferView = JSC::JSArrayBufferView::toWrapped(vm, value); |
241 | if (arrayBufferView) { |
242 | if (hasArrayBufferViewType) |
243 | return ConditionalReturner<ReturnType, hasArrayBufferViewType>::get(WTFMove(arrayBufferView)).value(); |
244 | return ConditionalConverter<ReturnType, ObjectType, hasObjectType>::convert(state, value).value(); |
245 | } |
246 | } |
247 | |
248 | // 8. If Type(V) is Object and V has a [[DataView]] internal slot, then: |
249 | // 1. If types includes DataView, then return the result of converting V to DataView. |
250 | // 2. If types includes object, then return the IDL value that is a reference to the object V. |
251 | constexpr bool hasDataViewType = brigand::any<TypeList, std::is_same<IDLDataView, brigand::_1>>::value; |
252 | if (hasDataViewType || hasObjectType) { |
253 | auto dataView = JSC::JSDataView::toWrapped(vm, value); |
254 | if (dataView) { |
255 | if (hasDataViewType) |
256 | return ConditionalReturner<ReturnType, hasDataViewType>::get(WTFMove(dataView)).value(); |
257 | return ConditionalConverter<ReturnType, ObjectType, hasObjectType>::convert(state, value).value(); |
258 | } |
259 | } |
260 | |
261 | // 9. If Type(V) is Object and V has a [[TypedArrayName]] internal slot, then: |
262 | // 1. If types includes a typed array type whose name is the value of V’s [[TypedArrayName]] internal slot, then return the result of converting V to that type. |
263 | // 2. If types includes object, then return the IDL value that is a reference to the object V. |
264 | // (FIXME: Add support for object and step 9.2) |
265 | constexpr bool hasTypedArrayType = brigand::any<TypeList, IsIDLTypedArray<brigand::_1>>::value; |
266 | if (hasTypedArrayType) { |
267 | Optional<ReturnType> returnValue; |
268 | brigand::for_each<TypedArrayTypeList>([&](auto&& type) { |
269 | if (returnValue) |
270 | return; |
271 | |
272 | using Type = typename WTF::RemoveCVAndReference<decltype(type)>::type::type; |
273 | using ImplementationType = typename Type::ImplementationType; |
274 | using WrapperType = typename Converter<Type>::WrapperType; |
275 | |
276 | auto castedValue = WrapperType::toWrapped(vm, value); |
277 | if (!castedValue) |
278 | return; |
279 | |
280 | returnValue = ReturnType(ImplementationType(castedValue)); |
281 | }); |
282 | |
283 | if (returnValue) |
284 | return WTFMove(returnValue.value()); |
285 | } |
286 | |
287 | // FIXME: Add support for step 10. |
288 | // |
289 | // 10. If IsCallable(V) is true, then: |
290 | // 1. If types includes a callback function type, then return the result of converting V to that callback function type. |
291 | // 2. If types includes object, then return the IDL value that is a reference to the object V. |
292 | |
293 | // 11. If V is any kind of object, then: |
294 | if (hasAnyObjectType) { |
295 | if (value.isCell()) { |
296 | JSC::JSCell* cell = value.asCell(); |
297 | if (cell->isObject()) { |
298 | auto object = asObject(value); |
299 | |
300 | // 1. If types includes a sequence type, then: |
301 | // 1. Let method be the result of GetMethod(V, @@iterator). |
302 | // 2. ReturnIfAbrupt(method). |
303 | // 3. If method is not undefined, return the result of creating a |
304 | // sequence of that type from V and method. |
305 | constexpr bool hasSequenceType = numberOfSequenceTypes != 0; |
306 | if (hasSequenceType) { |
307 | auto method = JSC::iteratorMethod(state, object); |
308 | RETURN_IF_EXCEPTION(scope, ReturnType()); |
309 | if (!method.isUndefined()) |
310 | return ConditionalSequenceConverter<ReturnType, SequenceType, hasSequenceType>::convert(state, object, method).value(); |
311 | } |
312 | |
313 | // 2. If types includes a frozen array type, then: |
314 | // 1. Let method be the result of GetMethod(V, @@iterator). |
315 | // 2. ReturnIfAbrupt(method). |
316 | // 3. If method is not undefined, return the result of creating a |
317 | // frozen array of that type from V and method. |
318 | constexpr bool hasFrozenArrayType = numberOfFrozenArrayTypes != 0; |
319 | if (hasFrozenArrayType) { |
320 | auto method = JSC::iteratorMethod(state, object); |
321 | RETURN_IF_EXCEPTION(scope, ReturnType()); |
322 | if (!method.isUndefined()) |
323 | return ConditionalSequenceConverter<ReturnType, FrozenArrayType, hasFrozenArrayType>::convert(state, object, method).value(); |
324 | } |
325 | |
326 | // 3. If types includes a dictionary type, then return the result of |
327 | // converting V to that dictionary type. |
328 | if (hasDictionaryType) |
329 | return ConditionalConverter<ReturnType, DictionaryType, hasDictionaryType>::convert(state, value).value(); |
330 | |
331 | // 4. If types includes a record type, then return the result of converting V to that record type. |
332 | if (hasRecordType) |
333 | return ConditionalConverter<ReturnType, RecordType, hasRecordType>::convert(state, value).value(); |
334 | |
335 | // 5. If types includes a callback interface type, then return the result of converting V to that interface type. |
336 | // (FIXME: Add support for callback interface type and step 12.5) |
337 | |
338 | // 6. If types includes object, then return the IDL value that is a reference to the object V. |
339 | if (hasObjectType) |
340 | return ConditionalConverter<ReturnType, ObjectType, hasObjectType>::convert(state, value).value(); |
341 | } |
342 | } |
343 | } |
344 | |
345 | // 12. If V is a Boolean value, then: |
346 | // 1. If types includes a boolean, then return the result of converting V to boolean. |
347 | constexpr bool hasBooleanType = brigand::any<TypeList, std::is_same<IDLBoolean, brigand::_1>>::value; |
348 | if (hasBooleanType) { |
349 | if (value.isBoolean()) |
350 | return ConditionalConverter<ReturnType, IDLBoolean, hasBooleanType>::convert(state, value).value(); |
351 | } |
352 | |
353 | // 13. If V is a Number value, then: |
354 | // 1. If types includes a numeric type, then return the result of converting V to that numeric type. |
355 | constexpr bool hasNumericType = brigand::size<NumericTypeList>::value != 0; |
356 | if (hasNumericType) { |
357 | if (value.isNumber()) |
358 | return ConditionalConverter<ReturnType, NumericType, hasNumericType>::convert(state, value).value(); |
359 | } |
360 | |
361 | // 14. If types includes a string type, then return the result of converting V to that type. |
362 | constexpr bool hasStringType = brigand::size<StringTypeList>::value != 0; |
363 | if (hasStringType) |
364 | return ConditionalConverter<ReturnType, StringType, hasStringType>::convert(state, value).value(); |
365 | |
366 | // 15. If types includes a numeric type, then return the result of converting V to that numeric type. |
367 | if (hasNumericType) |
368 | return ConditionalConverter<ReturnType, NumericType, hasNumericType>::convert(state, value).value(); |
369 | |
370 | // 16. If types includes a boolean, then return the result of converting V to boolean. |
371 | if (hasBooleanType) |
372 | return ConditionalConverter<ReturnType, IDLBoolean, hasBooleanType>::convert(state, value).value(); |
373 | |
374 | // 17. Throw a TypeError. |
375 | throwTypeError(&state, scope); |
376 | return ReturnType(); |
377 | } |
378 | }; |
379 | |
380 | template<typename... T> struct JSConverter<IDLUnion<T...>> { |
381 | using Type = IDLUnion<T...>; |
382 | using TypeList = typename Type::TypeList; |
383 | using ImplementationType = typename Type::ImplementationType; |
384 | |
385 | static constexpr bool needsState = true; |
386 | static constexpr bool needsGlobalObject = true; |
387 | |
388 | using Sequence = brigand::make_sequence<brigand::ptrdiff_t<0>, WTF::variant_size<ImplementationType>::value>; |
389 | |
390 | static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const ImplementationType& variant) |
391 | { |
392 | auto index = variant.index(); |
393 | |
394 | Optional<JSC::JSValue> returnValue; |
395 | brigand::for_each<Sequence>([&](auto&& type) { |
396 | using I = typename WTF::RemoveCVAndReference<decltype(type)>::type::type; |
397 | if (I::value == index) { |
398 | ASSERT(!returnValue); |
399 | returnValue = toJS<brigand::at<TypeList, I>>(state, globalObject, WTF::get<I::value>(variant)); |
400 | } |
401 | }); |
402 | |
403 | ASSERT(returnValue); |
404 | return returnValue.value(); |
405 | } |
406 | }; |
407 | |
408 | } // namespace WebCore |
409 | |