From 90a417bedf6b6fb1a1e130a0bbf72701ff4c62ff Mon Sep 17 00:00:00 2001 From: Luis Blanco Date: Mon, 11 Nov 2019 17:07:04 +0300 Subject: [PATCH] wip doc --- doc/addon-tools.md | 51 +++++++++----------------------------- doc/class-wrapping.md | 25 +++++++++++++++++-- include/addon-tools.hpp | 54 +++++++++++++++++++---------------------- 3 files changed, 59 insertions(+), 71 deletions(-) diff --git a/doc/addon-tools.md b/doc/addon-tools.md index 525f802..675cdeb 100644 --- a/doc/addon-tools.md +++ b/doc/addon-tools.md @@ -58,8 +58,6 @@ have `CallbackInfo info` passed as an argument. * `JS_NUM(VAL)` - create a `Napi::Number` value. * `JS_EXT(VAL)` - create a `Napi::External` (from pointer) value. * `JS_BOOL(VAL)` - create a `Napi::Boolean` value. -* `JS_FUN(VAL)` - create a `Napi::Function` value. -* `JS_OBJ(VAL)` - create a `Napi::Object` value. @@ -70,7 +68,7 @@ have `CallbackInfo info` passed as an argument. These checks throw JS TypeError if not passed. Here `T` is always used as a typename in error messages. `C` is a -[Napi::Value](https://github.com/nodejs/node-addon-api/blob/master/doc/value.md#isboolean) +[Napi::Value](https://github.com/nodejs/node-addon-api/blob/master/doc/value.md) check method, like `IsObject()`. `I` is the index of argument as in `info[I]`, starting from `0`. @@ -151,7 +149,6 @@ That extrapolates well to all the helpers below: ``` NAN_METHOD(test) { - REQ_UINT32_ARG(0, width); REQ_UINT32_ARG(1, height); LET_FLOAT_ARG(2, z); @@ -162,37 +159,6 @@ NAN_METHOD(test) { -
- -Set object accessors - -Simplified accessor assignment, adds accessors of NAME for OBJ. Read accessor is -assumed to have the name `NAME+'Getter'` and write accessor is `NAME+'Setter'`. - -* `ACCESSOR_RW(CLASS, NAME)` - add read and write accessors NAME of CLASS. -* `ACCESSOR_R(CLASS, NAME)` - add read accessor NAME of CLASS. -* `ACCESSOR_M(CLASS, NAME)` - add method NAME of CLASS. - - -``` -void MyClass::init(Napi::Env env, Napi::Object exports) { - ... - Napi::Function ctor = DefineClass(env, "MyClass", { - ACCESSOR_R(MyClass, isDestroyed), - ACCESSOR_RW(MyClass, x), - ACCESSOR_M(MyClass, reset), - }); - ... -} -JS_GETTER(MyClass::isDestroyedGetter) { ... -JS_GETTER(MyClass::xGetter) { ... -JS_SETTER(MyClass::xSetter) { ... -JS_METHOD(MyClass::save) { ... -``` - -
- -
Setter argument @@ -215,24 +181,29 @@ argument, from which a C++ value is extracted. * `SETTER_ARRV_ARG` ``` -JS_SETTER(MyClass::x) { SETTER_STR_ARG; +JS_IMPLEMENT_SETTER(MyClass, x) { THIS_CHECK; SETTER_STR_ARG; // Variable created: std::string v; ... ``` +See also: [Class Wrapping](class-wrapping.md) +
-Data retrieval +JS Data to C++ Data * `T *getArrayData(value, num = NULL)` - extracts TypedArray data of any type from the given JS value. Does not accept `Array`. Checks with `IsArrayBuffer()`. Returns `nullptr` for empty JS values. For unacceptable values throws TypeError. -* `void *getData(value)` - if value is a TypedArray, then the result of -`getArrayData(value)` is returned. Otherwise if value has `'data'` property, it's -content is then returned as `node::Buffer`. Returns `nullptr` in other cases. +* `T *getBufferData(value, num = NULL)` - extracts Buffer data from +the given JS value. Checks with `IsBuffer()`. +Returns `nullptr` for empty JS values. For unacceptable values throws TypeError. + +* `void *getData(value)` - if `value` or `value.data` is a `TypedArray|Buffer`, +calls `getArrayData` or `getArrayData` respectively. Returns `nullptr` in other cases.
diff --git a/doc/class-wrapping.md b/doc/class-wrapping.md index 6733277..259ec93 100644 --- a/doc/class-wrapping.md +++ b/doc/class-wrapping.md @@ -1,5 +1,16 @@ # Es5 class wrapping +For NAPI addons this allows to call `super()` from C++ and makes the class +constructor callable with `ClassName.call(obj, ...args)`. +Also multiple C++ objects can be attached to a single JS object +if it is necessary in an inheritance scenario. +On JS side `util.inherits` +[is used](https://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor), +and on C++ side `inheritEs5`. +This implementation is using composition rather than inheritance, so +it is easily pluggable. + + ## Class Declaration ``` @@ -40,7 +51,6 @@ the second is the name of the setter to be created. ## Class Implementation ``` -// Some utility stuff IMPLEMENT_ES5_CLASS(ClassName); // Fill the properties and export the class @@ -83,5 +93,16 @@ JS_IMPLEMENT_GETTER(ClassName, isDestroyed) { THIS_CHECK; RET_BOOL(_isDestroyed); } - ``` + +`IMPLEMENT_ES5_CLASS` - implements some utility functions for class wrapping. +`JS_ASSIGN_METHOD` - in `init()`, assigns the given method to this class. +`JS_ASSIGN_GETTER` - in `init()`, assigns the given getter to this class. +`JS_ASSIGN_SETTER` - in `init()`, assigns both getter and setter to this class. +It also takes only one argument because both have the same name. +`JS_IMPLEMENT_METHOD` - implements a method, the first argument is this class, +the second is the name of the method being implemented. +`JS_IMPLEMENT_GETTER` - implements a getter, the first argument is this class, +the second is the name of the getter being implemented. +`JS_IMPLEMENT_SETTER` - implements a setter, the first argument is this class, +the second is the name of the setter being implemented. diff --git a/include/addon-tools.hpp b/include/addon-tools.hpp index bc29d95..9582216 100644 --- a/include/addon-tools.hpp +++ b/include/addon-tools.hpp @@ -310,25 +310,21 @@ } \ } while (0) -#define NAPI_CALL(the_call, ATE) \ +#define NAPI_CALL(the_call) \ do { \ if ((the_call) != napi_ok) { \ GET_AND_THROW_LAST_ERROR(); \ - ATE; \ + RET_UNDEFINED; \ } \ } while (0) -#define JS_RUN_3(code, VAR, ATE) \ +#define JS_RUN(code, VAR) \ napi_value __RESULT_ ## VAR; \ NAPI_CALL( \ - napi_run_script(env, napi_value(JS_STR(code)), &__RESULT_ ## VAR), \ - ATE \ + napi_run_script(env, napi_value(JS_STR(code)), &__RESULT_ ## VAR) \ ); \ Napi::Value VAR(env, __RESULT_ ## VAR); -#define JS_RUN_2(code, VAR) JS_RUN_3(code, VAR, return) -#define JS_RUN JS_RUN_3 - template inline Type* getArrayData( @@ -400,6 +396,8 @@ inline void *getData(Napi::Env env, Napi::Object obj) { if (obj.IsTypedArray() || obj.IsArrayBuffer()) { pixels = getArrayData(env, obj); + } else if (obj.IsBuffer()) { + pixels = getBufferData(env, obj); } else if (obj.Has("data")) { Napi::Object data = obj.Get("data").As(); if (data.IsTypedArray() || data.IsArrayBuffer()) { @@ -414,23 +412,25 @@ inline void *getData(Napi::Env env, Napi::Object obj) { } -inline void consoleLog( +inline Napi::Value consoleLog( Napi::Env env, int argc, const Napi::Value *argv ) { - JS_RUN_2("console.log", log); + JS_RUN("console.log", log); std::vector args; for (int i = 0; i < argc; i++) { args.push_back(napi_value(argv[i])); } log.As().Call(args); + RET_UNDEFINED; } -inline void consoleLog(Napi::Env env, const std::string &message) { +inline Napi::Value consoleLog(Napi::Env env, const std::string &message) { Napi::Value arg = JS_STR(message); consoleLog(env, 1, &arg); + RET_UNDEFINED; } @@ -490,31 +490,26 @@ inline void eventEmitAsync( inline void inheritEs5( - napi_env env, + Napi::Env env, Napi::Function ctor, Napi::Function superCtor ) { - napi_value global; - napi_value globalObject; - napi_value setProto; - napi_value ctorProtoProp; - napi_value superCtorProtoProp; - napi_value argv[2]; + Napi::Object global = env.Global(); + Napi::Object globalObject = global.Get("Object").As(); + Napi::Function setProto = globalObject.Get("setPrototypeOf").As(); + Napi::Value ctorProtoProp = ctor.Get("prototype"); + Napi::Value superCtorProtoProp = superCtor.Get("prototype"); - napi_get_global(env, &global); - napi_get_named_property(env, global, "Object", &globalObject); - napi_get_named_property(env, globalObject, "setPrototypeOf", &setProto); - napi_get_named_property(env, ctor, "prototype", &ctorProtoProp); - napi_get_named_property(env, superCtor, "prototype", &superCtorProtoProp); + napi_value argv[2]; argv[0] = ctorProtoProp; argv[1] = superCtorProtoProp; - napi_call_function(env, global, setProto, 2, argv, nullptr); + setProto.Call(global, 2, argv); argv[0] = ctor; argv[1] = superCtor; - napi_call_function(env, global, setProto, 2, argv, nullptr); + setProto.Call(global, 2, argv); ctor.Set("super_", superCtor); @@ -640,11 +635,11 @@ private: \ ); #define JS_DECLARE_METHOD(CLASS, NAME) \ - inline static Napi::Value __st_##NAME(const Napi::CallbackInfo &info) { \ + inline static JS_METHOD(__st_##NAME) { \ JS_GET_THAT(CLASS); \ return that->__i_##NAME(info); \ }; \ - Napi::Value __i_##NAME(const Napi::CallbackInfo &info); + JS_METHOD(__i_##NAME); #define JS_DECLARE_GETTER(CLASS, NAME) JS_DECLARE_METHOD(CLASS, NAME##Getter) @@ -661,9 +656,10 @@ private: \ ); #define JS_IMPLEMENT_METHOD(CLASS, NAME) \ - Napi::Value CLASS::__i_##NAME(const Napi::CallbackInfo &info) + JS_METHOD(CLASS::__i_##NAME) -#define JS_IMPLEMENT_GETTER(CLASS, NAME) JS_IMPLEMENT_METHOD(CLASS, NAME##Getter) +#define JS_IMPLEMENT_GETTER(CLASS, NAME) \ + JS_IMPLEMENT_METHOD(CLASS, NAME##Getter) #define JS_IMPLEMENT_SETTER(CLASS, NAME) \ Napi::Value CLASS::__i_##NAME##Setter( \