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