Shaka Player Embedded
convert_js.h
Go to the documentation of this file.
1 // Copyright 2016 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef SHAKA_EMBEDDED_MAPPING_CONVERT_JS_H_
16 #define SHAKA_EMBEDDED_MAPPING_CONVERT_JS_H_
17 
18 #include <cmath>
19 #include <limits>
20 #include <string>
21 #include <type_traits>
22 #include <unordered_map>
23 #include <utility>
24 #include <vector>
25 
26 #include "shaka/optional.h"
27 #include "shaka/variant.h"
31 #include "src/util/templates.h"
32 
33 namespace shaka {
34 
35 namespace impl {
36 
37 // Types used to identify a category of types. These allow a specialization of
38 // ConvertHelper to be used on a set of types (for example, numbers).
42 
43 template <typename T, typename = void>
45  using type = void;
46 };
47 #define ADD_SPECIAL_TYPE(id, ...) \
48  template <typename T> \
49  struct _SelectSpecialTypes<T, util::enable_if_t<__VA_ARGS__>> { \
50  using type = id; \
51  }
52 
55  std::is_base_of<GenericConverter, T>::value);
57 #undef ADD_SPECIAL_TYPE
58 
83 
84 template <typename Number>
86  static bool FromJsValue(Handle<JsValue> source, Number* dest) {
87  // Number types.
88  const proto::ValueType type = GetValueType(source);
89  if (type != proto::ValueType::Number &&
91  return false;
92  }
93  const double value = NumberFromValue(source);
94 
95  if (std::numeric_limits<Number>::has_infinity &&
96  std::abs(value) == std::numeric_limits<Number>::infinity()) {
97  // OK.
98  } else if (value < std::numeric_limits<Number>::lowest() ||
99  value > std::numeric_limits<Number>::max()) {
100  return false;
101  }
102 
103  // JavaScript numbers will be intentionally truncated if stored as native
104  // integers.
105  *dest = static_cast<Number>(value);
106  return true;
107  }
108 
109  static ReturnVal<JsValue> ToJsValue(Number source) {
110 #if defined(USING_V8)
111  return v8::Number::New(GetIsolate(), source);
112 #elif defined(USING_JSC)
113  return JSValueMakeNumber(GetContext(), source);
114 #endif
115  }
116 };
117 
118 template <typename T>
120  static bool FromJsValue(Handle<JsValue> source, T* dest) {
121  return dest->TryConvert(source);
122  }
123 
124  static ReturnVal<JsValue> ToJsValue(const T& source) {
125  return source.ToJsValue();
126  }
127 };
128 
129 template <typename T>
130 struct ConvertHelper<shaka::optional<T>, void> {
131  static bool FromJsValue(Handle<JsValue> source, shaka::optional<T>* dest) {
132  if (IsNullOrUndefined(source)) {
133  dest->reset();
134  return true;
135  }
136 
137  T temp;
138  if (!ConvertHelper<T>::FromJsValue(source, &temp))
139  return false;
140  *dest = std::move(temp);
141  return true;
142  }
143 
144  static ReturnVal<JsValue> ToJsValue(const shaka::optional<T>& source) {
145  if (source.has_value()) {
146  return ConvertHelper<T>::ToJsValue(source.value());
147  } else {
148  return JsNull();
149  }
150  }
151 };
152 
153 template <typename... Types>
154 struct ConvertHelper<shaka::variant<Types...>> {
155  private:
156  template <size_t I, typename = void>
157  struct Helper {
158  using T = variant_alternative_t<I, shaka::variant<Types...>>;
159 
160  static bool FromJsValue(Handle<JsValue> source,
162  T temp;
163  if (ConvertHelper<T>::FromJsValue(source, &temp)) {
164  dest->template emplace<I>(std::move(temp));
165  return true;
166  }
167  return Helper<I + 1>::FromJsValue(source, dest);
168  }
169 
170  static ReturnVal<JsValue> ToJsValue(
171  const shaka::variant<Types...>& source) {
172  if (source.index() == I)
173  return ConvertHelper<T>::ToJsValue(get<I>(source));
174  else
175  return Helper<I + 1>::ToJsValue(source);
176  }
177  };
178  template <typename Dummy>
179  struct Helper<sizeof...(Types) - 1, Dummy> {
180  static constexpr const size_t I = sizeof...(Types) - 1;
181  using T = variant_alternative_t<I, shaka::variant<Types...>>;
182 
183  static bool FromJsValue(Handle<JsValue> source,
185  T temp;
186  if (ConvertHelper<T>::FromJsValue(source, &temp)) {
187  dest->template emplace<I>(std::move(temp));
188  return true;
189  }
190  return false;
191  }
192 
193  static ReturnVal<JsValue> ToJsValue(
194  const shaka::variant<Types...>& source) {
195  CHECK_EQ(source.index(), I);
196  return ConvertHelper<T>::ToJsValue(get<I>(source));
197  }
198  };
199 
200  public:
201  static bool FromJsValue(Handle<JsValue> source,
203  return Helper<0>::FromJsValue(source, dest);
204  }
205 
206  static ReturnVal<JsValue> ToJsValue(const shaka::variant<Types...>& source) {
207  return Helper<0>::ToJsValue(source);
208  }
209 };
210 
211 template <typename T>
212 struct ConvertHelper<std::vector<T>, void> {
213  static bool FromJsValue(Handle<JsValue> source, std::vector<T>* dest) {
214  if (GetValueType(source) != proto::ValueType::Array)
215  return false;
216 
217  LocalVar<JsObject> array(UnsafeJsCast<JsObject>(source));
218  const size_t length = ArrayLength(array);
219  // Store the data in a temp array since this should not modify |*dest| on
220  // failure.
221  std::vector<T> temp(length);
222  DCHECK_EQ(length, temp.size());
223  for (size_t i = 0; i < length; i++) {
224  LocalVar<JsValue> item(GetArrayIndexRaw(array, i));
225  if (!ConvertHelper<T>::FromJsValue(item, &temp[i]))
226  return false;
227  }
228 
229  swap(temp, *dest);
230  return true;
231  }
232 
233  static ReturnVal<JsValue> ToJsValue(const std::vector<T>& source) {
234  LocalVar<JsObject> ret(CreateArray(source.size()));
235  for (size_t i = 0; i < source.size(); i++) {
236  LocalVar<JsValue> item(ConvertHelper<T>::ToJsValue(source[i]));
237  SetArrayIndexRaw(ret, i, item);
238  }
239  return RawToJsValue(ret);
240  }
241 };
242 
243 template <typename Key, typename Value>
244 struct ConvertHelper<std::unordered_map<Key, Value>, void> {
245  static bool FromJsValue(Handle<JsValue> source,
246  std::unordered_map<Key, Value>* dest) {
247  static_assert(
248  std::is_same<typename std::decay<Key>::type, std::string>::value,
249  "Can only use std::string as a key for maps");
250  if (!IsObject(source))
251  return false;
252 
253  LocalVar<JsObject> map(UnsafeJsCast<JsObject>(source));
254  if (IsBuiltInObject(map))
255  return false;
256 
257  // Store the data in a temp array since this should not modify |*dest| on
258  // failure.
259  std::unordered_map<Key, Value> temp;
260  for (const auto& name : GetMemberNames(map)) {
261  LocalVar<JsValue> item(GetMemberRaw(map, name));
262 
263  Value temp_field;
264  if (!ConvertHelper<Value>::FromJsValue(item, &temp_field))
265  return false;
266  temp.emplace(name, std::move(temp_field));
267  }
268 
269  swap(temp, *dest);
270  return true;
271  }
272 
273  static ReturnVal<JsValue> ToJsValue(
274  const std::unordered_map<Key, Value>& source) {
275  LocalVar<JsObject> ret(CreateObject());
276  for (auto& pair : source) {
277  LocalVar<JsValue> value(ConvertHelper<Value>::ToJsValue(pair.second));
278  SetMemberRaw(ret, pair.first, value);
279  }
280  return RawToJsValue(ret);
281  }
282 };
283 
284 template <typename T>
285 struct ConvertHelper<T*, void> {
286  static_assert(std::is_base_of<BackingObject, T>::value,
287  "Don't accept raw pointers as arguments, use RefPtr<T>.");
288 
289  static ReturnVal<JsValue> ToJsValue(T* source) {
290  // We cannot implicitly convert a T* to a RefPtr<T> because the compiler
291  // cannot deduce the type parameter. This allows us to pass raw pointers
292  // (e.g. |this|).
293  if (!source)
294  return JsNull();
295  else
296  return source->JsThis();
297  }
298 };
299 
300 template <typename T>
301 struct ConvertHelper<ReturnVal<T>, void> {
302  static ReturnVal<JsValue> ToJsValue(const ReturnVal<T>& source) {
303  return RawToJsValue(source);
304  }
305 };
306 
307 template <>
308 struct ConvertHelper<Handle<JsObject>, void> {
309  static bool FromJsValue(Handle<JsValue> source, Handle<JsObject>* dest) {
310  if (!IsObject(source))
311  return false;
312  *dest = UnsafeJsCast<JsObject>(source);
313  return true;
314  }
315 };
316 
317 template <>
318 struct ConvertHelper<std::string, void> {
319  static bool FromJsValue(Handle<JsValue> source, std::string* dest) {
320  const proto::ValueType type = GetValueType(source);
321  if (type != proto::ValueType::String &&
323  return false;
324  }
325 
326  *dest = ConvertToString(source);
327  return true;
328  }
329 
330  static ReturnVal<JsValue> ToJsValue(const std::string& source) {
331  LocalVar<JsString> str(JsStringFromUtf8(source));
332  return RawToJsValue(str);
333  }
334 };
335 
336 // Note this is only hit for the |bool| type, NOT for things that can be
337 // implicitly cast to bool, like pointers.
338 template <>
339 struct ConvertHelper<bool, void> {
340  static bool FromJsValue(Handle<JsValue> source, bool* dest) {
341  const proto::ValueType type = GetValueType(source);
342  if (type != proto::ValueType::Boolean &&
344  return false;
345  }
346 
347  *dest = BooleanFromValue(source);
348  return true;
349  }
350 
351  static ReturnVal<JsValue> ToJsValue(bool source) {
352 #if defined(USING_V8)
353  return v8::Boolean::New(GetIsolate(), source);
354 #elif defined(USING_JSC)
355  return JSValueMakeBoolean(GetContext(), source);
356 #endif
357  }
358 };
359 
360 } // namespace impl
361 
369 template <typename T>
370 bool FromJsValue(Handle<JsValue> source, T* dest) {
371  using BaseT = typename std::decay<T>::type;
372  return impl::ConvertHelper<BaseT>::FromJsValue(source, dest);
373 }
374 
380 template <typename T>
381 ReturnVal<JsValue> ToJsValue(T&& source) {
382  using BaseT = typename std::decay<T>::type;
383  return impl::ConvertHelper<BaseT>::ToJsValue(std::forward<T>(source));
384 }
385 
386 } // namespace shaka
387 
388 #endif // SHAKA_EMBEDDED_MAPPING_CONVERT_JS_H_
void SetArrayIndexRaw(Handle< JsObject > object, size_t i, Handle< JsValue > value)
Definition: js_wrappers.cc:152
const char * dest
Definition: media_utils.cc:31
static ReturnVal< JsValue > ToJsValue(Number source)
Definition: convert_js.h:109
ReturnVal< JsObject > CreateObject()
Definition: js_wrappers.cc:296
bool IsObject(Handle< JsValue > value)
Definition: js_wrappers.cc:315
bool FromJsValue(Handle< JsValue > source, T *dest)
Definition: convert_js.h:370
bool IsNullOrUndefined(Handle< JsValue > value)
Definition: js_wrappers.cc:311
std::vector< std::string > GetMemberNames(Handle< JsObject > object)
Definition: js_wrappers.cc:118
ReturnVal< JsValue > JsNull()
Definition: js_wrappers.cc:288
ReturnVal< JsValue > GetArrayIndexRaw(Handle< JsObject > object, size_t index, LocalVar< JsValue > *exception=nullptr)
Definition: js_wrappers.cc:142
const char * name
static ReturnVal< JsValue > ToJsValue(const ReturnVal< T > &source)
Definition: convert_js.h:302
static ReturnVal< JsValue > ToJsValue(bool source)
Definition: convert_js.h:351
static bool FromJsValue(Handle< JsValue > source, std::vector< T > *dest)
Definition: convert_js.h:213
bool BooleanFromValue(Handle< JsValue > value)
Definition: js_wrappers.cc:394
static ReturnVal< JsValue > ToJsValue(const std::vector< T > &source)
Definition: convert_js.h:233
static ReturnVal< JsValue > ToJsValue(const shaka::optional< T > &source)
Definition: convert_js.h:144
ReturnVal< JsObject > CreateArray(size_t length)
Definition: js_wrappers.cc:292
ReturnVal< JsValue > ToJsValue(T &&source)
Definition: convert_js.h:381
const char * source
Definition: media_utils.cc:30
static bool FromJsValue(Handle< JsValue > source, Handle< JsObject > *dest)
Definition: convert_js.h:309
const T & value() const &
Definition: optional.h:147
ReturnVal< JsValue > RawToJsValue(Handle< T > source)
Definition: js_wrappers.h:405
static bool FromJsValue(Handle< JsValue > source, std::string *dest)
Definition: convert_js.h:319
static ReturnVal< JsValue > ToJsValue(const shaka::variant< Types... > &source)
Definition: convert_js.h:206
static bool FromJsValue(Handle< JsValue > source, Number *dest)
Definition: convert_js.h:86
ExceptionCode type
#define ADD_SPECIAL_TYPE(id,...)
Definition: convert_js.h:47
proto::ValueType GetValueType(Handle< JsValue > value)
Definition: js_wrappers.cc:329
double NumberFromValue(Handle< JsValue > value)
Definition: js_wrappers.cc:385
void reset()
Definition: optional.h:174
static ReturnVal< JsValue > ToJsValue(T *source)
Definition: convert_js.h:289
static bool FromJsValue(Handle< JsValue > source, bool *dest)
Definition: convert_js.h:340
void swap(shared_lock< Mutex > &a, shared_lock< Mutex > &b)
Definition: shared_lock.h:161
size_t index() const
Definition: variant.h:340
static ReturnVal< JsValue > ToJsValue(const T &source)
Definition: convert_js.h:124
static ReturnVal< JsValue > ToJsValue(const std::unordered_map< Key, Value > &source)
Definition: convert_js.h:273
static bool FromJsValue(Handle< JsValue > source, shaka::variant< Types... > *dest)
Definition: convert_js.h:201
void SetMemberRaw(Handle< JsObject > object, const std::string &name, Handle< JsValue > value)
Definition: js_wrappers.cc:147
std::string ConvertToString(Handle< JsValue > value)
Definition: js_wrappers.cc:203
static ReturnVal< JsValue > ToJsValue(const std::string &source)
Definition: convert_js.h:330
JSContextRef GetContext()
Definition: jsc_utils.cc:24
static bool FromJsValue(Handle< JsValue > source, T *dest)
Definition: convert_js.h:120
static bool FromJsValue(Handle< JsValue > source, shaka::optional< T > *dest)
Definition: convert_js.h:131
size_t ArrayLength(Handle< JsObject > value)
Definition: js_wrappers.h:418
ReturnVal< JsString > JsStringFromUtf8(const std::string &str)
Definition: js_wrappers.cc:266
v8::Isolate * GetIsolate()
Definition: v8_utils.cc:27
ReturnVal< JsValue > GetMemberRaw(Handle< JsObject > object, const std::string &name, LocalVar< JsValue > *exception=nullptr)
Definition: js_wrappers.cc:136
bool has_value() const
Definition: optional.h:143
bool IsBuiltInObject(Handle< JsObject > object)
Definition: js_wrappers.cc:319
static bool FromJsValue(Handle< JsValue > source, std::unordered_map< Key, Value > *dest)
Definition: convert_js.h:245
typename variant_alternative< I, Variant >::type variant_alternative_t
Definition: variant.h:288