Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error using ObjectReference to ObjectWrap in node console #873

Closed
trxcllnt opened this issue Jan 5, 2021 · 1 comment
Closed

Error using ObjectReference to ObjectWrap in node console #873

trxcllnt opened this issue Jan 5, 2021 · 1 comment
Labels

Comments

@trxcllnt
Copy link

trxcllnt commented Jan 5, 2021

I have a native module that's throwing an error, but only when running in the node console, not in a script. This makes me think I have a C++ instance lifetime issue, but I can't see anything obvious, and similar code in other places is behaving fine.

I've boiled the issue down to this small example. A C++ ObjectWrap instantiates and holds a reference to another ObjectWrap instance. Later, the outer ObjectWrap instance delegates various behavior to the inner instance.

Here's the example in JS:

class Child { get value() { return 5; } }
class Parent {
  constructor() { this.child = new Child(); }
  get value() { return this.child.value; }
}
let p =  new Parent();
p.value // 5

Below is my attempt to replicate the above in C++, but I'm seeing this error in the node console. It looks like there's an initial error being thrown, but then another error constructing the Napi::Error, leading to the abort:

$ node
Welcome to Node.js v15.4.0.
Type ".help" for more information.
> const { Parent } = require('./build/addon.node');
undefined
> const p = new Parent()
undefined
> p.child.value
5
> p.valueFATAL ERROR: Error::Error napi_create_reference
 1: 0xa68ab0 node::Abort() [node]
 2: 0x99bc57 node::FatalError(char const*, char const*) [node]
 3: 0x99bc60  [node]
 4: 0xa3933b napi_fatal_error [node]
 5: 0x7f6165498d44 Napi::Error::Error(napi_env__*, napi_value__*) [./build/addon.node]
 6: 0x7f6165498dea Napi::Error::Error(napi_env__*, napi_value__*) [./build/addon.node]
 7: 0x7f6165498c75 Napi::Error::New(napi_env__*) [./build/addon.node]
 8: 0x7f616549832f Napi::Object::Get(char const*) const [./build/addon.node]
 9: 0x7f616549933a Napi::ObjectReference::Get(char const*) const [./build/addon.node]
10: 0x7f616549b7b1 Parent::get_value(Napi::CallbackInfo const&) [./build/addon.node]
11: 0x7f616548fd6f  [./build/addon.node]
12: 0x7f616548ffb5  [./build/addon.node]
13: 0x7f616548fe07  [./build/addon.node]
14: 0xa1c5bf  [node]
15: 0x1469a4d  [node]
Aborted

But if I run it in a script, I don't get any errors:

// test.js
const { Parent } = require('./build/addon.node');
const p = new Parent();
console.log('p.value:', p.value);
console.log('p.child.value:', p.child.value);
// Give the GC time to sweep before accessing again
setTimeout(() => console.log('p.value:', p.value), 1000);
/*
$ node test.js
p.value: 5
p.child.value: 5
p.value: 5
*/

The C++:

#include <napi.h>

struct Child : public Napi::ObjectWrap<Child> {
  static Napi::FunctionReference constructor;
  static auto Init(Napi::Env env, Napi::Object exports) {
    exports.Set("Child", [&]() {
      (Child::constructor = Napi::Persistent(DefineClass(
         env, "Child", {InstanceAccessor<&Child::get_value>("value", napi_enumerable)})))
        .SuppressDestruct();
      return Child::constructor.Value();
    }());
    return exports;
  }
  Child(Napi::CallbackInfo const& info) : Napi::ObjectWrap<Child>(info){};
  Napi::Value get_value(Napi::CallbackInfo const& info) { return Napi::Number::New(info.Env(), 5); }
};

Napi::FunctionReference Child::constructor;

struct Parent : public Napi::ObjectWrap<Parent> {
  static Napi::FunctionReference constructor;
  static auto Init(Napi::Env env, Napi::Object exports) {
    exports.Set("Parent", [&]() {
      (Parent::constructor = Napi::Persistent(
         DefineClass(env,
                     "Parent",
                     {InstanceAccessor<&Parent::get_child>("child", napi_enumerable),
                      InstanceAccessor<&Parent::get_value>("value", napi_enumerable)})))
        .SuppressDestruct();
      return Parent::constructor.Value();
    }());
    return exports;
  }
  Parent(Napi::CallbackInfo const& info)
    : Napi::ObjectWrap<Parent>(info),  //
      child(Napi::Persistent(Child::constructor.New({}))){};
  Napi::Value get_child(Napi::CallbackInfo const& info) { return child.Value(); }
  Napi::Value get_value(Napi::CallbackInfo const& info) {
    // These blow up in the node console:
    // child.Value().InstanceOf(Child::constructor.Value());
    // return child.Value().Get("value");
    return child.Get("value");
    // This doesn't blow up in the node console:
    // return Child::Unwrap(child.Value())->get_value(info);
  }
  Napi::ObjectReference child;
};

Napi::FunctionReference Parent::constructor;

Napi::Object init(Napi::Env env, Napi::Object exports) {
  Child::Init(env, exports);
  Parent::Init(env, exports);
  return exports;
}

NODE_API_MODULE(addon, init);

Any ideas what could be going wrong here?

@trxcllnt trxcllnt changed the title Error using ObjectWrap ObjectReference in node console Error using ObjectReference to ObjectWrap in node console Jan 5, 2021
@github-actions
Copy link
Contributor

github-actions bot commented Apr 6, 2021

This issue is stale because it has been open many days with no activity. It will be closed soon unless the stale label is removed or a comment is made.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant