| // Copyright 2016 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef COBALT_SCRIPT_MOZJS_CONVERSION_HELPERS_H_ |
| #define COBALT_SCRIPT_MOZJS_CONVERSION_HELPERS_H_ |
| |
| #include <limits> |
| #include <string> |
| |
| #include "base/logging.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/optional.h" |
| #include "base/stringprintf.h" |
| #include "cobalt/base/enable_if.h" |
| #include "cobalt/base/token.h" |
| #include "cobalt/script/mozjs/mozjs_callback_interface_holder.h" |
| #include "cobalt/script/mozjs/mozjs_exception_state.h" |
| #include "cobalt/script/mozjs/mozjs_global_environment.h" |
| #include "cobalt/script/mozjs/mozjs_object_handle.h" |
| #include "cobalt/script/mozjs/mozjs_user_object_holder.h" |
| #include "cobalt/script/mozjs/mozjs_value_handle.h" |
| #include "cobalt/script/mozjs/type_traits.h" |
| #include "cobalt/script/mozjs/union_type_conversion_forward.h" |
| #include "cobalt/script/mozjs/util/algorithm_helpers.h" |
| #include "cobalt/script/sequence.h" |
| #include "nb/memory_scope.h" |
| #include "third_party/mozjs/js/src/jsapi.h" |
| #include "third_party/mozjs/js/src/jsarray.h" |
| #include "third_party/mozjs/js/src/jscntxt.h" |
| #include "third_party/mozjs/js/src/jsproxy.h" |
| #include "third_party/mozjs/js/src/jsstr.h" |
| |
| namespace cobalt { |
| namespace script { |
| namespace mozjs { |
| |
| // Flags that can be used as a bitmask for special conversion behaviour. |
| enum ConversionFlags { |
| kNoConversionFlags = 0, |
| kConversionFlagRestricted = 1 << 0, |
| kConversionFlagNullable = 1 << 1, |
| kConversionFlagTreatNullAsEmptyString = 1 << 2, |
| kConversionFlagTreatUndefinedAsEmptyString = 1 << 3, |
| kConversionFlagClamped = 1 << 4, |
| |
| // Valid conversion flags for numeric values. |
| kConversionFlagsNumeric = kConversionFlagRestricted | kConversionFlagClamped, |
| |
| // Valid conversion flags for string types. |
| kConversionFlagsString = kConversionFlagTreatNullAsEmptyString | |
| kConversionFlagTreatUndefinedAsEmptyString, |
| |
| // Valid conversion flags for objects. |
| kConversionFlagsObject = kConversionFlagNullable, |
| |
| // Valid conversion flags for callback functions. |
| kConversionFlagsCallbackFunction = kConversionFlagNullable, |
| |
| // Valid conversion flags for callback interfaces. |
| kConversionFlagsCallbackInterface = kConversionFlagNullable, |
| }; |
| |
| // std::string -> JSValue |
| inline void ToJSValue(JSContext* context, const std::string& in_string, |
| JS::MutableHandleValue out_value) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| size_t length = in_string.length(); |
| jschar* inflated_buffer = |
| js::InflateUTF8String(context, in_string.c_str(), &length); |
| |
| if (!inflated_buffer) { |
| LOG(ERROR) << "Failed to inflate UTF8 string."; |
| out_value.setNull(); |
| return; |
| } |
| |
| JS::RootedString rooted_string( |
| context, JS_NewUCStringCopyN(context, inflated_buffer, length)); |
| js_free(inflated_buffer); |
| |
| out_value.set(JS::StringValue(rooted_string)); |
| } |
| |
| // JSValue -> std::string |
| void FromJSValue(JSContext* context, JS::HandleValue value, |
| int conversion_flags, ExceptionState* exception_state, |
| std::string* out_string); |
| |
| // base::Token -> JSValue |
| inline void ToJSValue(JSContext* context, const base::Token& token, |
| JS::MutableHandleValue out_value) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| ToJSValue(context, std::string(token.c_str()), out_value); |
| } |
| |
| // bool -> JSValue |
| inline void ToJSValue(JSContext* context, bool in_boolean, |
| JS::MutableHandleValue out_value) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| out_value.set(JS::BooleanValue(in_boolean)); |
| } |
| |
| // JSValue -> bool |
| inline void FromJSValue(JSContext* context, JS::HandleValue value, |
| int conversion_flags, ExceptionState* exception_state, |
| bool* out_boolean) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| DCHECK_EQ(conversion_flags, kNoConversionFlags) |
| << "No conversion flags supported."; |
| DCHECK(out_boolean); |
| // ToBoolean implements the ECMAScript ToBoolean operation. |
| *out_boolean = JS::ToBoolean(value); |
| } |
| |
| // signed integers <= 4 bytes -> JSValue |
| template <typename T> |
| inline void ToJSValue( |
| JSContext* context, T in_number, JS::MutableHandleValue out_value, |
| typename base::enable_if<std::numeric_limits<T>::is_specialized && |
| std::numeric_limits<T>::is_integer && |
| std::numeric_limits<T>::is_signed && |
| (sizeof(T) <= 4), |
| T>::type* = NULL) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| out_value.set(INT_TO_JSVAL(in_number)); |
| } |
| |
| template <typename T> |
| inline const double UpperBound() { |
| return std::numeric_limits<T>::max(); |
| } |
| |
| template <typename T> |
| inline const double LowerBound() { |
| return std::numeric_limits<T>::min(); |
| } |
| |
| // The below specializations of UpperBound<T> and LowerBound<T> for 64 |
| // bit integers use the (2^(53) - 1) and similar bounds specified in |
| // step 1 of ConvertToInt, see: |
| // https://heycam.github.io/webidl/#abstract-opdef-converttoint |
| template <> |
| inline const double UpperBound<int64_t>() { |
| const double kInt64UpperBound = static_cast<double>((1ll << 53) - 1); |
| return kInt64UpperBound; |
| } |
| |
| template <> |
| inline const double LowerBound<int64_t>() { |
| const double kInt64LowerBound = static_cast<double>(-(1ll << 53) + 1); |
| return kInt64LowerBound; |
| } |
| |
| template <> |
| inline const double UpperBound<uint64_t>() { |
| const double kUInt64UpperBound = static_cast<double>((1ll << 53) - 1); |
| return kUInt64UpperBound; |
| } |
| |
| template <typename T> |
| void ClampedValue(JSContext* context, JS::HandleValue value, |
| JS::MutableHandleValue clamped_value) { |
| double value_double; |
| JS::ToNumber(context, value, &value_double); |
| if (value_double > UpperBound<T>()) { |
| clamped_value.set(DOUBLE_TO_JSVAL(UpperBound<T>())); |
| } else if (value_double < LowerBound<T>()) { |
| clamped_value.set(DOUBLE_TO_JSVAL(LowerBound<T>())); |
| } else { |
| clamped_value.set(value); |
| } |
| } |
| |
| // JSValue -> signed integers <= 4 bytes |
| template <typename T> |
| inline void FromJSValue( |
| JSContext* context, JS::HandleValue value, int conversion_flags, |
| ExceptionState* exception_state, T* out_number, |
| typename base::enable_if<std::numeric_limits<T>::is_specialized && |
| std::numeric_limits<T>::is_integer && |
| std::numeric_limits<T>::is_signed && |
| (sizeof(T) <= 4), |
| T>::type* = NULL) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| DCHECK(out_number); |
| |
| int32_t out; |
| // Convert a JavaScript value to an integer type as specified by the |
| // ECMAScript standard. |
| JS::RootedValue value_to_convert(context); |
| if (conversion_flags & kConversionFlagClamped) { |
| ClampedValue<T>(context, value, &value_to_convert); |
| } else { |
| value_to_convert.set(value); |
| } |
| JSBool success = JS_ValueToECMAInt32(context, value_to_convert, &out); |
| DCHECK(success); |
| |
| *out_number = static_cast<T>(out); |
| } |
| |
| // JSValue -> signed integers > 4 bytes |
| template <typename T> |
| inline void FromJSValue( |
| JSContext* context, JS::HandleValue value, int conversion_flags, |
| ExceptionState* exception_state, T* out_number, |
| typename base::enable_if<std::numeric_limits<T>::is_specialized && |
| std::numeric_limits<T>::is_integer && |
| std::numeric_limits<T>::is_signed && |
| (sizeof(T) > 4), |
| T>::type* = NULL) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| double to_number; |
| JS::ToNumber(context, value, &to_number); |
| |
| std::string value_str; |
| FromJSValue(context, value, conversion_flags, exception_state, &value_str); |
| DCHECK(out_number); |
| int64_t out; |
| // This produces and IDL unsigned long long. |
| JS::RootedValue value_to_convert(context); |
| if (conversion_flags & kConversionFlagClamped) { |
| ClampedValue<T>(context, value, &value_to_convert); |
| } else { |
| value_to_convert.set(value); |
| } |
| JSBool success = JS_ValueToInt64(context, value_to_convert, &out); |
| DCHECK(success); |
| if (!success) { |
| exception_state->SetSimpleException(kNotInt64Type); |
| return; |
| } |
| *out_number = static_cast<T>(out); |
| } |
| |
| // signed integers > 4 bytes -> JSValue |
| template <typename T> |
| inline void ToJSValue( |
| JSContext* context, T in_number, JS::MutableHandleValue out_value, |
| typename base::enable_if<std::numeric_limits<T>::is_specialized && |
| std::numeric_limits<T>::is_integer && |
| std::numeric_limits<T>::is_signed && |
| (sizeof(T) > 4), |
| T>::type* = NULL) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| out_value.set(JS_NumberValue(in_number)); |
| } |
| |
| // unsigned integers <= 4 bytes -> JSValue |
| template <typename T> |
| inline void ToJSValue( |
| JSContext* context, T in_number, JS::MutableHandleValue out_value, |
| typename base::enable_if<std::numeric_limits<T>::is_specialized && |
| std::numeric_limits<T>::is_integer && |
| !std::numeric_limits<T>::is_signed && |
| (sizeof(T) <= 4), |
| T>::type* = NULL) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| out_value.set(UINT_TO_JSVAL(in_number)); |
| } |
| |
| // JSValue -> unsigned integers <= 4 bytes |
| template <typename T> |
| inline void FromJSValue( |
| JSContext* context, JS::HandleValue value, int conversion_flags, |
| ExceptionState* exception_state, T* out_number, |
| typename base::enable_if<std::numeric_limits<T>::is_specialized && |
| std::numeric_limits<T>::is_integer && |
| !std::numeric_limits<T>::is_signed && |
| (sizeof(T) <= 4), |
| T>::type* = NULL) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| DCHECK(out_number); |
| |
| uint32_t out; |
| // Convert a JavaScript value to an integer type as specified by the |
| // ECMAScript standard. |
| JS::RootedValue value_to_convert(context); |
| if (conversion_flags & kConversionFlagClamped) { |
| ClampedValue<T>(context, value, &value_to_convert); |
| } else { |
| value_to_convert.set(value); |
| } |
| JSBool success = JS_ValueToECMAUint32(context, value_to_convert, &out); |
| DCHECK(success); |
| |
| *out_number = static_cast<T>(out); |
| } |
| |
| // JSValue -> unsigned integers > 4 bytes |
| template <typename T> |
| inline void FromJSValue( |
| JSContext* context, JS::HandleValue value, int conversion_flags, |
| ExceptionState* exception_state, T* out_number, |
| typename base::enable_if<std::numeric_limits<T>::is_specialized && |
| std::numeric_limits<T>::is_integer && |
| !std::numeric_limits<T>::is_signed && |
| (sizeof(T) > 4), |
| T>::type* = NULL) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| DCHECK(out_number); |
| |
| uint64_t out; |
| // This produces and IDL unsigned long long. |
| JS::RootedValue value_to_convert(context); |
| if (conversion_flags & kConversionFlagClamped) { |
| ClampedValue<T>(context, value, &value_to_convert); |
| } else { |
| value_to_convert.set(value); |
| } |
| JSBool success = JS_ValueToUint64(context, value_to_convert, &out); |
| DCHECK(success); |
| if (!success) { |
| exception_state->SetSimpleException(kNotUint64Type); |
| return; |
| } |
| *out_number = static_cast<T>(out); |
| } |
| |
| // unsigned integers > 4 bytes -> JSValue |
| template <typename T> |
| inline void ToJSValue( |
| JSContext* context, T in_number, JS::MutableHandleValue out_value, |
| typename base::enable_if<std::numeric_limits<T>::is_specialized && |
| std::numeric_limits<T>::is_integer && |
| !std::numeric_limits<T>::is_signed && |
| (sizeof(T) > 4), |
| T>::type* = NULL) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| out_value.set(JS_NumberValue(in_number)); |
| } |
| |
| // double -> JSValue |
| template <typename T> |
| inline void ToJSValue( |
| JSContext* context, T in_number, JS::MutableHandleValue out_value, |
| typename base::enable_if<std::numeric_limits<T>::is_specialized && |
| !std::numeric_limits<T>::is_integer, |
| T>::type* = NULL) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| out_value.set(DOUBLE_TO_JSVAL(in_number)); |
| } |
| |
| // JSValue -> double |
| template <typename T> |
| inline void FromJSValue( |
| JSContext* context, JS::HandleValue value, int conversion_flags, |
| ExceptionState* exception_state, T* out_number, |
| typename base::enable_if<std::numeric_limits<T>::is_specialized && |
| !std::numeric_limits<T>::is_integer, |
| T>::type* = NULL) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| DCHECK_EQ(conversion_flags & ~kConversionFlagsNumeric, 0) |
| << "Unexpected conversion flags found."; |
| DCHECK(out_number); |
| double double_value; |
| if (!JS::ToNumber(context, value, &double_value)) { |
| exception_state->SetSimpleException(kNotNumberType); |
| return; |
| } |
| |
| if (!mozilla::IsFinite(double_value) && |
| (conversion_flags & kConversionFlagRestricted)) { |
| exception_state->SetSimpleException(kNotFinite); |
| return; |
| } |
| |
| *out_number = double_value; |
| } |
| |
| // optional<T> -> JSValue |
| template <typename T> |
| inline void ToJSValue(JSContext* context, const base::optional<T>& in_optional, |
| JS::MutableHandleValue out_value) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| if (!in_optional) { |
| out_value.setNull(); |
| return; |
| } |
| ToJSValue(context, in_optional.value(), out_value); |
| } |
| |
| // JSValue -> optional<T> |
| template <typename T> |
| inline void FromJSValue(JSContext* context, JS::HandleValue value, |
| int conversion_flags, ExceptionState* exception_state, |
| base::optional<T>* out_optional) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| if (value.isNull()) { |
| *out_optional = base::nullopt; |
| } else if (value.isUndefined()) { |
| *out_optional = base::nullopt; |
| } else { |
| *out_optional = T(); |
| FromJSValue(context, value, conversion_flags & ~kConversionFlagNullable, |
| exception_state, &(out_optional->value())); |
| } |
| } |
| |
| // JSValue -> optional<std::string> |
| template <> |
| inline void FromJSValue(JSContext* context, JS::HandleValue value, |
| int conversion_flags, ExceptionState* exception_state, |
| base::optional<std::string>* out_optional) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| if (value.isNull()) { |
| *out_optional = base::nullopt; |
| } else if (value.isUndefined() && |
| !(conversion_flags & kConversionFlagTreatUndefinedAsEmptyString)) { |
| // If TreatUndefinedAs=EmptyString is set, skip the default conversion |
| // of undefined to null. |
| *out_optional = base::nullopt; |
| } else { |
| *out_optional = std::string(); |
| FromJSValue(context, value, conversion_flags & ~kConversionFlagNullable, |
| exception_state, &(out_optional->value())); |
| } |
| } |
| |
| // OpaqueHandle -> JSValue |
| void ToJSValue(JSContext* context, |
| const OpaqueHandleHolder* opaque_handle_holder, |
| JS::MutableHandleValue out_value); |
| |
| // JSValue -> OpaqueHandle |
| void FromJSValue(JSContext* context, JS::HandleValue value, |
| int conversion_flags, ExceptionState* exception_state, |
| MozjsObjectHandleHolder* out_holder); |
| |
| // ValueHandle -> JSValue |
| void ToJSValue(JSContext* context, const ValueHandleHolder* value_handle_holder, |
| JS::MutableHandleValue out_value); |
| |
| // JSValue -> ValueHandle |
| void FromJSValue(JSContext* context, JS::HandleValue value, |
| int conversion_flags, ExceptionState* exception_state, |
| MozjsValueHandleHolder* out_holder); |
| |
| // object -> JSValue |
| template <typename T> |
| inline void ToJSValue(JSContext* context, const scoped_refptr<T>& in_object, |
| JS::MutableHandleValue out_value) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| if (!in_object) { |
| out_value.setNull(); |
| return; |
| } |
| |
| MozjsGlobalEnvironment* global_environment = |
| static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); |
| JS::RootedObject object( |
| context, |
| global_environment->wrapper_factory()->GetWrapperProxy(in_object)); |
| DCHECK(object); |
| JS::RootedObject proxy_target(context, js::GetProxyTargetObject(object)); |
| if (JS_IsGlobalObject(proxy_target)) { |
| object = proxy_target; |
| } |
| |
| out_value.set(OBJECT_TO_JSVAL(object)); |
| } |
| |
| // raw object pointer -> JSValue |
| template <typename T> |
| inline void ToJSValue(JSContext* context, T* in_object, |
| JS::MutableHandleValue out_value) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| ToJSValue(context, scoped_refptr<T>(in_object), out_value); |
| } |
| |
| // JSValue -> object |
| template <typename T> |
| inline void FromJSValue(JSContext* context, JS::HandleValue value, |
| int conversion_flags, ExceptionState* exception_state, |
| scoped_refptr<T>* out_object) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| DCHECK_EQ(conversion_flags & ~kConversionFlagsObject, 0) |
| << "Unexpected conversion flags found."; |
| JS::RootedObject js_object(context); |
| if (value.isNull() || value.isUndefined()) { |
| if (!(conversion_flags & kConversionFlagNullable)) { |
| exception_state->SetSimpleException(kNotNullableType); |
| } |
| return; |
| } |
| if (!JS_ValueToObject(context, value, js_object.address())) { |
| exception_state->SetSimpleException(kNotObjectType); |
| return; |
| } |
| DCHECK(js_object); |
| JS::RootedObject wrapper(context, js::IsProxy(js_object) |
| ? js::GetProxyTargetObject(js_object) |
| : js_object); |
| MozjsGlobalEnvironment* global_environment = |
| static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); |
| const WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); |
| if (wrapper_factory->IsWrapper(wrapper)) { |
| bool object_implements_interface = |
| wrapper_factory->DoesObjectImplementInterface(js_object, |
| base::GetTypeId<T>()); |
| if (!object_implements_interface) { |
| exception_state->SetSimpleException(kDoesNotImplementInterface); |
| return; |
| } |
| WrapperPrivate* wrapper_private = |
| WrapperPrivate::GetFromWrapperObject(wrapper); |
| *out_object = wrapper_private->wrappable<T>(); |
| return; |
| } |
| // This is not a platform object. Return a type error. |
| exception_state->SetSimpleException(kDoesNotImplementInterface); |
| } |
| |
| // CallbackInterface -> JSValue |
| template <typename T> |
| inline void ToJSValue(JSContext* context, |
| const ScriptValue<T>* callback_interface, |
| JS::MutableHandleValue out_value) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| if (!callback_interface) { |
| out_value.set(JS::NullValue()); |
| return; |
| } |
| typedef typename CallbackInterfaceTraits<T>::MozjsCallbackInterfaceClass |
| MozjsCallbackInterfaceClass; |
| // Downcast to MozjsUserObjectHolder<T> so we can get the underlying JSObject. |
| typedef MozjsUserObjectHolder<MozjsCallbackInterfaceClass> |
| MozjsUserObjectHolderClass; |
| const MozjsUserObjectHolderClass* user_object_holder = |
| base::polymorphic_downcast<const MozjsUserObjectHolderClass*>( |
| callback_interface); |
| |
| // Shouldn't be NULL. If the callback was NULL then NULL should have been |
| // passed as an argument into this function. |
| // Downcast to the corresponding MozjsCallbackInterface type, from which we |
| // can get the implementing object. |
| const MozjsCallbackInterfaceClass* mozjs_callback_interface = |
| base::polymorphic_downcast<const MozjsCallbackInterfaceClass*>( |
| user_object_holder->GetScriptValue()); |
| DCHECK(mozjs_callback_interface); |
| out_value.set(mozjs_callback_interface->value()); |
| } |
| |
| // JSValue -> CallbackInterface |
| template <typename T> |
| inline void FromJSValue( |
| JSContext* context, JS::HandleValue value, int conversion_flags, |
| ExceptionState* out_exception, |
| MozjsCallbackInterfaceHolder<T>* out_callback_interface) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| typedef T MozjsCallbackInterfaceClass; |
| DCHECK_EQ(conversion_flags & ~kConversionFlagsCallbackFunction, 0) |
| << "No conversion flags supported."; |
| if (value.isNull()) { |
| if (!(conversion_flags & kConversionFlagNullable)) { |
| out_exception->SetSimpleException(kNotNullableType); |
| } |
| // If it is a nullable type, just return. |
| return; |
| } |
| |
| // https://www.w3.org/TR/WebIDL/#es-user-objects |
| // Any user object can be considered to implement a user interface. Actually |
| // checking if the correct properties exist will happen when the operation |
| // on the callback interface is run. |
| if (!value.isObject()) { |
| out_exception->SetSimpleException(kNotObjectType); |
| return; |
| } |
| |
| MozjsGlobalEnvironment* global_environment = |
| static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); |
| |
| JS::RootedObject implementing_object(context, JSVAL_TO_OBJECT(value)); |
| DCHECK(implementing_object); |
| *out_callback_interface = MozjsCallbackInterfaceHolder<T>( |
| implementing_object, context, global_environment->wrapper_factory()); |
| } |
| |
| template <typename T> |
| void ToJSValue(JSContext* context, const script::Sequence<T>& sequence, |
| JS::MutableHandleValue out_value) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| // 1. Let n be the length of S. |
| typedef typename script::Sequence<T>::size_type size_type; |
| size_type count = sequence.size(); |
| |
| // 2. Let A be a new Array object created as if by the expression []. |
| JS::RootedObject array(context, NewDenseEmptyArray(context)); |
| |
| // 3. Initialize i to be 0. |
| // 4. While i < n: |
| for (size_type index = 0; index < count; ++index) { |
| // 4.1. Let V be the value in S at index i. |
| // 4.2. Let E be the result of converting V to an ECMAScript value. |
| JS::RootedValue element(context); |
| ToJSValue(context, sequence.at(index), &element); |
| |
| std::string property = |
| base::StringPrintf("%" PRIu64, static_cast<uint64_t>(index)); |
| |
| // 4.3. Let P be the result of calling ToString(i). |
| // 4.4. Call CreateDataProperty(A, P, E). |
| JS_SetProperty(context, array, property.c_str(), element.address()); |
| |
| // 4.5. Set i to i + 1. |
| } |
| |
| // 5. Return A. |
| out_value.set(OBJECT_TO_JSVAL(array)); |
| } |
| |
| template <typename T> |
| void FromJSValue(JSContext* context, JS::HandleValue value, |
| int conversion_flags, ExceptionState* exception_state, |
| script::Sequence<T>* out_sequence) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| DCHECK_EQ(0, conversion_flags); |
| DCHECK(out_sequence); |
| |
| // JS -> IDL type conversion procedure described here: |
| // https://heycam.github.io/webidl/#es-sequence |
| |
| // 1. If Type(V) is not Object, throw a TypeError. |
| JS::RootedObject iterable(context); |
| if (!value.isObject() || |
| !JS_ValueToObject(context, value, iterable.address())) { |
| exception_state->SetSimpleException(kNotObjectType); |
| return; |
| } |
| |
| // 2. Let method be the result of GetMethod(V, @@iterator). |
| // 3. ReturnIfAbrupt(method). |
| // 4. If method is undefined, throw a TypeError. |
| // 5. Return the result of creating a sequence from V and method. |
| // https://heycam.github.io/webidl/#create-sequence-from-iterable |
| // 5.1. Let iter be GetIterator(iterable, method). |
| JS::RootedObject iter(context); |
| if (!util::GetIterator(context, iterable, &iter)) { |
| exception_state->SetSimpleException(kNotIterableType); |
| return; |
| } |
| |
| // 5.2. ReturnIfAbrupt(iter). |
| // 5.3. Initialize i to be 0. |
| // 5.4. Repeat |
| while (true) { |
| // 5.4.1. Let next be IteratorStep(iter). |
| // 5.4.2. ReturnIfAbrupt(next). |
| // 5.4.3. If next is false, then return an IDL sequence value of type |
| // sequence<T> of length i, where the value of the element at index j |
| // is Sj. |
| JS::RootedObject next(context); |
| if (!util::IteratorStep(context, iter, &next)) { |
| break; |
| } |
| |
| // 5.4.4. Let nextItem be IteratorValue(next). |
| // 5.4.5. ReturnIfAbrupt(nextItem). |
| JS::RootedValue next_item(context); |
| if (!util::IteratorValue(context, next, &next_item)) { |
| exception_state->SetSimpleException(kDoesNotImplementInterface); |
| util::IteratorClose(context, iter); |
| return; |
| } |
| |
| // 5.4.6. Initialize Si to the result of converting nextItem to an IDL value |
| // of type T. |
| T idl_next_item; |
| FromJSValue(context, next_item, conversion_flags, exception_state, |
| &idl_next_item); |
| if (context->isExceptionPending()) { |
| // Exception converting element into to sequence element type. |
| util::IteratorClose(context, iter); |
| return; |
| } |
| out_sequence->push_back(idl_next_item); |
| |
| // 5.4.7. Set i to i + 1. |
| } |
| |
| util::IteratorClose(context, iter); |
| return; |
| } |
| |
| } // namespace mozjs |
| } // namespace script |
| } // namespace cobalt |
| |
| // Union type conversion is generated by a pump script. |
| #include "cobalt/script/mozjs/union_type_conversion_impl.h" |
| |
| #endif // COBALT_SCRIPT_MOZJS_CONVERSION_HELPERS_H_ |