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