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
37namespace WebCore {
38
39template<typename ReturnType, bool enabled>
40struct ConditionalReturner;
41
42template<typename ReturnType>
43struct 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
51template<typename ReturnType>
52struct ConditionalReturner<ReturnType, false> {
53 template<typename T>
54 static Optional<ReturnType> get(T&&)
55 {
56 return WTF::nullopt;
57 }
58};
59
60template<typename ReturnType, typename T, bool enabled>
61struct ConditionalConverter;
62
63template<typename ReturnType, typename T>
64struct 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
71template<typename ReturnType, typename T>
72struct ConditionalConverter<ReturnType, T, false> {
73 static Optional<ReturnType> convert(JSC::ExecState&, JSC::JSValue)
74 {
75 return WTF::nullopt;
76 }
77};
78
79template<typename ReturnType, typename T, bool enabled>
80struct ConditionalSequenceConverter;
81
82template<typename ReturnType, typename T>
83struct 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
90template<typename ReturnType, typename T>
91struct ConditionalSequenceConverter<ReturnType, T, false> {
92 static Optional<ReturnType> convert(JSC::ExecState&, JSC::JSObject*, JSC::JSValue)
93 {
94 return WTF::nullopt;
95 }
96};
97
98namespace Detail {
99
100template<typename List, bool condition>
101struct ConditionalFront;
102
103template<typename List>
104struct ConditionalFront<List, true> {
105 using type = brigand::front<List>;
106};
107
108template<typename List>
109struct ConditionalFront<List, false> {
110 using type = void;
111};
112
113}
114
115template<typename List, bool condition>
116using ConditionalFront = typename Detail::ConditionalFront<List, condition>::type;
117
118template<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
380template<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