aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/bindings/webcore/JSDOMConvertRecord.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js/bindings/webcore/JSDOMConvertRecord.h')
-rw-r--r--src/bun.js/bindings/webcore/JSDOMConvertRecord.h191
1 files changed, 191 insertions, 0 deletions
diff --git a/src/bun.js/bindings/webcore/JSDOMConvertRecord.h b/src/bun.js/bindings/webcore/JSDOMConvertRecord.h
new file mode 100644
index 000000000..0f4c84eff
--- /dev/null
+++ b/src/bun.js/bindings/webcore/JSDOMConvertRecord.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2016-2019 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 "JSDOMConvertStrings.h"
+#include "JSDOMGlobalObject.h"
+#include "JavaScriptCore/ObjectConstructor.h"
+
+namespace WebCore {
+
+namespace Detail {
+
+template<typename IDLStringType>
+struct IdentifierConverter;
+
+template<> struct IdentifierConverter<IDLDOMString> {
+ static String convert(JSC::JSGlobalObject& lexicalGlobalObject, const JSC::Identifier& identifier)
+ {
+ return identifierToString(lexicalGlobalObject, identifier);
+ }
+};
+
+template<> struct IdentifierConverter<IDLByteString> {
+ static String convert(JSC::JSGlobalObject& lexicalGlobalObject, const JSC::Identifier& identifier)
+ {
+ return identifierToByteString(lexicalGlobalObject, identifier);
+ }
+};
+
+template<> struct IdentifierConverter<IDLUSVString> {
+ static String convert(JSC::JSGlobalObject& lexicalGlobalObject, const JSC::Identifier& identifier)
+ {
+ return identifierToUSVString(lexicalGlobalObject, 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::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value, JSDOMGlobalObject& globalObject)
+ {
+ return convertRecord<JSDOMGlobalObject&>(lexicalGlobalObject, value, globalObject);
+ }
+
+ static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value)
+ {
+ return convertRecord(lexicalGlobalObject, value);
+ }
+
+private:
+ template<class... Args>
+ static ReturnType convertRecord(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value, Args... args)
+ {
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ 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(&lexicalGlobalObject, scope);
+ return {};
+ }
+
+ JSC::JSObject* object = JSC::asObject(value);
+
+ ReturnType result;
+ HashMap<KeyType, size_t> resultMap;
+
+ // 4. Let keys be ? O.[[OwnPropertyKeys]]().
+ JSC::PropertyNameArray keys(vm, JSC::PropertyNameMode::StringsAndSymbols, JSC::PrivateSymbolMode::Exclude);
+ object->methodTable()->getOwnPropertyNames(object, &lexicalGlobalObject, keys, JSC::DontEnumPropertiesMode::Include);
+ 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::PropertySlot slot(object, JSC::PropertySlot::InternalMethodType::GetOwnProperty);
+ bool hasProperty = object->methodTable()->getOwnPropertySlot(object, &lexicalGlobalObject, key, slot);
+ RETURN_IF_EXCEPTION(scope, {});
+
+ // 2. If desc is not undefined and desc.[[Enumerable]] is true:
+
+ // It's necessary to filter enumerable here rather than using DontEnumPropertiesMode::Exclude,
+ // to prevent an observable extra [[GetOwnProperty]] operation in the case of ProxyObject records.
+ if (hasProperty && !(slot.attributes() & JSC::PropertyAttribute::DontEnum)) {
+ // 1. Let typedKey be key converted to an IDL value of type K.
+ auto typedKey = Detail::IdentifierConverter<K>::convert(lexicalGlobalObject, key);
+ RETURN_IF_EXCEPTION(scope, {});
+
+ // 2. Let value be ? Get(O, key).
+ JSC::JSValue subValue;
+ if (LIKELY(!slot.isTaintedByOpaqueObject()))
+ subValue = slot.getValue(&lexicalGlobalObject, key);
+ else
+ subValue = object->get(&lexicalGlobalObject, key);
+ RETURN_IF_EXCEPTION(scope, {});
+
+ // 3. Let typedValue be value converted to an IDL value of type V.
+ auto typedValue = Converter<V>::convert(lexicalGlobalObject, subValue, args...);
+ RETURN_IF_EXCEPTION(scope, {});
+
+ // 4. Set result[typedKey] to typedValue.
+ // Note: It's possible that typedKey is already in result if K is USVString and key contains unpaired surrogates.
+ if constexpr (std::is_same_v<K, IDLUSVString>) {
+ if (!typedKey.is8Bit()) {
+ auto addResult = resultMap.add(typedKey, result.size());
+ if (!addResult.isNewEntry) {
+ ASSERT(result[addResult.iterator->value].key == typedKey);
+ result[addResult.iterator->value].value = WTFMove(typedValue);
+ continue;
+ }
+ }
+ } else
+ UNUSED_VARIABLE(resultMap);
+
+ // 5. Otherwise, append to result a mapping (typedKey, typedValue).
+ result.append({ WTFMove(typedKey), WTFMove(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 MapType>
+ static JSC::JSValue convert(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject, const MapType& map)
+ {
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+
+ // 1. Let result be ! ObjectCreate(%ObjectPrototype%).
+ auto result = constructEmptyObject(&lexicalGlobalObject, globalObject.objectPrototype());
+
+ // 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>(lexicalGlobalObject, 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;
+ }
+};
+
+} // namespace WebCore