This commit is contained in:
Luis Blanco 2019-11-11 17:07:04 +03:00
parent 77995f76f6
commit 90a417bedf
3 changed files with 59 additions and 71 deletions

View File

@ -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.
</details>
@ -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) {
</details>
<details>
<summary>Set object accessors</summary>
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) { ...
```
</details>
<details>
<summary>Setter argument</summary>
@ -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)
</details>
<details>
<summary>Data retrieval</summary>
<summary>JS Data to C++ Data</summary>
* `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.
</details>

View File

@ -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.

View File

@ -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<typename Type = uint8_t>
inline Type* getArrayData(
@ -400,6 +396,8 @@ inline void *getData(Napi::Env env, Napi::Object obj) {
if (obj.IsTypedArray() || obj.IsArrayBuffer()) {
pixels = getArrayData<uint8_t>(env, obj);
} else if (obj.IsBuffer()) {
pixels = getBufferData<uint8_t>(env, obj);
} else if (obj.Has("data")) {
Napi::Object data = obj.Get("data").As<Napi::Object>();
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<napi_value> args;
for (int i = 0; i < argc; i++) {
args.push_back(napi_value(argv[i]));
}
log.As<Napi::Function>().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::Object>();
Napi::Function setProto = globalObject.Get("setPrototypeOf").As<Napi::Function>();
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( \