blob: 960fcf5d196921c8be98e03faa5608093c7f5d66 [file] [log] [blame]
/*
* 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS 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 "BufferSource.h"
#include "IDBBindingUtilities.h"
#include "IDLTypes.h"
#include "JSDOMBinding.h"
#include <runtime/JSGlobalObjectInlines.h>
#include <runtime/JSONObject.h>
namespace WebCore {
// Conversion from JSValue -> Implementation
template<typename T> struct Converter;
enum class IntegerConversionConfiguration { Normal, EnforceRange, Clamp };
enum class StringConversionConfiguration { Normal, TreatNullAsEmptyString };
struct DefaultExceptionThrower {
void operator()(JSC::ExecState& state, JSC::ThrowScope& scope)
{
throwTypeError(&state, scope);
}
};
template<typename T> typename Converter<T>::ReturnType convert(JSC::ExecState&, JSC::JSValue);
template<typename T> typename Converter<T>::ReturnType convert(JSC::ExecState&, JSC::JSValue, JSC::JSObject&);
template<typename T> typename Converter<T>::ReturnType convert(JSC::ExecState&, JSC::JSValue, JSDOMGlobalObject&);
template<typename T> typename Converter<T>::ReturnType convert(JSC::ExecState&, JSC::JSValue, IntegerConversionConfiguration);
template<typename T> typename Converter<T>::ReturnType convert(JSC::ExecState&, JSC::JSValue, StringConversionConfiguration);
template<typename T, typename ExceptionThrower> typename Converter<T>::ReturnType convert(JSC::ExecState&, JSC::JSValue, ExceptionThrower&&);
template<typename T, typename ExceptionThrower> typename Converter<T>::ReturnType convert(JSC::ExecState&, JSC::JSValue, JSC::JSObject&, ExceptionThrower&&);
template<typename T, typename ExceptionThrower> typename Converter<T>::ReturnType convert(JSC::ExecState&, JSC::JSValue, JSDOMGlobalObject&, ExceptionThrower&&);
// Specialized by generated code for IDL dictionary conversion.
template<typename T> T convertDictionary(JSC::ExecState&, JSC::JSValue);
// Specialized by generated code for IDL enumeration conversion.
template<typename T> std::optional<T> parseEnumeration(JSC::ExecState&, JSC::JSValue);
template<typename T> T convertEnumeration(JSC::ExecState&, JSC::JSValue);
template<typename T> const char* expectedEnumerationValues();
template<typename T> inline typename Converter<T>::ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
return Converter<T>::convert(state, value);
}
template<typename T> inline typename Converter<T>::ReturnType convert(JSC::ExecState& state, JSC::JSValue value, JSC::JSObject& thisObject)
{
return Converter<T>::convert(state, value, thisObject);
}
template<typename T> inline typename Converter<T>::ReturnType convert(JSC::ExecState& state, JSC::JSValue value, JSDOMGlobalObject& globalObject)
{
return Converter<T>::convert(state, value, globalObject);
}
template<typename T> inline typename Converter<T>::ReturnType convert(JSC::ExecState& state, JSC::JSValue value, IntegerConversionConfiguration configuration)
{
return Converter<T>::convert(state, value, configuration);
}
template<typename T> inline typename Converter<T>::ReturnType convert(JSC::ExecState& state, JSC::JSValue value, StringConversionConfiguration configuration)
{
return Converter<T>::convert(state, value, configuration);
}
template<typename T, typename ExceptionThrower> inline typename Converter<T>::ReturnType convert(JSC::ExecState& state, JSC::JSValue value, ExceptionThrower&& exceptionThrower)
{
return Converter<T>::convert(state, value, std::forward<ExceptionThrower>(exceptionThrower));
}
template<typename T, typename ExceptionThrower> inline typename Converter<T>::ReturnType convert(JSC::ExecState& state, JSC::JSValue value, JSC::JSObject& thisObject, ExceptionThrower&& exceptionThrower)
{
return Converter<T>::convert(state, value, thisObject, std::forward<ExceptionThrower>(exceptionThrower));
}
template<typename T, typename ExceptionThrower> inline typename Converter<T>::ReturnType convert(JSC::ExecState& state, JSC::JSValue value, JSDOMGlobalObject& globalObject, ExceptionThrower&& exceptionThrower)
{
return Converter<T>::convert(state, value, globalObject, std::forward<ExceptionThrower>(exceptionThrower));
}
// Conversion from Implementation -> JSValue
template<typename T> struct JSConverter;
template<typename T, typename U> inline JSC::JSValue toJS(U&&);
template<typename T, typename U> inline JSC::JSValue toJS(JSC::ExecState&, U&&);
template<typename T, typename U> inline JSC::JSValue toJS(JSC::ExecState&, JSDOMGlobalObject&, U&&);
template<typename T, typename U> inline JSC::JSValue toJS(JSC::ExecState&, JSC::ThrowScope&, ExceptionOr<U>&&);
template<typename T, typename U> inline JSC::JSValue toJS(JSC::ExecState&, JSDOMGlobalObject&, JSC::ThrowScope&, ExceptionOr<U>&&);
template<typename T, typename U> inline JSC::JSValue toJSNewlyCreated(JSC::ExecState&, JSDOMGlobalObject&, U&&);
template<typename T, typename U> inline JSC::JSValue toJSNewlyCreated(JSC::ExecState&, JSDOMGlobalObject&, JSC::ThrowScope&, ExceptionOr<U>&&);
// Specialized by generated code for IDL enumeration conversion.
template<typename T> JSC::JSString* convertEnumerationToJS(JSC::ExecState&, T);
template<typename T, bool needsState = JSConverter<T>::needsState, bool needsGlobalObject = JSConverter<T>::needsGlobalObject>
struct JSConverterOverloader;
template<typename T>
struct JSConverterOverloader<T, true, true> {
template<typename U> static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject& globalObject, U&& value)
{
return JSConverter<T>::convert(state, globalObject, std::forward<U>(value));
}
};
template<typename T>
struct JSConverterOverloader<T, true, false> {
template<typename U> static JSC::JSValue convert(JSC::ExecState& state, U&& value)
{
return JSConverter<T>::convert(state, std::forward<U>(value));
}
template<typename U> static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject&, U&& value)
{
return JSConverter<T>::convert(state, std::forward<U>(value));
}
};
template<typename T>
struct JSConverterOverloader<T, false, false> {
template<typename U> static JSC::JSValue convert(JSC::ExecState&, U&& value)
{
return JSConverter<T>::convert(std::forward<U>(value));
}
template<typename U> static JSC::JSValue convert(JSC::ExecState&, JSDOMGlobalObject&, U&& value)
{
return JSConverter<T>::convert(std::forward<U>(value));
}
};
template<typename T, typename U> inline JSC::JSValue toJS(U&& value)
{
return JSConverter<T>::convert(std::forward<U>(value));
}
template<typename T, typename U> inline JSC::JSValue toJS(JSC::ExecState& state, U&& value)
{
return JSConverterOverloader<T>::convert(state, std::forward<U>(value));
}
template<typename T, typename U> inline JSC::JSValue toJS(JSC::ExecState& state, JSDOMGlobalObject& globalObject, U&& value)
{
return JSConverterOverloader<T>::convert(state, globalObject, std::forward<U>(value));
}
template<typename T, typename U> inline JSC::JSValue toJS(JSC::ExecState& state, JSC::ThrowScope& throwScope, ExceptionOr<U>&& value)
{
if (UNLIKELY(value.hasException())) {
propagateException(state, throwScope, value.releaseException());
return { };
}
return toJS<T>(state, value.releaseReturnValue());
}
template<typename T, typename U> inline JSC::JSValue toJS(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSC::ThrowScope& throwScope, ExceptionOr<U>&& value)
{
if (UNLIKELY(value.hasException())) {
propagateException(state, throwScope, value.releaseException());
return { };
}
return toJS<T>(state, globalObject, value.releaseReturnValue());
}
template<typename T, typename U> inline JSC::JSValue toJSNewlyCreated(JSC::ExecState& state, JSDOMGlobalObject& globalObject, U&& value)
{
return JSConverter<T>::convertNewlyCreated(state, globalObject, std::forward<U>(value));
}
template<typename T, typename U> inline JSC::JSValue toJSNewlyCreated(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSC::ThrowScope& throwScope, ExceptionOr<U>&& value)
{
if (UNLIKELY(value.hasException())) {
propagateException(state, throwScope, value.releaseException());
return { };
}
return toJSNewlyCreated<T>(state, globalObject, value.releaseReturnValue());
}
template<typename T> struct DefaultConverter {
using ReturnType = typename T::ImplementationType;
};
// MARK: -
// MARK: Any type
template<> struct Converter<IDLAny> : DefaultConverter<IDLAny> {
using ReturnType = JSC::JSValue;
static JSC::JSValue convert(JSC::ExecState&, JSC::JSValue value)
{
return value;
}
static JSC::JSValue convert(const JSC::Strong<JSC::Unknown>& value)
{
return value.get();
}
};
template<> struct JSConverter<IDLAny> {
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(const JSC::JSValue& value)
{
return value;
}
static JSC::JSValue convert(const JSC::Strong<JSC::Unknown>& value)
{
return value.get();
}
};
// MARK: -
// MARK: Nullable type
namespace Detail {
template<typename IDLType>
struct NullableConversionType;
template<typename IDLType>
struct NullableConversionType {
using Type = typename IDLNullable<IDLType>::ImplementationType;
};
template<typename T>
struct NullableConversionType<IDLInterface<T>> {
using Type = typename Converter<IDLInterface<T>>::ReturnType;
};
template<>
struct NullableConversionType<IDLAny> {
using Type = typename Converter<IDLAny>::ReturnType;
};
}
template<typename T> struct Converter<IDLNullable<T>> : DefaultConverter<IDLNullable<T>> {
using ReturnType = typename Detail::NullableConversionType<T>::Type;
// 1. If Type(V) is not Object, and the conversion to an IDL value is being performed
// due to V being assigned to an attribute whose type is a nullable callback function
// that is annotated with [TreatNonObjectAsNull], then return the IDL nullable type T?
// value null.
//
// NOTE: Handled elsewhere.
//
// 2. Otherwise, if V is null or undefined, then return the IDL nullable type T? value null.
// 3. Otherwise, return the result of converting V using the rules for the inner IDL type T.
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
if (value.isUndefinedOrNull())
return T::nullValue();
return Converter<T>::convert(state, value);
}
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value, JSC::JSObject& thisObject)
{
if (value.isUndefinedOrNull())
return T::nullValue();
return Converter<T>::convert(state, value, thisObject);
}
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value, JSDOMGlobalObject& globalObject)
{
if (value.isUndefinedOrNull())
return T::nullValue();
return Converter<T>::convert(state, value, globalObject);
}
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value, IntegerConversionConfiguration configuration)
{
if (value.isUndefinedOrNull())
return T::nullValue();
return Converter<T>::convert(state, value, configuration);
}
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value, StringConversionConfiguration configuration)
{
if (value.isUndefinedOrNull())
return T::nullValue();
return Converter<T>::convert(state, value, configuration);
}
template<typename ExceptionThrower = DefaultExceptionThrower>
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value, ExceptionThrower&& exceptionThrower)
{
if (value.isUndefinedOrNull())
return T::nullValue();
return Converter<T>::convert(state, value, std::forward<ExceptionThrower>(exceptionThrower));
}
template<typename ExceptionThrower = DefaultExceptionThrower>
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value, JSC::JSObject& thisObject, ExceptionThrower&& exceptionThrower)
{
if (value.isUndefinedOrNull())
return T::nullValue();
return Converter<T>::convert(state, value, thisObject, std::forward<ExceptionThrower>(exceptionThrower));
}
template<typename ExceptionThrower = DefaultExceptionThrower>
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value, JSDOMGlobalObject& globalObject, ExceptionThrower&& exceptionThrower)
{
if (value.isUndefinedOrNull())
return T::nullValue();
return Converter<T>::convert(state, value, globalObject, std::forward<ExceptionThrower>(exceptionThrower));
}
};
template<typename T> struct JSConverter<IDLNullable<T>> {
using ImplementationType = typename IDLNullable<T>::ImplementationType;
static constexpr bool needsState = JSConverter<T>::needsState;
static constexpr bool needsGlobalObject = JSConverter<T>::needsGlobalObject;
template<typename U>
static JSC::JSValue convert(U&& value)
{
if (T::isNullValue(value))
return JSC::jsNull();
return JSConverter<T>::convert(T::extractValueFromNullable(value));
}
template<typename U>
static JSC::JSValue convert(JSC::ExecState& state, U&& value)
{
if (T::isNullValue(value))
return JSC::jsNull();
return JSConverter<T>::convert(state, T::extractValueFromNullable(value));
}
template<typename U>
static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject& globalObject, U&& value)
{
if (T::isNullValue(value))
return JSC::jsNull();
return JSConverter<T>::convert(state, globalObject, T::extractValueFromNullable(value));
}
template<typename U>
static JSC::JSValue convertNewlyCreated(JSC::ExecState& state, JSDOMGlobalObject& globalObject, U&& value)
{
if (T::isNullValue(value))
return JSC::jsNull();
return JSConverter<T>::convert(state, globalObject, T::extractValueFromNullable(value));
}
};
// MARK: -
// MARK: Null type
template<> struct Converter<IDLNull> : DefaultConverter<IDLNull> {
static std::nullptr_t convert(JSC::ExecState&, JSC::JSValue)
{
return nullptr;
}
};
template<> struct JSConverter<IDLNull> {
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(std::nullptr_t)
{
return JSC::jsNull();
}
};
// MARK: -
// MARK: Boolean type
template<> struct Converter<IDLBoolean> : DefaultConverter<IDLBoolean> {
static bool convert(JSC::ExecState& state, JSC::JSValue value)
{
return value.toBoolean(&state);
}
};
template<> struct JSConverter<IDLBoolean> {
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(bool value)
{
return JSC::jsBoolean(value);
}
};
// ArrayBuffer support.
template<> struct JSDOMWrapperConverterTraits<JSC::ArrayBuffer> {
using WrapperClass = JSC::JSArrayBuffer;
using ToWrappedReturnType = JSC::ArrayBuffer*;
};
// ArrayBufferView support.
template<> struct JSDOMWrapperConverterTraits<JSC::ArrayBufferView> {
using WrapperClass = JSC::JSArrayBufferView;
using ToWrappedReturnType = RefPtr<ArrayBufferView>;
};
// Typed arrays support.
template<typename Adaptor> struct JSDOMWrapperConverterTraits<JSC::GenericTypedArrayView<Adaptor>> {
using WrapperClass = JSC::JSGenericTypedArrayView<Adaptor>;
using ToWrappedReturnType = RefPtr<JSC::GenericTypedArrayView<Adaptor>>;
};
// MARK: -
// MARK: Interface type
template<typename T> struct Converter<IDLInterface<T>> : DefaultConverter<IDLInterface<T>> {
using ReturnType = typename JSDOMWrapperConverterTraits<T>::ToWrappedReturnType;
using WrapperType = typename JSDOMWrapperConverterTraits<T>::WrapperClass;
template<typename ExceptionThrower = DefaultExceptionThrower>
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value, ExceptionThrower&& exceptionThrower = ExceptionThrower())
{
JSC::VM& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
ReturnType object = WrapperType::toWrapped(value);
if (UNLIKELY(!object))
exceptionThrower(state, scope);
return object;
}
};
namespace Detail {
template <typename T> inline T* getPtrOrRef(const T* p) { return const_cast<T*>(p); }
template <typename T> inline T& getPtrOrRef(const T& p) { return const_cast<T&>(p); }
template <typename T> inline T* getPtrOrRef(const RefPtr<T>& p) { return p.get(); }
template <typename T> inline T& getPtrOrRef(const Ref<T>& p) { return p.get(); }
}
template<typename T> struct JSConverter<IDLInterface<T>> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = true;
template <typename U>
static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const U& value)
{
return toJS(&state, &globalObject, Detail::getPtrOrRef(value));
}
template<typename U>
static JSC::JSValue convertNewlyCreated(JSC::ExecState& state, JSDOMGlobalObject& globalObject, U&& value)
{
return toJSNewlyCreated(&state, &globalObject, std::forward<U>(value));
}
};
// MARK: -
// MARK: Integer types
template<> struct Converter<IDLByte> : DefaultConverter<IDLByte> {
static int8_t convert(JSC::ExecState& state, JSC::JSValue value, IntegerConversionConfiguration configuration = IntegerConversionConfiguration::Normal)
{
switch (configuration) {
case IntegerConversionConfiguration::Normal:
break;
case IntegerConversionConfiguration::EnforceRange:
return toInt8EnforceRange(state, value);
case IntegerConversionConfiguration::Clamp:
return toInt8Clamp(state, value);
}
return toInt8(state, value);
}
};
template<> struct JSConverter<IDLByte> {
using Type = typename IDLByte::ImplementationType;
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(Type value)
{
return JSC::jsNumber(value);
}
};
template<> struct Converter<IDLOctet> : DefaultConverter<IDLOctet> {
static uint8_t convert(JSC::ExecState& state, JSC::JSValue value, IntegerConversionConfiguration configuration = IntegerConversionConfiguration::Normal)
{
switch (configuration) {
case IntegerConversionConfiguration::Normal:
break;
case IntegerConversionConfiguration::EnforceRange:
return toUInt8EnforceRange(state, value);
case IntegerConversionConfiguration::Clamp:
return toUInt8Clamp(state, value);
}
return toUInt8(state, value);
}
};
template<> struct JSConverter<IDLOctet> {
using Type = typename IDLOctet::ImplementationType;
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(Type value)
{
return JSC::jsNumber(value);
}
};
template<> struct Converter<IDLShort> : DefaultConverter<IDLShort> {
static int16_t convert(JSC::ExecState& state, JSC::JSValue value, IntegerConversionConfiguration configuration = IntegerConversionConfiguration::Normal)
{
switch (configuration) {
case IntegerConversionConfiguration::Normal:
break;
case IntegerConversionConfiguration::EnforceRange:
return toInt16EnforceRange(state, value);
case IntegerConversionConfiguration::Clamp:
return toInt16Clamp(state, value);
}
return toInt16(state, value);
}
};
template<> struct JSConverter<IDLShort> {
using Type = typename IDLShort::ImplementationType;
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(Type value)
{
return JSC::jsNumber(value);
}
};
template<> struct Converter<IDLUnsignedShort> : DefaultConverter<IDLUnsignedShort> {
static uint16_t convert(JSC::ExecState& state, JSC::JSValue value, IntegerConversionConfiguration configuration = IntegerConversionConfiguration::Normal)
{
switch (configuration) {
case IntegerConversionConfiguration::Normal:
break;
case IntegerConversionConfiguration::EnforceRange:
return toUInt16EnforceRange(state, value);
case IntegerConversionConfiguration::Clamp:
return toUInt16Clamp(state, value);
}
return toUInt16(state, value);
}
};
template<> struct JSConverter<IDLUnsignedShort> {
using Type = typename IDLUnsignedShort::ImplementationType;
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(Type value)
{
return JSC::jsNumber(value);
}
};
template<> struct Converter<IDLLong> : DefaultConverter<IDLLong> {
static inline int32_t convert(JSC::ExecState&, JSC::ThrowScope&, double number)
{
return JSC::toInt32(number);
}
static int32_t convert(JSC::ExecState& state, JSC::JSValue value, IntegerConversionConfiguration configuration = IntegerConversionConfiguration::Normal)
{
switch (configuration) {
case IntegerConversionConfiguration::Normal:
break;
case IntegerConversionConfiguration::EnforceRange:
return toInt32EnforceRange(state, value);
case IntegerConversionConfiguration::Clamp:
return toInt32Clamp(state, value);
}
return value.toInt32(&state);
}
};
template<> struct JSConverter<IDLLong> {
using Type = typename IDLLong::ImplementationType;
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(Type value)
{
return JSC::jsNumber(value);
}
};
template<> struct Converter<IDLUnsignedLong> : DefaultConverter<IDLUnsignedLong> {
static uint32_t convert(JSC::ExecState& state, JSC::JSValue value, IntegerConversionConfiguration configuration = IntegerConversionConfiguration::Normal)
{
switch (configuration) {
case IntegerConversionConfiguration::Normal:
break;
case IntegerConversionConfiguration::EnforceRange:
return toUInt32EnforceRange(state, value);
case IntegerConversionConfiguration::Clamp:
return toUInt32Clamp(state, value);
}
return value.toUInt32(&state);
}
};
template<> struct JSConverter<IDLUnsignedLong> {
using Type = typename IDLUnsignedLong::ImplementationType;
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(Type value)
{
return JSC::jsNumber(value);
}
};
template<> struct Converter<IDLLongLong> : DefaultConverter<IDLLongLong> {
static int64_t convert(JSC::ExecState& state, JSC::JSValue value, IntegerConversionConfiguration configuration = IntegerConversionConfiguration::Normal)
{
if (value.isInt32())
return value.asInt32();
switch (configuration) {
case IntegerConversionConfiguration::Normal:
break;
case IntegerConversionConfiguration::EnforceRange:
return toInt64EnforceRange(state, value);
case IntegerConversionConfiguration::Clamp:
return toInt64Clamp(state, value);
}
return toInt64(state, value);
}
};
template<> struct JSConverter<IDLLongLong> {
using Type = typename IDLLongLong::ImplementationType;
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(Type value)
{
return JSC::jsNumber(value);
}
};
template<> struct Converter<IDLUnsignedLongLong> : DefaultConverter<IDLUnsignedLongLong> {
static uint64_t convert(JSC::ExecState& state, JSC::JSValue value, IntegerConversionConfiguration configuration = IntegerConversionConfiguration::Normal)
{
if (value.isUInt32())
return value.asUInt32();
switch (configuration) {
case IntegerConversionConfiguration::Normal:
break;
case IntegerConversionConfiguration::EnforceRange:
return toUInt64EnforceRange(state, value);
case IntegerConversionConfiguration::Clamp:
return toUInt64Clamp(state, value);
}
return toUInt64(state, value);
}
};
template<> struct JSConverter<IDLUnsignedLongLong> {
using Type = typename IDLUnsignedLongLong::ImplementationType;
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(Type value)
{
return JSC::jsNumber(value);
}
};
// MARK: -
// MARK: Floating point types
template<> struct Converter<IDLFloat> : DefaultConverter<IDLFloat> {
static inline float convert(JSC::ExecState& state, JSC::ThrowScope& scope, double number)
{
if (UNLIKELY(!std::isfinite(number)))
throwNonFiniteTypeError(state, scope);
return static_cast<float>(number);
}
static float convert(JSC::ExecState& state, JSC::JSValue value)
{
JSC::VM& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
double number = value.toNumber(&state);
if (UNLIKELY(!std::isfinite(number)))
throwNonFiniteTypeError(state, scope);
return static_cast<float>(number);
}
};
template<> struct JSConverter<IDLFloat> {
using Type = typename IDLFloat::ImplementationType;
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(Type value)
{
return JSC::jsNumber(value);
}
};
template<> struct Converter<IDLUnrestrictedFloat> : DefaultConverter<IDLUnrestrictedFloat> {
static inline float convert(JSC::ExecState&, JSC::ThrowScope&, double number)
{
return static_cast<float>(number);
}
static float convert(JSC::ExecState& state, JSC::JSValue value)
{
return static_cast<float>(value.toNumber(&state));
}
};
template<> struct JSConverter<IDLUnrestrictedFloat> {
using Type = typename IDLUnrestrictedFloat::ImplementationType;
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(Type value)
{
return JSC::jsNumber(value);
}
};
template<> struct Converter<IDLDouble> : DefaultConverter<IDLDouble> {
static inline double convert(JSC::ExecState& state, JSC::ThrowScope& scope, double number)
{
if (UNLIKELY(!std::isfinite(number)))
throwNonFiniteTypeError(state, scope);
return number;
}
static double convert(JSC::ExecState& state, JSC::JSValue value)
{
JSC::VM& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
double number = value.toNumber(&state);
if (UNLIKELY(!std::isfinite(number)))
throwNonFiniteTypeError(state, scope);
return number;
}
};
template<> struct JSConverter<IDLDouble> {
using Type = typename IDLDouble::ImplementationType;
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(Type value)
{
return JSC::jsNumber(value);
}
};
template<> struct Converter<IDLUnrestrictedDouble> : DefaultConverter<IDLUnrestrictedDouble> {
static inline double convert(JSC::ExecState&, JSC::ThrowScope&, double number)
{
return number;
}
static double convert(JSC::ExecState& state, JSC::JSValue value)
{
return value.toNumber(&state);
}
};
template<> struct JSConverter<IDLUnrestrictedDouble> {
using Type = typename IDLUnrestrictedDouble::ImplementationType;
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(Type value)
{
return JSC::jsNumber(value);
}
// Add overload for MediaTime.
static JSC::JSValue convert(MediaTime value)
{
return JSC::jsNumber(value.toDouble());
}
};
// MARK: -
// MARK: String types
template<> struct Converter<IDLDOMString> : DefaultConverter<IDLDOMString> {
static String convert(JSC::ExecState& state, JSC::JSValue value, StringConversionConfiguration configuration = StringConversionConfiguration::Normal)
{
if (configuration == StringConversionConfiguration::TreatNullAsEmptyString && value.isNull())
return emptyString();
return value.toWTFString(&state);
}
};
template<> struct JSConverter<IDLDOMString> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(JSC::ExecState& state, const String& value)
{
return JSC::jsStringWithCache(&state, value);
}
};
template<> struct Converter<IDLByteString> : DefaultConverter<IDLByteString> {
static String convert(JSC::ExecState& state, JSC::JSValue value, StringConversionConfiguration configuration = StringConversionConfiguration::Normal)
{
if (configuration == StringConversionConfiguration::TreatNullAsEmptyString && value.isNull())
return emptyString();
return valueToByteString(state, value);
}
};
template<> struct JSConverter<IDLByteString> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(JSC::ExecState& state, const String& value)
{
return JSC::jsStringWithCache(&state, value);
}
};
template<> struct Converter<IDLUSVString> : DefaultConverter<IDLUSVString> {
static String convert(JSC::ExecState& state, JSC::JSValue value, StringConversionConfiguration configuration = StringConversionConfiguration::Normal)
{
if (configuration == StringConversionConfiguration::TreatNullAsEmptyString && value.isNull())
return emptyString();
return valueToUSVString(state, value);
}
};
template<> struct JSConverter<IDLUSVString> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(JSC::ExecState& state, const String& value)
{
return JSC::jsStringWithCache(&state, value);
}
};
// MARK: -
// MARK: Object type
template<> struct Converter<IDLObject> : DefaultConverter<IDLObject> {
template<typename ExceptionThrower = DefaultExceptionThrower>
static JSC::Strong<JSC::JSObject> convert(JSC::ExecState& state, JSC::JSValue value, ExceptionThrower&& exceptionThrower = ExceptionThrower())
{
JSC::VM& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (!value.isObject()) {
exceptionThrower(state, scope);
return { };
}
return { vm, JSC::asObject(value) };
}
};
// MARK: -
// MARK: Array-like types
namespace Detail {
template<typename IDLType>
struct GenericSequenceConverter {
using ReturnType = Vector<typename IDLType::ImplementationType>;
static ReturnType convert(JSC::ExecState& state, JSC::JSObject* jsObject)
{
ReturnType result;
forEachInIterable(&state, jsObject, [&result](JSC::VM& vm, JSC::ExecState* state, JSC::JSValue jsValue) {
auto scope = DECLARE_THROW_SCOPE(vm);
auto convertedValue = Converter<IDLType>::convert(*state, jsValue);
if (UNLIKELY(scope.exception()))
return;
result.append(WTFMove(convertedValue));
});
return result;
}
};
// Specialization for numeric types
// FIXME: This is only implemented for the IDLFloatingPointTypes and IDLLong. To add
// support for more numeric types, add an overload of Converter<IDLType>::convert that
// takes an ExecState, ThrowScope, double as its arguments.
template<typename IDLType>
struct NumericSequenceConverter {
using GenericConverter = GenericSequenceConverter<IDLType>;
using ReturnType = typename GenericConverter::ReturnType;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
auto& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (!value.isObject()) {
throwSequenceTypeError(state, scope);
return { };
}
JSC::JSObject* object = JSC::asObject(value);
if (!JSC::isJSArray(object))
return GenericConverter::convert(state, object);
JSC::JSArray* array = JSC::asArray(object);
if (!array->globalObject()->isArrayIteratorProtocolFastAndNonObservable())
return GenericConverter::convert(state, object);
unsigned length = array->length();
ReturnType result;
if (!result.tryReserveCapacity(length)) {
// FIXME: Is the right exception to throw?
throwTypeError(&state, scope);
return { };
}
JSC::IndexingType indexingType = array->indexingType() & JSC::IndexingShapeMask;
if (indexingType == JSC::ContiguousShape) {
for (unsigned i = 0; i < length; i++) {
auto indexValue = array->butterfly()->contiguous()[i].get();
if (!indexValue)
result.uncheckedAppend(0);
else {
auto convertedValue = Converter<IDLType>::convert(state, indexValue);
RETURN_IF_EXCEPTION(scope, { });
result.uncheckedAppend(convertedValue);
}
}
return result;
}
if (indexingType == JSC::Int32Shape) {
for (unsigned i = 0; i < length; i++) {
auto indexValue = array->butterfly()->contiguousInt32()[i].get();
ASSERT(!indexValue || indexValue.isInt32());
if (!indexValue)
result.uncheckedAppend(0);
else
result.uncheckedAppend(indexValue.asInt32());
}
return result;
}
if (indexingType == JSC::DoubleShape) {
for (unsigned i = 0; i < length; i++) {
auto doubleValue = array->butterfly()->contiguousDouble()[i];
if (std::isnan(doubleValue))
result.uncheckedAppend(0);
else {
auto convertedValue = Converter<IDLType>::convert(state, scope, doubleValue);
RETURN_IF_EXCEPTION(scope, { });
result.uncheckedAppend(convertedValue);
}
}
return result;
}
for (unsigned i = 0; i < length; i++) {
auto indexValue = array->getDirectIndex(&state, i);
RETURN_IF_EXCEPTION(scope, { });
if (!indexValue)
result.uncheckedAppend(0);
else {
auto convertedValue = Converter<IDLType>::convert(state, indexValue);
RETURN_IF_EXCEPTION(scope, { });
result.uncheckedAppend(convertedValue);
}
}
return result;
}
};
template<typename IDLType>
struct SequenceConverter {
using GenericConverter = GenericSequenceConverter<IDLType>;
using ReturnType = typename GenericConverter::ReturnType;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
auto& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (!value.isObject()) {
throwSequenceTypeError(state, scope);
return { };
}
JSC::JSObject* object = JSC::asObject(value);
if (!JSC::isJSArray(object))
return GenericConverter::convert(state, object);
JSC::JSArray* array = JSC::asArray(object);
if (!array->globalObject()->isArrayIteratorProtocolFastAndNonObservable())
return GenericConverter::convert(state, object);
unsigned length = array->length();
ReturnType result;
if (!result.tryReserveCapacity(length)) {
// FIXME: Is the right exception to throw?
throwTypeError(&state, scope);
return { };
}
JSC::IndexingType indexingType = array->indexingType() & JSC::IndexingShapeMask;
if (indexingType == JSC::ContiguousShape) {
for (unsigned i = 0; i < length; i++) {
auto indexValue = array->butterfly()->contiguous()[i].get();
if (!indexValue)
indexValue = JSC::jsUndefined();
auto convertedValue = Converter<IDLType>::convert(state, indexValue);
RETURN_IF_EXCEPTION(scope, { });
result.uncheckedAppend(convertedValue);
}
return result;
}
for (unsigned i = 0; i < length; i++) {
auto indexValue = array->getDirectIndex(&state, i);
RETURN_IF_EXCEPTION(scope, { });
if (!indexValue)
indexValue = JSC::jsUndefined();
auto convertedValue = Converter<IDLType>::convert(state, indexValue);
RETURN_IF_EXCEPTION(scope, { });
result.uncheckedAppend(convertedValue);
}
return result;
}
};
template<>
struct SequenceConverter<IDLLong> {
using ReturnType = typename GenericSequenceConverter<IDLLong>::ReturnType;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
return NumericSequenceConverter<IDLLong>::convert(state, value);
}
};
template<>
struct SequenceConverter<IDLFloat> {
using ReturnType = typename GenericSequenceConverter<IDLFloat>::ReturnType;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
return NumericSequenceConverter<IDLFloat>::convert(state, value);
}
};
template<>
struct SequenceConverter<IDLUnrestrictedFloat> {
using ReturnType = typename GenericSequenceConverter<IDLUnrestrictedFloat>::ReturnType;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
return NumericSequenceConverter<IDLUnrestrictedFloat>::convert(state, value);
}
};
template<>
struct SequenceConverter<IDLDouble> {
using ReturnType = typename GenericSequenceConverter<IDLDouble>::ReturnType;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
return NumericSequenceConverter<IDLDouble>::convert(state, value);
}
};
template<>
struct SequenceConverter<IDLUnrestrictedDouble> {
using ReturnType = typename GenericSequenceConverter<IDLUnrestrictedDouble>::ReturnType;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
return NumericSequenceConverter<IDLUnrestrictedDouble>::convert(state, value);
}
};
}
template<typename T> struct Converter<IDLSequence<T>> : DefaultConverter<IDLSequence<T>> {
using ReturnType = typename Detail::SequenceConverter<T>::ReturnType;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
return Detail::SequenceConverter<T>::convert(state, value);
}
};
template<typename T> struct JSConverter<IDLSequence<T>> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = true;
template<typename U, size_t inlineCapacity>
static JSC::JSValue convert(JSC::ExecState& exec, JSDOMGlobalObject& globalObject, const Vector<U, inlineCapacity>& vector)
{
JSC::MarkedArgumentBuffer list;
for (auto& element : vector)
list.append(toJS<T>(exec, globalObject, element));
return JSC::constructArray(&exec, nullptr, &globalObject, list);
}
};
template<typename T> struct Converter<IDLFrozenArray<T>> : DefaultConverter<IDLFrozenArray<T>> {
using ReturnType = typename Detail::SequenceConverter<T>::ReturnType;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
return Detail::SequenceConverter<T>::convert(state, value);
}
};
template<typename T> struct JSConverter<IDLFrozenArray<T>> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = true;
template<typename U, size_t inlineCapacity>
static JSC::JSValue convert(JSC::ExecState& exec, JSDOMGlobalObject& globalObject, const Vector<U, inlineCapacity>& vector)
{
JSC::MarkedArgumentBuffer list;
for (auto& element : vector)
list.append(toJS<T>(exec, globalObject, element));
auto* array = JSC::constructArray(&exec, nullptr, &globalObject, list);
return JSC::objectConstructorFreeze(&exec, array);
}
};
// MARK: -
// MARK: Record type
namespace Detail {
template<typename IDLStringType>
struct IdentifierConverter;
template<> struct IdentifierConverter<IDLDOMString> {
static String convert(JSC::ExecState&, const JSC::Identifier& identifier)
{
return identifier.string();
}
};
template<> struct IdentifierConverter<IDLByteString> {
static String convert(JSC::ExecState& state, const JSC::Identifier& identifier)
{
return identifierToByteString(state, identifier);
}
};
template<> struct IdentifierConverter<IDLUSVString> {
static String convert(JSC::ExecState& state, const JSC::Identifier& identifier)
{
return identifierToUSVString(state, identifier);
}
};
}
template<typename K, typename V> struct Converter<IDLRecord<K, V>> : DefaultConverter<IDLRecord<K, V>> {
using ReturnType = typename IDLRecord<K, V>::ImplementationType;
using KeyType = typename K::ImplementationType;
using ValueType = typename V::ImplementationType;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
auto& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
// 1. Let result be a new empty instance of record<K, V>.
// 2. If Type(O) is Undefined or Null, return result.
if (value.isUndefinedOrNull())
return { };
// 3. If Type(O) is not Object, throw a TypeError.
if (!value.isObject()) {
throwTypeError(&state, scope);
return { };
}
JSC::JSObject* object = JSC::asObject(value);
ReturnType result;
// 4. Let keys be ? O.[[OwnPropertyKeys]]().
JSC::PropertyNameArray keys(&vm, JSC::PropertyNameMode::Strings);
object->getOwnPropertyNames(object, &state, keys, JSC::EnumerationMode());
RETURN_IF_EXCEPTION(scope, { });
// 5. Repeat, for each element key of keys in List order:
for (auto& key : keys) {
// 1. Let desc be ? O.[[GetOwnProperty]](key).
JSC::PropertyDescriptor descriptor;
bool didGetDescriptor = object->getOwnPropertyDescriptor(&state, key, descriptor);
RETURN_IF_EXCEPTION(scope, { });
if (!didGetDescriptor)
continue;
// 2. If desc is not undefined and desc.[[Enumerable]] is true:
// FIXME: Do we need to check for enumerable / undefined, or is this handled by the default
// enumeration mode?
if (!descriptor.value().isUndefined() && descriptor.enumerable()) {
// 1. Let typedKey be key converted to an IDL value of type K.
auto typedKey = Detail::IdentifierConverter<K>::convert(state, key);
// 2. Let value be ? Get(O, key).
auto subValue = object->get(&state, key);
RETURN_IF_EXCEPTION(scope, { });
// 3. Let typedValue be value converted to an IDL value of type V.
auto typedValue = Converter<V>::convert(state, subValue);
RETURN_IF_EXCEPTION(scope, { });
// 4. If typedKey is already a key in result, set its value to typedValue.
// Note: This can happen when O is a proxy object.
// 5. Otherwise, append to result a mapping (typedKey, typedValue).
result.set(typedKey, typedValue);
}
}
// 6. Return result.
return result;
}
};
template<typename K, typename V> struct JSConverter<IDLRecord<K, V>> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = true;
template<typename ValueType>
static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const HashMap<String, ValueType>& map)
{
auto& vm = state.vm();
// 1. Let result be ! ObjectCreate(%ObjectPrototype%).
auto result = constructEmptyObject(&state);
// 2. Repeat, for each mapping (key, value) in D:
for (const auto& keyValuePair : map) {
// 1. Let esKey be key converted to an ECMAScript value.
// Note, this step is not required, as we need the key to be
// an Identifier, not a JSValue.
// 2. Let esValue be value converted to an ECMAScript value.
auto esValue = toJS<V>(state, globalObject, keyValuePair.value);
// 3. Let created be ! CreateDataProperty(result, esKey, esValue).
bool created = result->putDirect(vm, JSC::Identifier::fromString(&vm, keyValuePair.key), esValue);
// 4. Assert: created is true.
ASSERT_UNUSED(created, created);
}
// 3. Return result.
return result;
}
};
// MARK: -
// MARK: Dictionary type
template<typename T> struct Converter<IDLDictionary<T>> : DefaultConverter<IDLDictionary<T>> {
using ReturnType = T;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
return convertDictionary<T>(state, value);
}
};
template<typename T> struct JSConverter<IDLDictionary<T>> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = true;
static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const T& dictionary)
{
return convertDictionaryToJS(state, globalObject, dictionary);
}
};
// MARK: -
// MARK: Enumeration type
template<typename T> struct Converter<IDLEnumeration<T>> : DefaultConverter<IDLEnumeration<T>> {
static T convert(JSC::ExecState& state, JSC::JSValue value)
{
return convertEnumeration<T>(state, value);
}
};
template<typename T> struct JSConverter<IDLEnumeration<T>> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(JSC::ExecState& exec, T value)
{
return convertEnumerationToJS(exec, value);
}
};
// MARK: -
// MARK: Callback function type
template<typename T> struct Converter<IDLCallbackFunction<T>> : DefaultConverter<IDLCallbackFunction<T>> {
template<typename ExceptionThrower = DefaultExceptionThrower>
static RefPtr<T> convert(JSC::ExecState& state, JSC::JSValue value, JSDOMGlobalObject& globalObject, ExceptionThrower&& exceptionThrower = ExceptionThrower())
{
JSC::VM& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (!value.isFunction()) {
exceptionThrower(state, scope);
return nullptr;
}
return T::create(JSC::asObject(value), &globalObject);
}
};
template<typename T> struct JSConverter<IDLCallbackFunction<T>> {
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
template <typename U>
static JSC::JSValue convert(const U& value)
{
return toJS(Detail::getPtrOrRef(value));
}
template<typename U>
static JSC::JSValue convertNewlyCreated(U&& value)
{
return toJSNewlyCreated(std::forward<U>(value));
}
};
// MARK: -
// MARK: Callback interface type
template<typename T> struct Converter<IDLCallbackInterface<T>> : DefaultConverter<IDLCallbackInterface<T>> {
template<typename ExceptionThrower = DefaultExceptionThrower>
static RefPtr<T> convert(JSC::ExecState& state, JSC::JSValue value, JSDOMGlobalObject& globalObject, ExceptionThrower&& exceptionThrower = ExceptionThrower())
{
JSC::VM& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (!value.isObject()) {
exceptionThrower(state, scope);
return nullptr;
}
return T::create(JSC::asObject(value), &globalObject);
}
};
template<typename T> struct JSConverter<IDLCallbackInterface<T>> {
static constexpr bool needsState = false;
static constexpr bool needsGlobalObject = false;
template <typename U>
static JSC::JSValue convert(const U& value)
{
return toJS(Detail::getPtrOrRef(value));
}
template<typename U>
static JSC::JSValue convertNewlyCreated(U&& value)
{
return toJSNewlyCreated(std::forward<U>(value));
}
};
// MARK: -
// MARK: Union type
template<typename ReturnType, typename T, bool enabled>
struct ConditionalConverter;
template<typename ReturnType, typename T>
struct ConditionalConverter<ReturnType, T, true> {
static std::optional<ReturnType> convert(JSC::ExecState& state, JSC::JSValue value)
{
return ReturnType(Converter<T>::convert(state, value));
}
};
template<typename ReturnType, typename T>
struct ConditionalConverter<ReturnType, T, false> {
static std::optional<ReturnType> convert(JSC::ExecState&, JSC::JSValue)
{
return std::nullopt;
}
};
namespace Detail {
template<typename List, bool condition>
struct ConditionalFront;
template<typename List>
struct ConditionalFront<List, true>
{
using type = brigand::front<List>;
};
template<typename List>
struct ConditionalFront<List, false>
{
using type = void;
};
}
template<typename List, bool condition>
using ConditionalFront = typename Detail::ConditionalFront<List, condition>::type;
template<typename... T> struct Converter<IDLUnion<T...>> : DefaultConverter<IDLUnion<T...>> {
using Type = IDLUnion<T...>;
using TypeList = typename Type::TypeList;
using ReturnType = typename Type::ImplementationType;
using NumericTypeList = brigand::filter<TypeList, IsIDLNumber<brigand::_1>>;
static constexpr size_t numberOfNumericTypes = brigand::size<NumericTypeList>::value;
static_assert(numberOfNumericTypes == 0 || numberOfNumericTypes == 1, "There can be 0 or 1 numeric types in an IDLUnion.");
using NumericType = ConditionalFront<NumericTypeList, numberOfNumericTypes != 0>;
// FIXME: This should also check for IDLEnumeration<T>.
using StringTypeList = brigand::filter<TypeList, std::is_base_of<IDLString, brigand::_1>>;
static constexpr size_t numberOfStringTypes = brigand::size<StringTypeList>::value;
static_assert(numberOfStringTypes == 0 || numberOfStringTypes == 1, "There can be 0 or 1 string types in an IDLUnion.");
using StringType = ConditionalFront<StringTypeList, numberOfStringTypes != 0>;
using SequenceTypeList = brigand::filter<TypeList, IsIDLSequence<brigand::_1>>;
static constexpr size_t numberOfSequenceTypes = brigand::size<SequenceTypeList>::value;
static_assert(numberOfSequenceTypes == 0 || numberOfSequenceTypes == 1, "There can be 0 or 1 sequence types in an IDLUnion.");
using SequenceType = ConditionalFront<SequenceTypeList, numberOfSequenceTypes != 0>;
using FrozenArrayTypeList = brigand::filter<TypeList, IsIDLFrozenArray<brigand::_1>>;
static constexpr size_t numberOfFrozenArrayTypes = brigand::size<FrozenArrayTypeList>::value;
static_assert(numberOfFrozenArrayTypes == 0 || numberOfFrozenArrayTypes == 1, "There can be 0 or 1 FrozenArray types in an IDLUnion.");
using FrozenArrayType = ConditionalFront<FrozenArrayTypeList, numberOfFrozenArrayTypes != 0>;
using DictionaryTypeList = brigand::filter<TypeList, IsIDLDictionary<brigand::_1>>;
static constexpr size_t numberOfDictionaryTypes = brigand::size<DictionaryTypeList>::value;
static_assert(numberOfDictionaryTypes == 0 || numberOfDictionaryTypes == 1, "There can be 0 or 1 dictionary types in an IDLUnion.");
static constexpr bool hasDictionaryType = numberOfDictionaryTypes != 0;
using DictionaryType = ConditionalFront<DictionaryTypeList, hasDictionaryType>;
using RecordTypeList = brigand::filter<TypeList, IsIDLRecord<brigand::_1>>;
static constexpr size_t numberOfRecordTypes = brigand::size<RecordTypeList>::value;
static_assert(numberOfRecordTypes == 0 || numberOfRecordTypes == 1, "There can be 0 or 1 record types in an IDLUnion.");
static constexpr bool hasRecordType = numberOfRecordTypes != 0;
using RecordType = ConditionalFront<RecordTypeList, hasRecordType>;
static constexpr bool hasObjectType = (numberOfSequenceTypes + numberOfFrozenArrayTypes + numberOfDictionaryTypes + numberOfRecordTypes) > 0;
using InterfaceTypeList = brigand::filter<TypeList, IsIDLInterface<brigand::_1>>;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
auto scope = DECLARE_THROW_SCOPE(state.vm());
// 1. If the union type includes a nullable type and V is null or undefined, then return the IDL value null.
constexpr bool hasNullType = brigand::any<TypeList, std::is_same<IDLNull, brigand::_1>>::value;
if (hasNullType) {
if (value.isUndefinedOrNull())
return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, IDLNull, hasNullType>::convert(state, value).value());
}
// 2. Let types be the flattened member types of the union type.
// NOTE: Union is expected to be pre-flattented.
// 3. If V is null or undefined then:
if (hasDictionaryType || hasRecordType) {
if (value.isUndefinedOrNull()) {
// 1. If types includes a dictionary type, then return the result of converting V to that dictionary type.
if (hasDictionaryType)
return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, DictionaryType, hasDictionaryType>::convert(state, value).value());
// 2. If types includes a record type, then return the result of converting V to that record type.
if (hasRecordType)
return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, RecordType, hasRecordType>::convert(state, value).value());
}
}
// 4. If V is a platform object, then:
// 1. If types includes an interface type that V implements, then return the IDL value that is a reference to the object V.
// 2. If types includes object, then return the IDL value that is a reference to the object V.
// (FIXME: Add support for object and step 4.2)
if (brigand::any<TypeList, IsIDLInterface<brigand::_1>>::value) {
std::optional<ReturnType> returnValue;
brigand::for_each<InterfaceTypeList>([&](auto&& type) {
if (returnValue)
return;
using Type = typename WTF::RemoveCVAndReference<decltype(type)>::type::type;
using ImplementationType = typename Type::ImplementationType;
using RawType = typename Type::RawType;
using WrapperType = typename JSDOMWrapperConverterTraits<RawType>::WrapperClass;
auto castedValue = WrapperType::toWrapped(value);
if (!castedValue)
return;
returnValue = ReturnType(ImplementationType(castedValue));
});
if (returnValue)
return WTFMove(returnValue.value());
}
// FIXME: Add support for steps 5 - 10.
// 11. If V is any kind of object, then:
if (hasObjectType) {
if (value.isCell()) {
JSC::JSCell* cell = value.asCell();
if (cell->isObject()) {
// FIXME: We should be able to optimize the following code by making use
// of the fact that we have proved that the value is an object.
// 1. If types includes a sequence type, then:
// 1. Let method be the result of GetMethod(V, @@iterator).
// 2. ReturnIfAbrupt(method).
// 3. If method is not undefined, return the result of creating a
// sequence of that type from V and method.
constexpr bool hasSequenceType = numberOfSequenceTypes != 0;
if (hasSequenceType) {
bool hasIterator = hasIteratorMethod(state, value);
RETURN_IF_EXCEPTION(scope, ReturnType());
if (hasIterator)
return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, SequenceType, hasSequenceType>::convert(state, value).value());
}
// 2. If types includes a frozen array type, then:
// 1. Let method be the result of GetMethod(V, @@iterator).
// 2. ReturnIfAbrupt(method).
// 3. If method is not undefined, return the result of creating a
// frozen array of that type from V and method.
constexpr bool hasFrozenArrayType = numberOfFrozenArrayTypes != 0;
if (hasFrozenArrayType) {
bool hasIterator = hasIteratorMethod(state, value);
RETURN_IF_EXCEPTION(scope, ReturnType());
if (hasIterator)
return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, FrozenArrayType, hasFrozenArrayType>::convert(state, value).value());
}
// 3. If types includes a dictionary type, then return the result of
// converting V to that dictionary type.
if (hasDictionaryType)
return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, DictionaryType, hasDictionaryType>::convert(state, value).value());
// 4. If types includes a record type, then return the result of converting V to that record type.
if (hasRecordType)
return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, RecordType, hasRecordType>::convert(state, value).value());
// 5. If types includes a callback interface type, then return the result of converting V to that interface type.
// (FIXME: Add support for callback interface type and step 12.5)
// 6. If types includes object, then return the IDL value that is a reference to the object V.
// (FIXME: Add support for object and step 12.6)
}
}
}
// 12. If V is a Boolean value, then:
// 1. If types includes a boolean, then return the result of converting V to boolean.
constexpr bool hasBooleanType = brigand::any<TypeList, std::is_same<IDLBoolean, brigand::_1>>::value;
if (hasBooleanType) {
if (value.isBoolean())
return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, IDLBoolean, hasBooleanType>::convert(state, value).value());
}
// 13. If V is a Number value, then:
// 1. If types includes a numeric type, then return the result of converting V to that numeric type.
constexpr bool hasNumericType = brigand::size<NumericTypeList>::value != 0;
if (hasNumericType) {
if (value.isNumber())
return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, NumericType, hasNumericType>::convert(state, value).value());
}
// 14. If types includes a string type, then return the result of converting V to that type.
constexpr bool hasStringType = brigand::size<StringTypeList>::value != 0;
if (hasStringType)
return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, StringType, hasStringType>::convert(state, value).value());
// 15. If types includes a numeric type, then return the result of converting V to that numeric type.
if (hasNumericType)
return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, NumericType, hasNumericType>::convert(state, value).value());
// 16. If types includes a boolean, then return the result of converting V to boolean.
if (hasBooleanType)
return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, IDLBoolean, hasBooleanType>::convert(state, value).value());
// 17. Throw a TypeError.
throwTypeError(&state, scope);
return ReturnType();
}
};
template<typename... T> struct JSConverter<IDLUnion<T...>> {
using Type = IDLUnion<T...>;
using TypeList = typename Type::TypeList;
using ImplementationType = typename Type::ImplementationType;
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = true;
using Sequence = brigand::make_sequence<brigand::ptrdiff_t<0>, WTF::variant_size<ImplementationType>::value>;
static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const ImplementationType& variant)
{
auto index = variant.index();
std::optional<JSC::JSValue> returnValue;
brigand::for_each<Sequence>([&](auto&& type) {
using I = typename WTF::RemoveCVAndReference<decltype(type)>::type::type;
if (I::value == index) {
ASSERT(!returnValue);
returnValue = toJS<brigand::at<TypeList, I>>(state, globalObject, WTF::get<I::value>(variant));
}
});
ASSERT(returnValue);
return returnValue.value();
}
};
// MARK: -
// MARK: Date type
template<> struct Converter<IDLDate> : DefaultConverter<IDLDate> {
static double convert(JSC::ExecState& state, JSC::JSValue value)
{
return valueToDate(&state, value);
}
};
template<> struct JSConverter<IDLDate> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(JSC::ExecState& state, double value)
{
return jsDate(&state, value);
}
};
// MARK: -
// MARK: IDLJSON type
template<> struct Converter<IDLJSON> : DefaultConverter<IDLJSON> {
static String convert(JSC::ExecState& state, JSC::JSValue value)
{
return JSC::JSONStringify(&state, value, 0);
}
};
template<> struct JSConverter<IDLJSON> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = false;
static JSC::JSValue convert(JSC::ExecState& state, const String& value)
{
return JSC::JSONParse(&state, value);
}
};
// MARK: -
// MARK: SerializedScriptValue type
template<typename T> struct Converter<IDLSerializedScriptValue<T>> : DefaultConverter<IDLSerializedScriptValue<T>> {
static RefPtr<T> convert(JSC::ExecState& state, JSC::JSValue value)
{
return T::create(state, value);
}
};
template<typename T> struct JSConverter<IDLSerializedScriptValue<T>> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = true;
static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject& globalObject, RefPtr<T> value)
{
return value ? value->deserialize(state, &globalObject) : JSC::jsNull();
}
};
// MARK: -
// MARK: Event Listener type
template<typename T> struct Converter<IDLEventListener<T>> : DefaultConverter<IDLEventListener<T>> {
using ReturnType = RefPtr<T>;
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value, JSC::JSObject& thisObject)
{
auto scope = DECLARE_THROW_SCOPE(state.vm());
auto listener = T::create(value, thisObject, false, currentWorld(&state));
if (!listener)
throwTypeError(&state, scope);
return listener;
}
};
// MARK: -
// MARK: XPathNSResolver type
template<typename T> struct Converter<IDLXPathNSResolver<T>> : DefaultConverter<IDLXPathNSResolver<T>> {
using ReturnType = RefPtr<T>;
using WrapperType = typename JSDOMWrapperConverterTraits<T>::WrapperClass;
template<typename ExceptionThrower = DefaultExceptionThrower>
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value, ExceptionThrower&& exceptionThrower = ExceptionThrower())
{
JSC::VM& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
ReturnType object = WrapperType::toWrapped(state, value);
if (UNLIKELY(!object))
exceptionThrower(state, scope);
return object;
}
};
template<typename T> struct JSConverter<IDLXPathNSResolver<T>> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = true;
template <typename U>
static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const U& value)
{
return toJS(&state, &globalObject, Detail::getPtrOrRef(value));
}
template<typename U>
static JSC::JSValue convertNewlyCreated(JSC::ExecState& state, JSDOMGlobalObject& globalObject, U&& value)
{
return toJSNewlyCreated(&state, &globalObject, std::forward<U>(value));
}
};
// MARK: -
// MARK: IDLIDBKey type
template<> struct JSConverter<IDLIDBKey> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = true;
template <typename U>
static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject& globalObject, U&& value)
{
return toJS(state, globalObject, std::forward<U>(value));
}
};
#if ENABLE(WEBGL)
// MARK: -
// MARK: IDLWebGLAny type
template<> struct JSConverter<IDLWebGLAny> {
static constexpr bool needsState = true;
static constexpr bool needsGlobalObject = true;
static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const WebGLAny& value)
{
return convertToJSValue(state, globalObject, value);
}
};
#endif
// MARK: -
// MARK: Support for variadic tail convertions
namespace Detail {
template<typename IDLType>
struct VariadicConverterBase;
template<typename IDLType>
struct VariadicConverterBase {
using Item = typename IDLType::ImplementationType;
static std::optional<Item> convert(JSC::ExecState& state, JSC::JSValue value)
{
auto& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto result = Converter<IDLType>::convert(state, value);
RETURN_IF_EXCEPTION(scope, std::nullopt);
return WTFMove(result);
}
};
template<typename T>
struct VariadicConverterBase<IDLInterface<T>> {
using Item = std::reference_wrapper<T>;
static std::optional<Item> convert(JSC::ExecState& state, JSC::JSValue value)
{
auto* result = Converter<IDLInterface<T>>::convert(state, value);
if (!result)
return std::nullopt;
return std::optional<Item>(*result);
}
};
template<typename IDLType>
struct VariadicConverter : VariadicConverterBase<IDLType> {
using Item = typename VariadicConverterBase<IDLType>::Item;
using Container = Vector<Item>;
struct Result {
size_t argumentIndex;
std::optional<Container> arguments;
};
};
}
template<typename IDLType> typename Detail::VariadicConverter<IDLType>::Result convertVariadicArguments(JSC::ExecState& state, size_t startIndex)
{
size_t length = state.argumentCount();
if (startIndex > length)
return { 0, std::nullopt };
typename Detail::VariadicConverter<IDLType>::Container result;
result.reserveInitialCapacity(length - startIndex);
for (size_t i = startIndex; i < length; ++i) {
auto value = Detail::VariadicConverter<IDLType>::convert(state, state.uncheckedArgument(i));
if (!value)
return { i, std::nullopt };
result.uncheckedAppend(WTFMove(*value));
}
return { length, WTFMove(result) };
}
} // namespace WebCore