blob: bb8660b4c3b3d3915fe02240942415324494eec5 [file] [log] [blame]
/*
* 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::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, JSDOMGlobalObject& globalObject)
{
return convertRecord<JSDOMGlobalObject&>(state, value, globalObject);
}
static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
{
return convertRecord(state, value);
}
private:
template<class...Args>
static ReturnType convertRecord(JSC::ExecState& state, JSC::JSValue value, Args ... args)
{
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, JSC::PrivateSymbolMode::Exclude);
object->methodTable(vm)->getOwnPropertyNames(object, &state, keys, JSC::EnumerationMode(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::PropertyDescriptor descriptor;
bool didGetDescriptor = object->getOwnPropertyDescriptor(&state, key, descriptor);
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 the default EnumerationMode,
// to prevent an observable extra [[GetOwnProperty]] operation in the case of ProxyObject records.
if (didGetDescriptor && descriptor.enumerable()) {
// 1. Let typedKey be key converted to an IDL value of type K.
auto typedKey = Detail::IdentifierConverter<K>::convert(state, key);
RETURN_IF_EXCEPTION(scope, { });
// 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, args...);
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.
// FIXME: Handle this case.
// 5. Otherwise, append to result a mapping (typedKey, typedValue).
result.append({ 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 MapType>
static JSC::JSValue convert(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const MapType& map)
{
auto& vm = state.vm();
// 1. Let result be ! ObjectCreate(%ObjectPrototype%).
auto result = constructEmptyObject(&state, 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>(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;
}
};
} // namespace WebCore