从 NodeJS Addon 构造本机对象

Constructing native object from NodeJS Addon

预期

我想使用像下面的 javascript 模块一样工作的本机 NodeJS 模块来实现

class C1{
  make(){return new C2()}
}
class C2{}
module.exports({C1, C2})

方法

我的尝试是,(在其他一些失败的尝试中)

// so.cpp
#include <napi.h>

class C1: public Napi::ObjectWrap<C1> {
public:
  C1(const Napi::CallbackInfo &info);
  static void Initialize(Napi::Env& env, Napi::Object& target);
  Napi::Value make(const Napi::CallbackInfo &info);
};

class C2: public Napi::ObjectWrap<C2> {
  static Napi::Function constructor;
  public:
  C2(const Napi::CallbackInfo &info);
  static void Initialize(Napi::Env& env, Napi::Object& target);
  static C2 *New(const std::initializer_list<napi_value>& args);
};
Napi::Function C2::constructor;
void C2::Initialize(Napi::Env& env, Napi::Object& target){
  constructor = Napi::ObjectWrap<C2>::DefineClass(env, "C2", {});
  target.Set("C2", constructor);
}
void C1::Initialize(Napi::Env& env, Napi::Object& target) {
  Napi::Function constructor = Napi::ObjectWrap<C1>::DefineClass(env, "C1", {
    Napi::ObjectWrap<C1>::InstanceMethod("make", &C1::make),
  });
  target.Set("C1", constructor);
}

C2 *C2::New(const std::initializer_list<napi_value>& args){
  return Napi::ObjectWrap<C2>::Unwrap(constructor.New(args));
}
C2::C2(const Napi::CallbackInfo &info): Napi::ObjectWrap<C2>(info) {}
C1::C1(const Napi::CallbackInfo &info): Napi::ObjectWrap<C1>(info) {}

// make() {return new C2()}
Napi::Value C1::make(const Napi::CallbackInfo &info){
  C2 *jsclat = C2::New({});
  return jsclat->Value();
}


// module.exports = {C1, C2}
Napi::Object Init(Napi::Env env, Napi::Object exports)
{
  C1::Initialize(env, exports);
  C2::Initialize(env, exports);
  return exports;
}
NODE_API_MODULE(so, Init);

问题

当我尝试使用来自 javascript 的已编译模块时,节点已终止

const m = require('bindings')('hello')
const c1 = new m.c1()
c1.make() // FATAL ERROR: Error::New napi_get_last_error_info
          // Aborted

环境

如果你想要和我一样的环境,这里有一个dockerfile。

FROM node:13.8.0-alpine3.11
RUN apk add --upgrade git python3 g++ make
WORKDIR /opt
RUN git clone https://github.com/nodejs/node-addon-examples.git
WORKDIR /opt/node-addon-examples/1_hello_world/node-addon-api/
RUN npm install \
 && npm install --save-dev gyp
 && npx node-gyp configure 
COPY so.cpp hello.cc
RUN npx node-gyp rebuild 

您不能在创建它的函数之外使用 constructor - 它是在堆栈上分配的自动 v8::Local 引用 - 您需要一个持久引用来执行此操作:

#include <napi.h>

class C1: public Napi::ObjectWrap<C1> {
public:
  C1(const Napi::CallbackInfo &info);
  static void Initialize(Napi::Env &env, Napi::Object &target);
  Napi::Value make(const Napi::CallbackInfo &info);
};

class C2: public Napi::ObjectWrap<C2> {
  static Napi::Function constructor;
  public:
  static Napi::FunctionReference *New;
  C2(const Napi::CallbackInfo &info);
  static void Initialize(Napi::Env& env, Napi::Object& target);
};

Napi::FunctionReference *C2::New;

void C2::Initialize(Napi::Env& env, Napi::Object& target){
  Napi::Function constructor = Napi::ObjectWrap<C2>::DefineClass(env, "C2", {});
  target.Set("C2", constructor);
  New = new Napi::FunctionReference();
  *New = Napi::Persistent(constructor);
}
void C1::Initialize(Napi::Env& env, Napi::Object& target) {
  Napi::Function constructor = Napi::ObjectWrap<C1>::DefineClass(env, "C1", {
    Napi::ObjectWrap<C1>::InstanceMethod("make", &C1::make),
  });
  target.Set("C1", constructor);
}

C2::C2(const Napi::CallbackInfo &info): Napi::ObjectWrap<C2>(info) {}
C1::C1(const Napi::CallbackInfo &info): Napi::ObjectWrap<C1>(info) {}

// make() {return new C2()}
Napi::Value C1::make(const Napi::CallbackInfo &info){
  Napi::Value c2 = C2::New->New({});
  return c2;
}


// module.exports = {C1, C2}
Napi::Object Init(Napi::Env env, Napi::Object exports)
{
  C1::Initialize(env, exports);
  C2::Initialize(env, exports);
  return exports;
}
NODE_API_MODULE(so, Init);