Skip to content

Commit a5e4e04

Browse files
author
Gabriel Schulhof
committed
Update DefineClass comment
1 parent c762ae0 commit a5e4e04

File tree

1 file changed

+47
-1
lines changed

1 file changed

+47
-1
lines changed

napi-inl.h

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2823,6 +2823,15 @@ ObjectWrap<T>::DefineClass(Napi::Env env,
28232823
const napi_property_descriptor* props,
28242824
void* data) {
28252825
napi_status status;
2826+
2827+
// Before defining the class we can replace static method property descriptors
2828+
// with value property descriptors such that the value is a function-valued
2829+
// `napi_value` created with `CreateFunction()`. Note that we have to break
2830+
// constness to do this.
2831+
//
2832+
// This replacement could be made for instance methods as well, but V8 aborts
2833+
// if we do that, because it expects methods defined on the prototype template
2834+
// to have `FunctionTemplate`s.
28262835
for (size_t index = 0; index < props_count; index++) {
28272836
napi_property_descriptor* prop =
28282837
const_cast<napi_property_descriptor*>(&props[index]);
@@ -2858,6 +2867,41 @@ ObjectWrap<T>::DefineClass(Napi::Env env,
28582867
&value);
28592868
NAPI_THROW_IF_FAILED(env, status, Function());
28602869

2870+
// After defining the class we iterate once more over the property descriptors
2871+
// and attach the data associated with accessors and instance methods to the
2872+
// newly created JavaScript class.
2873+
//
2874+
// TODO: On some engines it might be possible to detach instance methods from
2875+
// the prototype. This would mean that the detached prototype method would
2876+
// become a plain function and so its data should be associated with itself
2877+
// rather than with the class. If this is the case then in this loop, and for
2878+
// prototype methods, we should retrieve the `napi_value` representing the
2879+
// resulting function and attach the data to *it* rather than the class.
2880+
//
2881+
// IOW if in JavaScript one does this,
2882+
//
2883+
// let MyClass = binding.defineMyClass();
2884+
// const someMethod = MyClass.prototype.someMethod;
2885+
// delete MyClass;
2886+
// someMethod();
2887+
//
2888+
// then the class, onto which we attached the data, would be gone, and the
2889+
// data that will be passed to the native implementation of `someMethod()`
2890+
// would be stale and would cause a segfault if accessed, and so in the
2891+
// following loop, we may have to
2892+
//
2893+
// napi_get_named_property(env, value, "prototype", &proto);
2894+
// once at the top, and then, for each property, if it is an instance method,
2895+
// napi_get_named_property(env, value, prop->utf8name, &proto_method);
2896+
// or
2897+
// napi_get_property(env, prop->name, &proto_method);
2898+
// and then
2899+
// Napi::details::AttachData(env,
2900+
// proto_method,
2901+
// static_cast<...*>(prop->data));
2902+
//
2903+
// instead of attaching the data to the class. The downside is that all this
2904+
// retrieving of prototype properties would be very expensive.
28612905
for (size_t idx = 0; idx < props_count; idx++) {
28622906
const napi_property_descriptor* prop = &props[idx];
28632907

@@ -2872,17 +2916,19 @@ ObjectWrap<T>::DefineClass(Napi::Env env,
28722916
status = Napi::details::AttachData(env,
28732917
value,
28742918
static_cast<InstanceAccessorCallbackData*>(prop->data));
2919+
NAPI_THROW_IF_FAILED(env, status, Function());
28752920
} else if (prop->method != nullptr && !(prop->attributes & napi_static)) {
28762921
if (prop->method == T::InstanceVoidMethodCallbackWrapper) {
28772922
status = Napi::details::AttachData(env,
28782923
value,
28792924
static_cast<InstanceVoidMethodCallbackData*>(prop->data));
2925+
NAPI_THROW_IF_FAILED(env, status, Function());
28802926
} else if (prop->method == T::InstanceMethodCallbackWrapper) {
28812927
status = Napi::details::AttachData(env,
28822928
value,
28832929
static_cast<InstanceMethodCallbackData*>(prop->data));
2930+
NAPI_THROW_IF_FAILED(env, status, Function());
28842931
}
2885-
NAPI_THROW_IF_FAILED(env, status, Function());
28862932
}
28872933
}
28882934

0 commit comments

Comments
 (0)