Repository: nodejs/node-addon-api

Issue #1000: [BUG] node REPL crashes creating ObjectWrap in getter
Created: 2021-05-14T06:49:37Z
The node REPL seems to crash if a C++ getter creates an instance of a class that extends ObjectWrap.
This works in a script, but it crashes as soon as the REPL's auto-complete attempts to "preview" bar.get_new_foo:
#include <napi.h>

struct Foo : public Napi::ObjectWrap<Foo> {
  static Napi::Function Init(Napi::Env env, Napi::Object exports) {
    return DefineClass(env, "Foo", {});
  }
  Foo(Napi::CallbackInfo const& info) : Napi::ObjectWrap<Foo>(info){};
};

struct Bar : public Napi::ObjectWrap<Bar> {
  static Napi::Function Init(Napi::Env env, Napi::Object exports) {
    FooConstructor = Napi::Persistent(Foo::Init(env, exports));
    FooConstructor.SuppressDestruct();
    return DefineClass(env, "Bar", {InstanceAccessor<&Bar::get_new_foo>("get_new_foo")});
  }

  Bar(Napi::CallbackInfo const& info) : Napi::ObjectWrap<Bar>(info){};

 private:
  static Napi::FunctionReference FooConstructor;
  Napi::Value get_new_foo(Napi::CallbackInfo const& info) {
    return FooConstructor.New({});
  }
};

Napi::FunctionReference Bar::FooConstructor;

struct NodeREPLCrashRepro : public Napi::Addon<NodeREPLCrashRepro> {
  NodeREPLCrashRepro(Napi::Env env, Napi::Object exports) {
    DefineAddon(exports, {InstanceValue("Bar", Bar::Init(env, exports))});
  }
};

NODE_API_ADDON(NodeREPLCrashRepro);
$ node
Welcome to Node.js v15.14.0.
Type ".help" for more information.
> var { Bar } = require(`./build/Debug/crash_repro.node`)
undefined
> var bar = new Bar()
undefined
> bar.gFATAL ERROR: Error::Error napi_create_reference
 1: 0xa89e60 node::Abort() [node]
 2: 0x9ade29 node::FatalError(char const*, char const*) [node]
 3: 0x9ade32  [node]
 4: 0xa55b3b napi_fatal_error [node]
 5: 0x7f3bf031ea4e Napi::ObjectReference::~ObjectReference() [build/Debug/crash_repro.node]
 6: 0x7f3bf031eb14 Napi::Error::Error(napi_env__*, napi_value__*) [build/Debug/crash_repro.node]
 7: 0x7f3bf031e97f Napi::Error::New(napi_env__*) [build/Debug/crash_repro.node]
 8: 0x7f3bf031e758 Napi::Function::New(unsigned long, napi_value__* const*) const [build/Debug/crash_repro.node]
 9: 0x7f3bf031e6d9 Napi::Function::New(std::initializer_list<napi_value__*> const&) const [build/Debug/crash_repro.node]
10: 0x7f3bf031f3a8 Napi::FunctionReference::New(std::initializer_list<napi_value__*> const&) const [build/Debug/crash_repro.node]
11: 0x7f3bf032050d Bar::get_new_foo(Napi::CallbackInfo const&) [build/Debug/crash_repro.node]
12: 0x7f3bf031db65  [build/Debug/crash_repro.node]
13: 0x7f3bf031dc39  [build/Debug/crash_repro.node]
14: 0x7f3bf031dbfd  [build/Debug/crash_repro.node]
15: 0xa390bf  [node]
16: 0x149e62d  [node]
Aborted

Duplicate issue #1239: Unable to process the null exceptions of the Node.js 14.x REPL 
Created: 2022-11-23T16:44:40Z
FATAL ERROR: Error::Error napi_define_properties
 1: 0xa3ac30 node::Abort() [node]
 2: 0x970199 node::FatalError(char const*, char const*) [node]
 3: 0x9701a2  [node]
 4: 0xa07f1b napi_fatal_error [node]

This part of the code 
  
    
      node-addon-api/napi-inl.h
    
    
         Line 2613
      in
      e9db2ad
    
  
  
    

        
          
           if (status != napi_ok) { 
        
    
  

 can cause a crash in Node.js 14.x in REPL - I was not able to reproduce in a later version and maybe it does not happen.
The type-ahead displaying in REPL has some very special peculiarities and constructors can fail - I suppose because it uses an eval context without globals - which it can detect, catch and ignore. In particular, a constructor can throw a null value. In this case, node-addon-api  will be unable to create an Error object - it will fail with the above error.
I won't bother with a minimal reproduction, since it probably won't be fixed anyway, but if you want to see it:
nvm use 14.x
git clone https://github.com/mmomtchev/pymport
npm install
node-pre-gyp configure --debug
node-pre-gyp build
node
> const { PyObject } = require('.');
const a = PyObject.fromJS(4);
> a.co     // Start typing a.constr and it will crash, if you copy&paste a.constr everything will be ok

The crash happens when calling FunctionReference::New() - napi_new_instance throws null  and the wrapping of the error object  in the code cited above fails.
If you short-circuit the error object wrapping, the Node.js REPL correctly catches the null exception and continues without error.
Bypassing node-addon-api and manually re-implementing FunctionReference::New() also fixes the issue.

Closing comment (2022-11-24T21:00:01Z): I believe this was related to #1000 that was specific to Node.js 14 (fixed in 16 and later).
I am not too certain of the mechanism behind this but it was an issue with V8 IIRC. We attempted to backport that fix into the Node14 line but there was quite a bit of change required so we've abandoned the fix.