From 98aaf2d4ba38bbc539ce86c2b123da7fb08e41f0 Mon Sep 17 00:00:00 2001 From: Luis Blanco Date: Tue, 3 Sep 2019 22:06:57 +0300 Subject: [PATCH] Add ES5 classes --- include/addon-tools.hpp | 154 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/include/addon-tools.hpp b/include/addon-tools.hpp index 6d744be..b3a6d91 100644 --- a/include/addon-tools.hpp +++ b/include/addon-tools.hpp @@ -1,7 +1,7 @@ #ifndef _ADDON_TOOLS_HPP_ #define _ADDON_TOOLS_HPP_ - +#define NODE_ADDON_API_DISABLE_DEPRECATED #include @@ -479,5 +479,157 @@ inline void eventEmitAsync( } +inline void inheritEs5(napi_env env, Napi::Function ctor, Napi::Function superCtor) { + napi_value global, globalObject, setProto, ctorProtoProp, superCtorProtoProp; + napi_value argv[2]; + + 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); + + argv[0] = ctorProtoProp; + argv[1] = superCtorProtoProp; + napi_call_function(env, global, setProto, 2, argv, nullptr); + + argv[0] = ctor; + argv[1] = superCtor; + napi_call_function(env, global, setProto, 2, argv, nullptr); + + ctor.Set("super_", superCtor); \ +} + + +typedef Napi::Value (*StaticMethodCallback)(const Napi::CallbackInfo& info); +typedef Napi::Value (*StaticGetterCallback)(const Napi::CallbackInfo& info); +typedef void (*StaticSetterCallback)(const Napi::CallbackInfo& info); + + +#define DECLARE_ES5_CLASS(CLASS, NAME) \ + static Napi::FunctionReference _ctorEs5; \ + static const char *_nameEs5; \ + static void CLASS::_finalizeEs5(napi_env e, void *dest, void* hint); \ + static napi_value CLASS::_createEs5(napi_env e, napi_callback_info i); \ + inline void super( \ + const Napi::CallbackInfo& info, \ + int argc = 0, \ + const Napi::Value *argv = nullptr \ + ) { \ + Napi::Function ctor = _ctorEs5.Value(); \ + if (ctor.Has("super_")) { \ + Napi::Function _super = ctor.Get("super_").As(); \ + std::vector args; \ + for (int i = 0; i < argc; i++) { \ + args.push_back(argv[i]); \ + } \ + _super.Call(info.This(), args); \ + } \ + }; \ + inline static Napi::Function wrap(Napi::Env env) { \ + napi_value __initResult; \ + napi_create_function(env, #NAME, 0, CLASS::_createEs5, nullptr, &__initResult); \ + Napi::Function ctor = Napi::Function(env, __initResult); \ + _ctorEs5 = Napi::Persistent(ctor); \ + _ctorEs5.SuppressDestruct(); \ + return ctor; \ + }; \ + inline static Napi::Function wrap( \ + Napi::Env env, \ + Napi::Function superCtor \ + ) { \ + Napi::Function ctor = wrap(env); \ + inheritEs5(env, ctor, superCtor); \ + return ctor; \ + }; \ + inline static void method( \ + const char *name, \ + StaticMethodCallback cb \ + ) { \ + Napi::Function proto = _ctorEs5.Value().Get("prototype").As(); \ + proto.DefineProperty( \ + Napi::PropertyDescriptor::Function(proto.Env(), proto, name, cb) \ + ); \ + }; \ + inline static void accessorR( \ + const char *name, \ + StaticGetterCallback getter \ + ) { \ + Napi::Function proto = _ctorEs5.Value().Get("prototype").As(); \ + proto.DefineProperty( \ + Napi::PropertyDescriptor::Accessor(proto.Env(), proto, name, getter) \ + ); \ + }; \ + inline static void accessorRw( \ + const char *name, \ + StaticGetterCallback getter, \ + StaticSetterCallback setter \ + ) { \ + Napi::Function proto = _ctorEs5.Value().Get("prototype").As(); \ + proto.DefineProperty( \ + Napi::PropertyDescriptor::Accessor( \ + proto.Env(), \ + proto, \ + name, \ + getter, \ + setter \ + ) \ + ); \ + }; + +#define JS_GET_THAT(CLASS) \ + CLASS *that; \ + Napi::Object thatObj = info.This().As(); \ + napi_unwrap(info.Env(), thatObj.Get(_nameEs5), reinterpret_cast(&that)); + +#define JS_DECLARE_METHOD(CLASS, NAME) \ + inline static Napi::Value __st_##NAME(const Napi::CallbackInfo &info) { \ + JS_GET_THAT(CLASS); \ + return that->__i_##NAME(info); \ + }; \ + Napi::Value __i_##NAME(const Napi::CallbackInfo &info); + +#define JS_DECLARE_GETTER(CLASS, NAME) JS_DECLARE_METHOD(CLASS, NAME##Getter) + +#define JS_DECLARE_SETTER(CLASS, NAME) \ + inline static void __st_##NAME##Setter( \ + const Napi::CallbackInfo &info \ + ) { \ + JS_GET_THAT(CLASS); \ + that->__i_##NAME(info, info[0]); \ + }; \ + void __i_##NAME##Setter(const Napi::CallbackInfo &info, const Napi::Value &value); + +#define JS_IMPLEMENT_METHOD(CLASS, NAME) \ + Napi::Value CLASS::__i_##NAME(const Napi::CallbackInfo &info) + +#define JS_IMPLEMENT_GETTER(CLASS, NAME) JS_IMPLEMENT_METHOD(CLASS, NAME##Getter) + +#define JS_IMPLEMENT_SETTER(CLASS, NAME) \ + void CLASS::__i_##NAME##Setter( \ + const Napi::CallbackInfo &info, \ + const Napi::Value &value \ + ) + +#define JS_ASSIGN_METHOD(NAME) method(#NAME, __st_##NAME) +#define JS_ASSIGN_GETTER(NAME) accessorR(#NAME, __st_##NAME##Getter) +#define JS_ASSIGN_SETTER(NAME) accessorRw(#NAME, __st_##NAME##Getter, __st_##NAME##Setter) + +#define IMPLEMENT_ES5_CLASS(CLASS) \ + Napi::FunctionReference CLASS::_ctorEs5; \ + const char *CLASS::_nameEs5 = "CLASS##__TIMESTAMP__"; \ + void CLASS::_finalizeEs5(napi_env e, void *dest, void* hint) { \ + CLASS *instance = reinterpret_cast(dest); \ + delete instance; \ + } \ + napi_value CLASS::_createEs5(napi_env env, napi_callback_info i) { \ + Napi::CallbackInfo info(env, i); \ + CLASS *instance = new CLASS(info); \ + Napi::Object wrapObj = Napi::Object::New(env); \ + info.This().As().Set(_nameEs5, wrapObj); \ + napi_wrap(env, wrapObj, instance, CLASS::_finalizeEs5, nullptr, nullptr); \ + return info.Env().Undefined(); \ + } + #endif // _ADDON_TOOLS_HPP_