diff options
Diffstat (limited to 'src/bun.js/bindings/webcore/JSDOMConvertSequences.h')
-rw-r--r-- | src/bun.js/bindings/webcore/JSDOMConvertSequences.h | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/src/bun.js/bindings/webcore/JSDOMConvertSequences.h b/src/bun.js/bindings/webcore/JSDOMConvertSequences.h new file mode 100644 index 000000000..a08336f64 --- /dev/null +++ b/src/bun.js/bindings/webcore/JSDOMConvertSequences.h @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2016-2017 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 "IDLTypes.h" +#include "JSDOMConvertBase.h" +#include "JSDOMConvertNumbers.h" +#include "JSDOMGlobalObject.h" +#include "JavaScriptCore/IteratorOperations.h" +#include "JavaScriptCore/JSArray.h" +#include "JavaScriptCore/JSGlobalObjectInlines.h" +#include "JavaScriptCore/ObjectConstructor.h" + +namespace WebCore { + +namespace Detail { + +template<typename IDLType> +struct GenericSequenceConverter { + using ReturnType = Vector<typename IDLType::SequenceStorageType>; + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object) + { + return convert(lexicalGlobalObject, object, ReturnType()); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, ReturnType&& result) + { + forEachInIterable(&lexicalGlobalObject, object, [&result](JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSValue nextValue) { + auto scope = DECLARE_THROW_SCOPE(vm); + + auto convertedValue = Converter<IDLType>::convert(*lexicalGlobalObject, nextValue); + if (UNLIKELY(scope.exception())) + return; + result.append(WTFMove(convertedValue)); + }); + return WTFMove(result); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method) + { + return convert(lexicalGlobalObject, object, method, ReturnType()); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method, ReturnType&& result) + { + forEachInIterable(lexicalGlobalObject, object, method, [&result](JSC::VM& vm, JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue nextValue) { + auto scope = DECLARE_THROW_SCOPE(vm); + + auto convertedValue = Converter<IDLType>::convert(lexicalGlobalObject, nextValue); + if (UNLIKELY(scope.exception())) + return; + result.append(WTFMove(convertedValue)); + }); + return WTFMove(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 a JSGlobalObject, ThrowScope and double as its arguments. +template<typename IDLType> +struct NumericSequenceConverter { + using GenericConverter = GenericSequenceConverter<IDLType>; + using ReturnType = typename GenericConverter::ReturnType; + + static ReturnType convertArray(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, JSC::JSArray* array, unsigned length, JSC::IndexingType indexingType, ReturnType&& result) + { + if (indexingType == JSC::Int32Shape) { + for (unsigned i = 0; i < length; i++) { + auto indexValue = array->butterfly()->contiguousInt32().at(array, i).get(); + ASSERT(!indexValue || indexValue.isInt32()); + if (!indexValue) + result.uncheckedAppend(0); + else + result.uncheckedAppend(indexValue.asInt32()); + } + return WTFMove(result); + } + + ASSERT(indexingType == JSC::DoubleShape); + for (unsigned i = 0; i < length; i++) { + double doubleValue = array->butterfly()->contiguousDouble().at(array, i); + if (std::isnan(doubleValue)) + result.uncheckedAppend(0); + else { + auto convertedValue = Converter<IDLType>::convert(lexicalGlobalObject, scope, doubleValue); + RETURN_IF_EXCEPTION(scope, {}); + + result.uncheckedAppend(convertedValue); + } + } + return WTFMove(result); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value) + { + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (!value.isObject()) { + throwSequenceTypeError(lexicalGlobalObject, scope); + return {}; + } + + JSC::JSObject* object = JSC::asObject(value); + if (!JSC::isJSArray(object)) + RELEASE_AND_RETURN(scope, GenericConverter::convert(lexicalGlobalObject, object)); + + JSC::JSArray* array = JSC::asArray(object); + if (!array->isIteratorProtocolFastAndNonObservable()) + RELEASE_AND_RETURN(scope, GenericConverter::convert(lexicalGlobalObject, object)); + + unsigned length = array->length(); + ReturnType result; + // If we're not an int32/double array, it's possible that converting a + // JSValue to a number could cause the iterator protocol to change, hence, + // we may need more capacity, or less. In such cases, we use the length + // as a proxy for the capacity we will most likely need (it's unlikely that + // a program is written with a valueOf that will augment the iterator protocol). + // If we are an int32/double array, then length is precisely the capacity we need. + if (!result.tryReserveCapacity(length)) { + // FIXME: Is the right exception to throw? + throwTypeError(&lexicalGlobalObject, scope); + return {}; + } + + JSC::IndexingType indexingType = array->indexingType() & JSC::IndexingShapeMask; + if (indexingType != JSC::Int32Shape && indexingType != JSC::DoubleShape) + RELEASE_AND_RETURN(scope, GenericConverter::convert(lexicalGlobalObject, object, WTFMove(result))); + + return convertArray(lexicalGlobalObject, scope, array, length, indexingType, WTFMove(result)); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method) + { + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (!JSC::isJSArray(object)) + RELEASE_AND_RETURN(scope, GenericConverter::convert(lexicalGlobalObject, object, method)); + + JSC::JSArray* array = JSC::asArray(object); + if (!array->isIteratorProtocolFastAndNonObservable()) + RELEASE_AND_RETURN(scope, GenericConverter::convert(lexicalGlobalObject, object, method)); + + unsigned length = array->length(); + ReturnType result; + // If we're not an int32/double array, it's possible that converting a + // JSValue to a number could cause the iterator protocol to change, hence, + // we may need more capacity, or less. In such cases, we use the length + // as a proxy for the capacity we will most likely need (it's unlikely that + // a program is written with a valueOf that will augment the iterator protocol). + // If we are an int32/double array, then length is precisely the capacity we need. + if (!result.tryReserveCapacity(length)) { + // FIXME: Is the right exception to throw? + throwTypeError(&lexicalGlobalObject, scope); + return {}; + } + + JSC::IndexingType indexingType = array->indexingType() & JSC::IndexingShapeMask; + if (indexingType != JSC::Int32Shape && indexingType != JSC::DoubleShape) + RELEASE_AND_RETURN(scope, GenericConverter::convert(lexicalGlobalObject, object, method, WTFMove(result))); + + return convertArray(lexicalGlobalObject, scope, array, length, indexingType, WTFMove(result)); + } +}; + +template<typename IDLType> +struct SequenceConverter { + using GenericConverter = GenericSequenceConverter<IDLType>; + using ReturnType = typename GenericConverter::ReturnType; + + static ReturnType convertArray(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSArray* array) + { + auto& vm = lexicalGlobalObject.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + unsigned length = array->length(); + + ReturnType result; + if (!result.tryReserveCapacity(length)) { + // FIXME: Is the right exception to throw? + throwTypeError(&lexicalGlobalObject, 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().at(array, i).get(); + if (!indexValue) + indexValue = JSC::jsUndefined(); + + auto convertedValue = Converter<IDLType>::convert(lexicalGlobalObject, indexValue); + RETURN_IF_EXCEPTION(scope, {}); + + result.uncheckedAppend(convertedValue); + } + return result; + } + + for (unsigned i = 0; i < length; i++) { + auto indexValue = array->getDirectIndex(&lexicalGlobalObject, i); + RETURN_IF_EXCEPTION(scope, {}); + + if (!indexValue) + indexValue = JSC::jsUndefined(); + + auto convertedValue = Converter<IDLType>::convert(lexicalGlobalObject, indexValue); + RETURN_IF_EXCEPTION(scope, {}); + + result.uncheckedAppend(convertedValue); + } + return result; + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value) + { + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (!value.isObject()) { + throwSequenceTypeError(lexicalGlobalObject, scope); + return {}; + } + + JSC::JSObject* object = JSC::asObject(value); + if (Converter<IDLType>::conversionHasSideEffects) + RELEASE_AND_RETURN(scope, (GenericConverter::convert(lexicalGlobalObject, object))); + + if (!JSC::isJSArray(object)) + RELEASE_AND_RETURN(scope, (GenericConverter::convert(lexicalGlobalObject, object))); + + JSC::JSArray* array = JSC::asArray(object); + if (!array->isIteratorProtocolFastAndNonObservable()) + RELEASE_AND_RETURN(scope, (GenericConverter::convert(lexicalGlobalObject, object))); + + RELEASE_AND_RETURN(scope, (convertArray(lexicalGlobalObject, array))); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method) + { + if (Converter<IDLType>::conversionHasSideEffects) + return GenericConverter::convert(lexicalGlobalObject, object, method); + + if (!JSC::isJSArray(object)) + return GenericConverter::convert(lexicalGlobalObject, object, method); + + JSC::JSArray* array = JSC::asArray(object); + if (!array->isIteratorProtocolFastAndNonObservable()) + return GenericConverter::convert(lexicalGlobalObject, object, method); + + return convertArray(lexicalGlobalObject, array); + } +}; + +template<> +struct SequenceConverter<IDLLong> { + using ReturnType = typename GenericSequenceConverter<IDLLong>::ReturnType; + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value) + { + return NumericSequenceConverter<IDLLong>::convert(lexicalGlobalObject, value); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method) + { + return NumericSequenceConverter<IDLLong>::convert(lexicalGlobalObject, object, method); + } +}; + +template<> +struct SequenceConverter<IDLFloat> { + using ReturnType = typename GenericSequenceConverter<IDLFloat>::ReturnType; + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value) + { + return NumericSequenceConverter<IDLFloat>::convert(lexicalGlobalObject, value); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method) + { + return NumericSequenceConverter<IDLFloat>::convert(lexicalGlobalObject, object, method); + } +}; + +template<> +struct SequenceConverter<IDLUnrestrictedFloat> { + using ReturnType = typename GenericSequenceConverter<IDLUnrestrictedFloat>::ReturnType; + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value) + { + return NumericSequenceConverter<IDLUnrestrictedFloat>::convert(lexicalGlobalObject, value); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method) + { + return NumericSequenceConverter<IDLUnrestrictedFloat>::convert(lexicalGlobalObject, object, method); + } +}; + +template<> +struct SequenceConverter<IDLDouble> { + using ReturnType = typename GenericSequenceConverter<IDLDouble>::ReturnType; + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value) + { + return NumericSequenceConverter<IDLDouble>::convert(lexicalGlobalObject, value); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method) + { + return NumericSequenceConverter<IDLDouble>::convert(lexicalGlobalObject, object, method); + } +}; + +template<> +struct SequenceConverter<IDLUnrestrictedDouble> { + using ReturnType = typename GenericSequenceConverter<IDLUnrestrictedDouble>::ReturnType; + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value) + { + return NumericSequenceConverter<IDLUnrestrictedDouble>::convert(lexicalGlobalObject, value); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method) + { + return NumericSequenceConverter<IDLUnrestrictedDouble>::convert(lexicalGlobalObject, object, method); + } +}; + +} + +template<typename T> struct Converter<IDLSequence<T>> : DefaultConverter<IDLSequence<T>> { + using ReturnType = typename Detail::SequenceConverter<T>::ReturnType; + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value) + { + return Detail::SequenceConverter<T>::convert(lexicalGlobalObject, value); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method) + { + return Detail::SequenceConverter<T>::convert(lexicalGlobalObject, object, method); + } +}; + +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::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject, const Vector<U, inlineCapacity>& vector) + { + JSC::VM& vm = JSC::getVM(&lexicalGlobalObject); + auto scope = DECLARE_THROW_SCOPE(vm); + JSC::MarkedArgumentBuffer list; + for (auto& element : vector) { + auto jsValue = toJS<T>(lexicalGlobalObject, globalObject, element); + RETURN_IF_EXCEPTION(scope, {}); + list.append(jsValue); + } + if (UNLIKELY(list.hasOverflowed())) { + throwOutOfMemoryError(&lexicalGlobalObject, scope); + return {}; + } + RELEASE_AND_RETURN(scope, JSC::constructArray(&globalObject, static_cast<JSC::ArrayAllocationProfile*>(nullptr), list)); + } +}; + +template<typename T> struct Converter<IDLFrozenArray<T>> : DefaultConverter<IDLFrozenArray<T>> { + using ReturnType = typename Detail::SequenceConverter<T>::ReturnType; + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value) + { + return Detail::SequenceConverter<T>::convert(lexicalGlobalObject, value); + } + + static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method) + { + return Detail::SequenceConverter<T>::convert(lexicalGlobalObject, object, method); + } +}; + +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::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject, const Vector<U, inlineCapacity>& vector) + { + JSC::VM& vm = JSC::getVM(&lexicalGlobalObject); + auto scope = DECLARE_THROW_SCOPE(vm); + JSC::MarkedArgumentBuffer list; + for (auto& element : vector) { + auto jsValue = toJS<T>(lexicalGlobalObject, globalObject, element); + RETURN_IF_EXCEPTION(scope, {}); + list.append(jsValue); + } + if (UNLIKELY(list.hasOverflowed())) { + throwOutOfMemoryError(&lexicalGlobalObject, scope); + return {}; + } + auto* array = JSC::constructArray(&globalObject, static_cast<JSC::ArrayAllocationProfile*>(nullptr), list); + RETURN_IF_EXCEPTION(scope, {}); + RELEASE_AND_RETURN(scope, JSC::objectConstructorFreeze(&lexicalGlobalObject, array)); + } +}; + +} // namespace WebCore |