Node-addon-api: v8::FunctionTemplate inherit to Napi

Created on 23 Apr 2018  路  17Comments  路  Source: nodejs/node-addon-api

Can we implement v8::FunctionTemplate::Inherit in Napi ?

Most helpful comment

@iclosure I created an example using plain C N-API.

All 17 comments

FunctionTemplate is a V8-specific concept, whereas we're trying to keep N-API VM-agnostic. The behaviour of Inherit can be achieved with existing APIs.

Hi everyone,
@gabrielschulhof you're right, but I think that this is a duplicate issue, there is a discussion about Inherit here https://github.com/nodejs/node-addon-api/issues/229

But the method is invalid in 'super class' when I call with javascript. How can I fix it? (When I call the super method, then print error message: "TypeError: Illegal invocation")

@iclosure can you show me the code?

1) Super Init:

void BaseObject::Initialize(Napi::Env env, Napi::Object exports)
{
    Napi::HandleScope scope(env);
    std::vector<PropertyDescriptor> properties = {
        InstanceAccessor("objectType", &BaseObject::GetObjectType, nullptr),
        InstanceAccessor("id", &BaseObject::GetId, &BaseObject::SetId),
        InstanceAccessor("domain", &BaseObject::GetDomain, &BaseObject::SetDomain),
        InstanceAccessor("name", &BaseObject::GetName, &BaseObject::SetName),
        InstanceAccessor("mark", &BaseObject::GetMark, &BaseObject::SetMark),
        InstanceAccessor("desc", &BaseObject::GetDesc, &BaseObject::SetDesc),
        InstanceMethod("isPrivateMark", &BaseObject::IsPrivateMark),
        InstanceMethod("childCount", &BaseObject::ChildCount),
        InstanceMethod("objectTypeString", &BaseObject::ObjectTypeString),
        InstanceMethod("resetData", &BaseObject::ResetData),
        InstanceMethod("clearData", &BaseObject::ClearData),
        InstanceMethod("domainOfType", &BaseObject::DomainOfType)
    };
    ctor = napi_init<BaseObject>(env, exports, "Object", properties);
}

2) Sub Class inherit:

template<typename Sub, typename Super>
inline Napi::FunctionReference napi_init(Napi::Env env, Napi::Object exports, const char *name,
                                         const std::vector<Napi::ClassPropertyDescriptor<Sub> > &properties)
{
    Napi::HandleScope scope(env);
    Napi::Function object = Sub::DefineClass(env, name, properties);
    napi_inherits(env, object, Super::ctor.Value());
    exports.Set(name, object);
    Napi::FunctionReference ctor = Napi::Persistent(object);
    ctor.SuppressDestruct();
    return ctor;
}

3) Javascript code:

var vehicle = new proto.Vehicle();
console.log(vehicle.clearData);
vehicle.clearData();

4) napi_inherits():

inline void napi_inherits(napi_env env, napi_value ctor, napi_value super_ctor)
{
    napi_value global, global_object, set_proto, ctor_proto_prop, super_ctor_proto_prop;
    napi_value argv[2];

    napi_get_global(env, &global);
    napi_get_named_property(env, global, "Object", &global_object);
    napi_get_named_property(env, global_object, "setPrototypeOf", &set_proto);
    napi_get_named_property(env, ctor, "prototype", &ctor_proto_prop);
    napi_get_named_property(env, super_ctor, "prototype", &super_ctor_proto_prop);

    argv[0] = ctor_proto_prop;
    argv[1] = super_ctor_proto_prop;
    napi_call_function(env, global, set_proto, 2, argv, nullptr);

    argv[0] = ctor;
    argv[1] = super_ctor;
    napi_call_function(env, global, set_proto, 2, argv, nullptr);
}

@iclosure I created an example using plain C N-API.

Hi @gabrielschulhof
It is helpful for static method and object's member properties to inherit! But the static or non-static member method of super-class could not be inherited by sub-class by using N-Api framework. The example as bellow:
The super-class method:
InstanceMethod("clearData", &SuperObject::ClearData)
I want to call the method "clearData" of super-class on sub-class in javascript, like this:
var obj = new addon.SubObject(); obj.clearData();
Then I got an error message: "TypeError: Illegal invocation".
How can I resolve this issue, please show me an example, thank you!!! 馃槉
I print all properties of SubObject:
image

Exists the "clearData" function in sub-class, but cannot be called. Because of it's a native code? Why?

@iclosure I have reproduced the problem. I'll try and find the reason.

// ES6 notation

class SuperClass {
  constructor() {
    console.log("SuperClass constructor");
  }
  superclassMethod() {
    console.log("SuperClass method");
  }
}

class SubClass extends SuperClass {
  constructor() {
    super();
    console.log("SubClass constructor");
  }
}

const x = new SubClass();
x.superclassMethod();

// Old style

const OldSuperClass = function OldSuperClass() {
  console.log("OldSuperClass constructor");
};

OldSuperClass.prototype.oldsuperclassMethod = function oldsuperclassMethod() {
  console.log("OldSuperClass method");
};

const OldSubClass = function OldSubClass() {
  OldSuperClass.apply(this, arguments);
  console.log("OldSubClass constructor");
};

Object.setPrototypeOf(OldSubClass.prototype, OldSuperClass.prototype);
Object.setPrototypeOf(OldSubClass, OldSuperClass);

const y = new OldSubClass();
y.oldsuperclassMethod();

// Native

const addon = require('./build/Release/inheritance.node');

const z = new addon.Subclass();
z.superclassMethod(); // <-- Throws illegal invocation error

I think there's a bug in napi_define_class, because if I give napi_define_class zero descriptors, and I subsequently

  1. retrieve the "prototype" property of the resulting constructor,
  2. create a function that will become the instance method, and
  3. attach the function to the prototype retrieved in the first step,

then the call from JS gets routed correctly to the native method.

OK, so it's not retrieving the "prototype" property and adding methods to that that fixes the problem, but rather adding methods without a v8::Signature that fixes the problem. So, calling napi_define_properties() on the object returned by retrieving the "prototype" property instead of using napi_define_class() will result in a class that has methods that do not have a v8::Signature. Yet, as mentioned in 08a5b442e42ff67bd5666604eebe69f1b4a1c919, the v8::Signature is there for a good reason.

@gabrielschulhof Has any solution for this? :)

@iclosure there is no easy solution, because, in order to do it properly, we'd need to be able to retrieve the v8::FunctionTemplate from V8 after the fact. Now, some constructors, such as those for built-in objects may not even have a v8::FunctionTemplate.

Either way, implementing this without the support of the V8 folks would result in pretty limited support.

@iclosure with V8 as it is now, we could only support inheritance if both the parent class and the child class were created using N-API.

Get it,Thanks!馃槉

@iclosure ok if we close this issue now?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

blagoev picture blagoev  路  6Comments

cinderblock picture cinderblock  路  3Comments

langxiong picture langxiong  路  5Comments

Wulf picture Wulf  路  5Comments

felipezacani picture felipezacani  路  8Comments