• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
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  *     http://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 
16 #ifndef FOUNDATION_ACE_FRAMEWORKS_DECLARATIVE_FRONTEND_ENGINE_BINDINGS_IMPLEMENTATION_H
17 #define FOUNDATION_ACE_FRAMEWORKS_DECLARATIVE_FRONTEND_ENGINE_BINDINGS_IMPLEMENTATION_H
18 
19 #include <cmath>
20 #include <string>
21 #include <unordered_map>
22 #include <vector>
23 
24 #include "base/log/log.h"
25 #include "base/memory/ace_type.h"
26 #include "bindings_defines.h"
27 #include "frameworks/bridge/common/utils/function_traits.h"
28 #include "frameworks/bridge/declarative_frontend/engine/js_ref_ptr.h"
29 #include "frameworks/bridge/declarative_frontend/engine/js_types.h"
30 
31 namespace OHOS::Ace::Framework {
32 
33 enum MethodOptions : uint8_t {
34     NONE = 0,
35     RETURN_SELF = 1 << 0, // for chaining
36     STRICT_TYPE_CHECK = 1 << 1,
37 };
38 
39 class IFunctionBinding {
40 public:
IFunctionBinding(const char * name,MethodOptions options)41     IFunctionBinding(const char* name, MethodOptions options) : name_(name), options_(options) {}
~IFunctionBinding()42     virtual ~IFunctionBinding() {}
43 
Name()44     const char* Name() const
45     {
46         return name_;
47     }
48 
Options()49     MethodOptions Options() const
50     {
51         return options_;
52     }
53 
54 private:
55     const char* name_;
56     MethodOptions options_;
57 };
58 
59 template<typename Class, typename ReturnType, typename... Args>
60 class FunctionBinding : public IFunctionBinding {
61     using FunctionPtr = ReturnType (Class::*)(Args...);
62 
63 public:
FunctionBinding(const char * name,MethodOptions options,FunctionPtr func)64     FunctionBinding(const char* name, MethodOptions options, FunctionPtr func)
65         : IFunctionBinding(name, options), func_(func)
66     {}
67 
68     ~FunctionBinding() override = default;
69 
Get()70     FunctionPtr Get()
71     {
72         return func_;
73     }
74 
75 private:
76     FunctionPtr func_;
77 };
78 
79 template<typename ReturnType, typename... Args>
80 class StaticFunctionBinding : public IFunctionBinding {
81     using FunctionPtr = ReturnType (*)(Args...);
82 
83 public:
StaticFunctionBinding(const char * name,MethodOptions options,FunctionPtr func)84     StaticFunctionBinding(const char* name, MethodOptions options, FunctionPtr func)
85         : IFunctionBinding(name, options), func_(func)
86     {}
87     ~StaticFunctionBinding() override = default;
88 
Get()89     FunctionPtr Get()
90     {
91         return func_;
92     }
93 
94 private:
95     FunctionPtr func_;
96 };
97 
98 /**
99  *  \brief A class template that binds C++ classes to Javascript.
100  *
101  *  This class is the entry point for binding classes and methods to Javascript regardless of which engine is
102  *  underneath. In the following text, a class C is the C++ class to be bound to Javascript, and engine-specific
103  *  implementation is abbreviated as ESI.
104  *
105  *  Methods and member variables registered to javascript are equivalent to a property in a javascript object.
106  *  \p Method(...) is used to register methods. If a member function is registered directly, the ESI should take care of
107  *  the value conversion between Javascript and C++ types.
108  *
109  *  If more control is required than just mere conversions between JS and C++ (such as error checks, weak types, storing
110  * JS objects etc.), methods can be bound to a signature that enables this control. The signature is <tt> void(*)(const
111  * JSCallbackInfo&)</tt>. This is done to work regardless of the engine underneath. See \p JSRef , \p JSRefPtr for more
112  * info on usage.
113  *
114  *  If a direct communication with the engine is required when communicating with javascript, member functions and
115  * callbacks with engine-specific signatures can be registered to Javascript and the embedder can access the engine APIs
116  * directly. Additional options can be passed to \p Method(...) when binding member functions, such as the object can
117  * return itself in case of functions that do not return a value, so that in Javascript one can do so-called "chaining":
118  *  \code{.js}
119  *  object.width(10).height(10).top(0).left(20)
120  *  \endcode
121  *
122  *  There is a general constructor for every bound class. When calling \p Bind however, the constructor argument types
123  *  must be passes as template arguments (see example) so that the automatic conversion checks and converts the values.
124  *
125  *
126  *
127  *  Engine-specific implementation guide:
128  *  This code is using the curiously recurring template pattern (CRTP) idiom as neither function templates nor static
129  *  methods can be overloaded. Therefore, an ESI "inherits" from this class because these class methods call methods
130  *  that have to be defined, otherwise the compiler will report an error. As the binding itself is considerably
131  *  engine-specific, this class implements only engine-agnostic attributes such as class names, method names, member
132  *  function pointers etc. The actual value conversion is to be implemented by the engine implementation, and choosing
133  *  the engine to be used should be conducted by the build system.
134  *
135  *  ESIs are expected to define several aliases:
136  *  1. one called \p JSClass with the class C and the ESI class template as template
137  *  arguments to this class:
138  *  \code{.cpp}
139  *  template<typename C>
140  *  using JSClass = JSClassImpl<C, MyJSEngineClassImpl>
141  *  \endcode
142  *
143  *  Inside the ImplDetail class template:
144  *  2.  \p BindingTarget that corresponds to an ESI object template
145  *  \code{.cpp}
146  *  // A v8 object template
147  *  using BindingTarget = v8::Local<v8::ObjectTemplate>;
148  *  // A QJS object template
149  *  using BindingTarget = JSValue;
150  *  \endcode
151  *
152  *  3. \p FunctionCallback and \p MemberFunctionCallback corresponding to ESI callback signatures:
153  *  \code{.cpp}
154  *  // v8 callback signatures
155  *  using FunctionCallback = void (*)(const v8::FunctionCallbackInfo<v8::Value>&);
156  *  using MemberFunctionCallback = void (C::*)(const v8::FunctionCallbackInfo<v8::Value>&);
157  *  // QJS callback signatures
158  *  using FunctionCallback = JSValue (*)(JSContext* ctx, JSValueConst thisObj, int argc, JSValueConst* argv);
159  *  using MemberFunctionCallback = JSValue (C::*)(JSContext* ctx, JSValueConst thisObj, int argc, JSValueConst* argv);
160  *  \endcode
161  *
162  *
163  *  \tparam C The C++ class to be bound
164  *  \tparam ImplDetail an engine-specific class template that takes class C as a template argument
165  *
166  *  \example Binding classes TwoDPoint and ThreeDPoint as "Point2" and "Point3" in javascript, and registering its
167  * methods.
168  *
169  *  \code{.cpp}
170  *
171  *  // We are using V8 engine for this example:
172  *  template<typename C>
173  *  using JSClass = JSClassImpl<C, V8Class>;
174  *
175  *  // Somewhere in engine-initialization:
176  *  JSClass<TwoDPoint>::Declare("Point2");
177  *  JSClass<TwoDPoint>::Method("setX", &TwoDPoint::SetX, MethodOptions::RETURN_SELF);
178  *  JSClass<TwoDPoint>::Method("getX", &TwoDPoint::GetX);
179  *  JSClass<TwoDPoint>::Method("setY", &TwoDPoint::SetY, MethodOptions::RETURN_SELF);
180  *  JSClass<TwoDPoint>::Method("getY", &TwoDPoint::GetY);
181  *  JSClass<TwoDPoint>::Method("print", &TwoDPoint::Print);
182  *  JSClass<TwoDPoint>::StaticMethod("parse", &TwoDPoint::Parse);
183  *  JSClass<TwoDPoint>::Bind<float, float>(globalObject);   // Note the template arguments. Here we are specifying
184  *                                                          // that we're expecting the JS constructor to accept
185  *                                                          // two "float" arguments
186  *
187  *  JSClass<ThreeDPoint>::Declare("Point3");
188  *  JSClass<ThreeDPoint>::Method("setZ", &ThreeDPoint::SetZ, MethodOptions::RETURN_SELF);
189  *  JSClass<ThreeDPoint>::Method("getZ", &ThreeDPoint::GetZ);
190  *  JSClass<ThreeDPoint>::Inherit<TwoDPoint>();
191  *  JSClass<ThreeDPoint>::Bind<float, float, float>(globalObject);
192  *                                                           // Note the template arguments. Here we are specifying
193  *                                                           // that we're expecting the JS constructor to accept
194  *                                                           // three "float" arguments
195  *  \endcode
196  *
197  * \code{.js}
198  *                                            // C++ call tree
199  * let point = new Point2(1,2);               // V8Class<TwoDPoint>::InternalConstructor<float, float>
200  * point.print();                             // V8Class<TwoDPoint>::InternalMethodCallback<void>
201  *                                            //     "Point(1,2)"
202  * let other = Point2.parse("(3,4)");         // V8Class<TwoDPoint>::JSStaticMethodCallback
203  * console.log("point.x is " + point.getX()); // V8Class<TwoDPoint>::InternalMethodCallback<float>
204  *                                            //     "point.x is 1"
205  * console.log("other.x is " + other.getX()); // V8Class<TwoDPoint>::InternalMethodCallback<float>
206  *                                            //     "other.x is 3"
207  * point.setX(5).setY(10);                    // V8Class<TwoDPoint>::InternalMethodCallback<void, float>
208  *                                            // V8Class<TwoDPoint>::InternalMethodCallback<void, float>
209  * point.print();                             // V8Class<TwoDPoint>::InternalMethodCallback<void>
210  *                                            //     "Point(5,10)"
211  * let anotherPoint = new Point3(1,2,3);      // V8Class<ThreeDPoint>::InternalConstructor<float, float, float>
212  * anotherPoint.setX(5).setY(6).setZ(7)       // V8Class<TwoDPoint>::InternalMethodCallback<void, float>
213  *                                            // V8Class<TwoDPoint>::InternalMethodCallback<void, float>
214  *                                            // V8Class<ThreeDPoint>::InternalMethodCallback<void, float>
215  * anotherpoint.print();                      // V8Class<ThreeDPoint>::InternalMethodCallback<void>
216  *                                            //     "Point(5,6,7)"
217  * \endcode
218  * \class JSClassImpl
219  */
220 template<typename C, template<typename> typename ImplDetail>
221 class JSClassImpl {
222 public:
223     JSClassImpl() = delete;
224 
225     /**
226      *  Declare class C that will be exposed with the given \p name in Javascript
227      *  \note This must be always called first before any other registrations. The engine-specific implementations
228      *  should instantiate object templates with this call
229      *  \param name A string literal
230      *  \static
231      */
232     static void Declare(const char* name);
233 
234     /**
235      *  Register a method that is a member of a class C or its base class.
236      *  \note Trying to bind a method of unrelated classes will result in a compile error
237      *  \param name The name of the method that will be exposed as in Javascript
238      *  \param func A member-function pointer belonging to class C's base class
239      *  \param options Method options flags, default value is NONE
240      *
241      *  \tparam Base A base class to \p C . No need to specify, since it will be deducted from the function pointer
242      *  \tparam R The return type of \p func . No need to specify, since it will be deducted from the function pointer
243      *  \tparam Args... Types of function arguments of \p func . No need to specify, since they will be deducted from
244      * the function pointer
245      *  \static
246      */
247     template<typename Base, typename R, typename... Args>
248     static void Method(const char* name, R (Base::*func)(Args...), MethodOptions options = MethodOptions::NONE);
249 
250     /**
251      *  Register a static method of class C.
252      *  \param name The name of the method that will be exposed as in Javascript
253      *  \param func A static function
254      *  \param options Method options flags, default value is NONE
255      *
256      *  \tparam R The return type of \p func . No need to specify, since it will be deducted from the function pointer
257      *  \tparam Args... Types of function arguments of \p func . No need to specify, since they will be deducted from
258      * the function pointer
259      *  \static
260      */
261     template<typename R, typename... Args>
262     static void StaticMethod(const char* name, R (*func)(Args...), MethodOptions options = MethodOptions::NONE);
263 
264     /**
265      *  Register a static method of class C with a void(*)(const JSCallbackInfo&) signature.
266      *  \param name The name of the method that will be exposed as in Javascript
267      *  \param func A static function with void(*)(const JSCallbackInfo&) signature
268      *  \static
269      */
270     static void StaticMethod(const char* name, JSFunctionCallback func);
271 
272     /**
273      *  Register a method that is a member of a related class T with an engine-specific callback signature
274      *
275      *  \tparam T A class that is either equivalent or a base to C
276      *  \param name The name of the method that will be exposed as in Javascript
277      *  \param callback A member-function pointer belonging to class C's class with engine-specific signature
278      *  \static
279      */
280     template<typename T>
281     static void CustomMethod(const char* name, MemberFunctionCallback<T> callback);
282 
283     /**
284      *  Register a method with an engine-specific callback signature
285      *
286      *  \param name The name of the method that will be exposed as in Javascript
287      *  \param callback A function pointer with the engine-specific signature
288      *  \static
289      */
290     static void CustomMethod(const char* name, FunctionCallback callback);
291 
292     /**
293      *  Register a method with an generic callback signature
294      *
295      *  \param name The name of the method that will be exposed as in Javascript
296      *  \param callback A function pointer with the engine-specific signature
297      *  \static
298      */
299     template<typename T>
300     static void CustomMethod(const char* name, JSMemberFunctionCallback<T> callback);
301 
302     template<typename T>
303     static void CustomProperty(const char* name, MemberFunctionGetCallback<T> getter,
304         MemberFunctionSetCallback<T> setter);
305 
306     static void CustomProperty(const char* name, FunctionGetCallback getter,
307         FunctionSetCallback setter);
308 
309     template<typename T>
310     static void CustomProperty(const char* name, JSMemberFunctionCallback<T> getter,
311         JSMemberFunctionCallback<T> setter);
312     /**
313      *  Register a static method with an engine-specific callback signature
314      *
315      *  \param name The name of the method that will be exposed as in Javascript
316      *  \param callback A function pointer with the engine-specific signature
317      *  \static
318      */
319     static void CustomStaticMethod(const char* name, FunctionCallback callback);
320 
321     static void ExoticGetter(ExoticGetterCallback callback);
322     static void ExoticSetter(ExoticSetterCallback callback);
323     static void ExoticHasProperty(ExoticHasPropertyCallback callback);
324 
325     template<typename T>
326     static void StaticConstant(const char* name, T value);
327 
328     /**
329      *  Bind the class to Javascript with a custom constructor that has engine-specific callback signature
330      *
331      *  \param bindTarget An object template to bind this class to.
332      *  \param ctor Constructor
333      *  \static
334      */
335     static void Bind(BindingTarget bindTarget, FunctionCallback ctor);
336 
337     /**
338      *  Bind the class to Javascript with custom constructor, destructor and GC mark callbacks.
339      *  If no destructor callback is specified, the C++ instance is simply "delete"-d on garbage collection sweeps.
340      *
341      *  \param bindTarget An object template to bind this class to
342      *  \param ctor Constructor with void(*)(const JSCallbackInfo&) signature
343      *  \param dtor Destructor with void(*)(C* instance) signature (optional)
344      *  \param gcMark A GC mark callback with void(*)(C* instance, const JSGCMarkCallbackInfo&) signature (optional)
345      *  \static
346      */
347     static void Bind(BindingTarget bindTarget, JSFunctionCallback ctor, JSDestructorCallback<C> dtor = nullptr,
348         JSGCMarkCallback<C> gcMark = nullptr);
349 
350     /**
351      *  Bind the class to Javascript with optional destructor and GC mark callbacks.
352      *  If no destructor callback is specified, the C++ instance is simply "delete"-d on garbage collection sweeps.
353      *
354      *  \tparam Args... A list of argument types that the constructor of class C accepts.
355      *  \param bindTarget An object template to bind this class to.
356      *  \param dtor Destructor with void(*)(C* instance) signature (optional)
357      *  \param gcMark A GC mark callback with void(*)(C* instance, const JSGCMarkCallbackInfo&) signature (optional)
358      *  \static
359      */
360     template<typename... Args>
361     static void Bind(
362         BindingTarget bindTarget, JSDestructorCallback<C> dtor = nullptr, JSGCMarkCallback<C> gcMark = nullptr);
363 
364     /**
365      *  Inherit all bound methods and properties from \p Base
366      *  \note A binding for the base class must exist beforehand with
367      *  \code JSClassImpl<Base,Impl>::Declare("MyBaseClass") \endcode
368      *
369      *  \tparam Base A base class of C
370      */
371     template<typename Base>
372     static void Inherit();
373 
374     static IFunctionBinding* GetFunctionBinding(int id);
375     static IFunctionBinding* GetGetFunctionBinding(int id);
376     static IFunctionBinding* GetSetFunctionBinding(int id);
377 
378     /**
379      *  Get the Javascript name of class C
380      *  \return The javascript name
381      */
382     static const char* JSName();
383 
384     /**
385      *  Create new instance of declared class
386      *  \return new JS object instance
387      */
388     static JSRef<JSObject> NewInstance();
389 
390 private:
391     static thread_local std::string jsName;
392     /*  OPTIMIZE(cvetan): Functions can be stored at compile-time with an additional index as a template parameter, for
393         example:
394 
395         template<int N, typename RetType, typename... Args>
396         static RetType(C::*function_)(Args...);
397 
398         But only if current approach proves to be a bottleneck.
399     */
400     static thread_local std::unordered_map<int, std::unique_ptr<IFunctionBinding>> functions_;
401     static thread_local std::unordered_map<int, std::unique_ptr<IFunctionBinding>> getFunctions_;
402     static thread_local std::unordered_map<int, std::unique_ptr<IFunctionBinding>> setFunctions_;
403     static thread_local int nextFreeId_;
404 };
405 
406 }; // namespace OHOS::Ace::Framework
407 
408 #include "bindings_implementation.inl"
409 
410 #endif // FOUNDATION_ACE_FRAMEWORKS_DECLARATIVE_FRONTEND_ENGINE_BINDINGS_IMPLEMENTATION_H
411