Shaka Player Embedded
backing_object_factory.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_BACKING_OBJECT_FACTORY_H_
16 #define SHAKA_EMBEDDED_MAPPING_BACKING_OBJECT_FACTORY_H_
17 
18 #include <glog/logging.h>
19 
20 #include <functional>
21 #include <memory>
22 #include <string>
23 #include <utility>
24 
25 #include "src/core/ref_ptr.h"
30 #include "src/mapping/struct.h"
32 #include "src/util/templates.h"
33 
34 namespace shaka {
35 class BackingObject;
36 
37 #ifdef USING_V8
38 using NativeCtor = void (*)(const v8::FunctionCallbackInfo<v8::Value>&);
39 #elif defined(USING_JSC)
40 using NativeCtor = JSObjectRef (*)(JSContextRef, JSObjectRef, size_t,
41  const JSValueRef*, JSValueRef*);
42 #endif
43 
44 namespace impl {
45 
52  public:
53  virtual ~IndexerHandler() {}
54  virtual ReturnVal<JsValue> GetIndex(Handle<JsObject> that, size_t index) = 0;
55  virtual void SetIndex(Handle<JsObject> that, size_t index,
56  Handle<JsValue> value) = 0;
57 };
58 
59 template <typename This, typename T>
61  public:
62  IndexerHandlerImpl(const std::string& type_name,
63  bool (This::*get)(size_t, T*) const,
64  void (This::*set)(size_t, T))
65  : type_name_(type_name), get_(get), set_(set) {}
66 
67  ReturnVal<JsValue> GetIndex(Handle<JsObject> that, size_t index) override {
68  RefPtr<This> obj;
69  if (!FromJsValue(that, &obj)) {
70  ThrowError</* ReturnPromise */ false>::IllegalInvocation(
71  nullptr, "indexer", type_name_);
72  return JsUndefined();
73  }
74 
75  T value;
76  if (!((obj.get())->*get_)(index, &value))
77  return JsUndefined(); // Not found.
78  return ToJsValue(value);
79  }
80 
81  void SetIndex(Handle<JsObject> that, size_t index,
82  Handle<JsValue> given) override {
83  RefPtr<This> obj;
84  if (!FromJsValue(that, &obj)) {
85  ThrowError</* ReturnPromise */ false>::IllegalInvocation(
86  nullptr, "indexer", type_name_);
87  return;
88  }
89  if (!set_) {
90  ThrowError</* ReturnPromise */ false>::General(
91  nullptr, "indexer", type_name_,
92  "Indexer on '" + type_name_ + "' is read-only.");
93  return;
94  }
95 
96  T value;
97  if (!FromJsValue(given, &value)) {
98  ThrowError</* ReturnPromise */ false>::CannotConvert(
99  nullptr, "indexer", type_name_, ConvertToString(given),
101  return;
102  }
103  ((obj.get())->*set_)(index, std::move(value));
104  }
105 
106  private:
107  const std::string type_name_;
108  bool (This::*get_)(size_t, T*) const;
109  void (This::*set_)(size_t, T);
110 };
111 
112 } // namespace impl
113 
114 namespace util {
115 
116 #ifdef USING_JSC
117 template <>
118 struct RefTypeTraits<JSClassRef> {
119  // The API docs for JSClassCreate says it follows the "Create" rule, which
120  // suggests we shouldn't have to acquire; however, if we don't, we will
121  // occasionally get crashes that appear like the class is getting deleted
122  // while we reference it. Setting this to true seems to workaround it.
123  // TODO: Investigate this further and fix or file a bug.
124  static constexpr const bool AcquireWithRaw = true;
125 
126  static JSClassRef Duplicate(JSClassRef arg) {
127  if (arg)
128  return JSClassRetain(arg);
129  else
130  return nullptr;
131  }
132 
133  static void Release(JSClassRef arg) {
134  if (arg)
135  JSClassRelease(arg);
136  }
137 };
138 #endif
139 
140 } // namespace util
141 
157  public:
158  virtual ~BackingObjectFactoryBase();
159 
161  const std::string& name() const {
162  return type_name_;
163  }
164 
167  return base_;
168  }
169 
171  ReturnVal<JsValue> GetConstructor() const {
172  return RawToJsValue(Handle<JsFunction>(constructor_));
173  }
174 
175 #if defined(USING_JSC)
176  JSClassRef GetClass() const {
177  return class_definition_;
178  }
179 #endif
180 
185  bool DerivedFrom(const std::string& name) const;
186 
192  ReturnVal<JsValue> WrapInstance(BackingObject* object);
193 
194 
201  ReturnVal<JsValue> GetIndex(Handle<JsObject> that, size_t index);
202 
209  void SetIndex(Handle<JsObject> that, size_t index, Handle<JsValue> value);
210 
211  protected:
212  BackingObjectFactoryBase(const std::string& name, NativeCtor ctor,
213  const BackingObjectFactoryBase* base);
214 
215  template <typename Callback>
216  void AddMemberFunction(const std::string& name, Callback callback) {
217  LocalVar<JsFunction> js_function =
218  CreateMemberFunction(type_name_, name, MakeMemFn<void>(callback));
219  LocalVar<JsValue> value(RawToJsValue(js_function));
220  SetMemberRaw(prototype_, name, value);
221  }
222 
223  template <typename Ret, typename... Args>
224  void AddStaticFunction(const std::string& name, Ret (*callback)(Args...)) {
225  LocalVar<JsFunction> js_function =
226  CreateStaticFunction(type_name_, name, callback);
227  LocalVar<JsValue> value(RawToJsValue(js_function));
228  SetMemberRaw(constructor_, name, value);
229  }
230 
235  template <typename Prop>
236  void AddListenerField(js::EventType type, Prop member) {
237  AddReadWriteProperty("on" + to_string(type), member);
238  }
239 
240  template <typename This, typename Prop>
241  void AddReadOnlyProperty(const std::string& name, Prop This::*member) {
242 #ifndef ALLOW_STRUCT_FIELDS
243  static_assert(
244  !std::is_base_of<Struct, typename std::decay<Prop>::type>::value,
245  "Cannot store Structs in fields");
246 #endif
247 
248  auto getter = [=](RefPtr<This> that) -> Prop& {
249  return that.get()->*member;
250  };
251  LocalVar<JsFunction> js_getter =
252  CreateMemberFunction(type_name_, "get_" + name, std::move(getter));
253  LocalVar<JsFunction> setter;
254  SetGenericPropertyRaw(prototype_, name, js_getter, setter);
255  }
256 
257  template <typename This, typename Prop>
258  void AddReadWriteProperty(const std::string& name, Prop This::*member) {
259 #ifndef ALLOW_STRUCT_FIELDS
260  static_assert(
261  !std::is_base_of<Struct, typename std::decay<Prop>::type>::value,
262  "Cannot store Structs in fields");
263 #endif
264 
265  auto getter = [=](RefPtr<This> that) -> Prop& { return that->*member; };
266  LocalVar<JsFunction> js_getter =
267  CreateMemberFunction(type_name_, "get_" + name, std::move(getter));
268  auto setter = [=](RefPtr<This> that, Prop value) {
269  that->*member = std::move(value);
270  };
271  LocalVar<JsFunction> js_setter =
272  CreateMemberFunction(type_name_, "set_" + name, std::move(setter));
273 
274  SetGenericPropertyRaw(prototype_, name, js_getter, js_setter);
275  }
276 
277  template <typename Derived = void, typename This = void,
278  typename GetProp = void>
279  void AddGenericProperty(const std::string& name,
280  GetProp (This::*get)() const) {
281  LocalVar<JsFunction> getter = CreateMemberFunction(
282  type_name_, "get_" + name, MakeMemFn<Derived>(get));
283  LocalVar<JsFunction> setter;
284  SetGenericPropertyRaw(prototype_, name, getter, setter);
285  }
286 
287  template <typename Derived = void, typename This = void,
288  typename GetProp = void, typename SetProp = void,
289  typename SetPropRet = void>
290  void AddGenericProperty(const std::string& name, GetProp (This::*get)() const,
291  SetPropRet (This::*set)(SetProp)) {
292  // Use two different types so the setter can accept a const-reference.
294  "Getter and setter must use the same type.");
295  static_assert(std::is_same<void, SetPropRet>::value ||
296  std::is_same<ExceptionOr<void>, SetPropRet>::value,
297  "Setter can only return void.");
298  LocalVar<JsFunction> getter = CreateMemberFunction(
299  type_name_, "get_" + name, MakeMemFn<Derived>(get));
300  LocalVar<JsFunction> setter = CreateMemberFunction(
301  type_name_, "set_" + name, MakeMemFn<Derived>(set));
302  SetGenericPropertyRaw(prototype_, name, getter, setter);
303  }
304 
305  template <typename T>
306  void AddConstant(const std::string& name, T value) {
307  LocalVar<JsValue> js_value = ToJsValue(value);
308  SetMemberRaw(prototype_, name, js_value);
309  SetMemberRaw(constructor_, name, js_value);
310  }
311 
312  template <typename This, typename T>
313  void AddIndexer(bool (This::*get)(size_t, T*) const,
314  void (This::*set)(size_t, T) = nullptr) {
315  CHECK(!indexer_);
316  indexer_.reset(new impl::IndexerHandlerImpl<This, T>(type_name_, get, set));
317  }
318 
319  void NotImplemented(const std::string& name);
320 
321  private:
322  template <typename Derived, typename This>
323  using get_this = typename std::conditional<std::is_same<Derived, void>::value,
324  This, Derived>::type;
325 
326  template <typename Derived, typename Callback>
327  struct MakeMemFnImpl;
328  template <typename Derived, typename This, typename Ret, typename... Args>
329  struct MakeMemFnImpl<Derived, Ret (This::*)(Args...)> {
330  using ArgThis = get_this<Derived, This>;
331  using type = std::function<Ret(RefPtr<ArgThis>, Args...)>;
332  static type Make(Ret (This::*callback)(Args...)) {
333  return [=](RefPtr<ArgThis> that, Args... args) -> Ret {
334  return ((that.get())->*(callback))(std::move(args)...);
335  };
336  }
337  };
338  template <typename Derived, typename This, typename Ret, typename... Args>
339  struct MakeMemFnImpl<Derived, Ret (This::*)(Args...) const> {
340  using ArgThis = get_this<Derived, This>;
341  using type = std::function<Ret(RefPtr<ArgThis>, Args...)>;
342  static type Make(Ret (This::*callback)(Args...) const) {
343  return [=](RefPtr<ArgThis> that, Args... args) -> Ret {
344  return ((that.get())->*(callback))(std::move(args)...);
345  };
346  }
347  };
348 
349  template <typename Derived, typename Callback>
350  static typename MakeMemFnImpl<Derived, Callback>::type MakeMemFn(
351  Callback callback) {
352  return MakeMemFnImpl<Derived, Callback>::Make(callback);
353  }
354 
355  const std::string type_name_;
356  const BackingObjectFactoryBase* const base_;
357  std::unique_ptr<impl::IndexerHandler> indexer_;
358  Global<JsFunction> constructor_;
359  Global<JsObject> prototype_;
360 #ifdef USING_V8
361  Global<v8::FunctionTemplate> class_definition_;
362 #elif defined(USING_JSC)
363  JSClassDefinition definition_;
364  util::CFRef<JSClassRef> class_definition_;
365 #endif
366 };
367 
368 
376 template <typename T>
378  : public BackingObjectFactoryBase,
379  private PseudoSingleton<BackingObjectFactoryRegistry<T>> {
380  public:
382  : BackingObjectFactoryBase(T::name(), &impl::JsConstructor<T>::Call,
383  base) {}
384 
393  }
394 
395  private:
397 };
398 template <>
401  return nullptr;
402 }
403 
404 
405 template <typename T, typename Base = void>
407  public:
410  BackingObjectFactoryRegistry<Base>::CheckedInstance()) {}
411 };
412 
413 } // namespace shaka
414 
415 #endif // SHAKA_EMBEDDED_MAPPING_BACKING_OBJECT_FACTORY_H_
void AddMemberFunction(const std::string &name, Callback callback)
ReturnVal< JsFunction > CreateStaticFunction(const std::string &target, const std::string &name, Func &&callback)
ReturnVal< JsValue > JsUndefined()
Definition: js_wrappers.cc:284
bool FromJsValue(Handle< JsValue > source, T *dest)
Definition: convert_js.h:370
void AddGenericProperty(const std::string &name, GetProp(This::*get)() const, SetPropRet(This::*set)(SetProp))
void AddReadWriteProperty(const std::string &name, Prop This::*member)
ReturnVal< JsValue > GetConstructor() const
void AddGenericProperty(const std::string &name, GetProp(This::*get)() const)
const char * name
void SetIndex(Handle< JsObject > that, size_t index, Handle< JsValue > given) override
ReturnVal< JsValue > GetIndex(Handle< JsObject > that, size_t index) override
ReturnVal< JsValue > ToJsValue(T &&source)
Definition: convert_js.h:381
std::string to_string(VideoReadyState state)
Definition: media_player.cc:32
ReturnVal< JsValue > RawToJsValue(Handle< T > source)
Definition: js_wrappers.h:405
BackingObjectFactoryRegistry(BackingObjectFactoryBase *base)
IndexerHandlerImpl(const std::string &type_name, bool(This::*get)(size_t, T *) const, void(This::*set)(size_t, T))
ExceptionCode type
T * get() const
Definition: ref_ptr.h:125
virtual void SetIndex(Handle< JsObject > that, size_t index, Handle< JsValue > value)=0
virtual ReturnVal< JsValue > GetIndex(Handle< JsObject > that, size_t index)=0
void AddIndexer(bool(This::*get)(size_t, T *) const, void(This::*set)(size_t, T)=nullptr)
impl::get_const_reference_at_t< I, Choices... > get(const variant< Choices... > &variant)
Definition: variant.h:456
static BackingObjectFactoryRegistry< T > * Instance()
const std::string & name() const
void AddListenerField(js::EventType type, Prop member)
static BackingObjectFactoryBase * CheckedInstance()
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
void AddConstant(const std::string &name, T value)
void AddReadOnlyProperty(const std::string &name, Prop This::*member)
const BackingObjectFactoryBase * base() const
void AddStaticFunction(const std::string &name, Ret(*callback)(Args...))
ReturnVal< JsFunction > CreateMemberFunction(const std::string &target, const std::string &name, Func &&callback)
void SetGenericPropertyRaw(Handle< JsObject > object, const std::string &name, Handle< JsFunction > getter, Handle< JsFunction > setter)
Definition: js_wrappers.cc:158