Skip to content

Commit

Permalink
use napi_ref instead of a new type
Browse files Browse the repository at this point in the history
  • Loading branch information
vmoroz committed Apr 6, 2022
1 parent d63ae6e commit 516e36e
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 138 deletions.
59 changes: 30 additions & 29 deletions src/js_native_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -315,34 +315,54 @@ NAPI_EXTERN napi_status napi_get_value_external(napi_env env,

// Methods to control object lifespan

#ifdef NAPI_EXPERIMENTAL
// For node_api_reftype_complex set initial_refcount to 0 for a weak reference,
// >0 for a strong reference.
// The node_api_reftype_persistent ignores the initial_refcount and always
// uses 1.
NAPI_EXTERN napi_status node_api_create_reference(napi_env env,
napi_value value,
node_api_reftype reftype,
uint32_t initial_refcount,
napi_ref* result);
// Get type of the reference.
NAPI_EXTERN napi_status node_api_get_reference_type(napi_env env,
napi_ref ref,
node_api_reftype* result);
#endif

// Create node_api_reftype_complex reference.
// Set initial_refcount to 0 for a weak reference, >0 for a strong reference.
NAPI_EXTERN napi_status napi_create_reference(napi_env env,
napi_value value,
uint32_t initial_refcount,
napi_ref* result);

// Deletes a reference. The referenced value is released, and may
// be GC'd unless there are other references to it.
// Delete a node_api_reftype_complex reference. The referenced value is
// released, and may be GC'd unless there are other references to it.
// This method cannot be used with node_api_reftype_persistent references.
NAPI_EXTERN napi_status napi_delete_reference(napi_env env, napi_ref ref);

// Increments the reference count, optionally returning the resulting count.
// Increment the reference count, optionally returning the resulting count.
// After this call the reference will be a strong reference because its
// refcount is >0, and the referenced object is effectively "pinned".
// Calling this when the refcount is 0 and the object is unavailable
// results in an error.
NAPI_EXTERN napi_status napi_reference_ref(napi_env env,
napi_ref ref,
uint32_t* result);

// Decrements the reference count, optionally returning the resulting count.
// If the result is 0 the reference is now weak and the object may be GC'd
// at any time if there are no other references. Calling this when the
// refcount is already 0 results in an error.
// Decrement the reference count, optionally returning the resulting count.
// For node_api_reftype_complex references if the result is 0, then the
// reference is now weak and the object may be GC'd at any time if there are no
// other references.
// For node_api_reftype_persistent references if the result is 0, then the
// reference is deleted and the stored napi_value may be GC'd at any time if
// there are no other references.
// Calling this when the refcount is already 0 results in an error.
NAPI_EXTERN napi_status napi_reference_unref(napi_env env,
napi_ref ref,
uint32_t* result);

// Attempts to get a referenced value. If the reference is weak,
// Attempt to get a referenced value. If the reference is weak,
// the value might no longer be available, in that case the call
// is still successful but the result is NULL.
NAPI_EXTERN napi_status napi_get_reference_value(napi_env env,
Expand Down Expand Up @@ -562,25 +582,6 @@ NAPI_EXTERN napi_status napi_object_freeze(napi_env env, napi_value object);
NAPI_EXTERN napi_status napi_object_seal(napi_env env, napi_value object);
#endif // NAPI_VERSION >= 8

#ifdef NAPI_EXPERIMENTAL

NAPI_EXTERN napi_status napi_create_persistent(napi_env env,
napi_value value,
napi_persistent* result);

NAPI_EXTERN napi_status napi_get_persistent_value(napi_env env,
napi_persistent persistent,
napi_value* result);

NAPI_EXTERN napi_status napi_persistent_ref(napi_env env,
napi_persistent persistent);

NAPI_EXTERN napi_status napi_persistent_unref(napi_env env,
napi_persistent persistent,
bool* is_deleted);

#endif

EXTERN_C_END

#endif // SRC_JS_NATIVE_API_H_
7 changes: 7 additions & 0 deletions src/js_native_api_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,11 @@ typedef struct {
} napi_type_tag;
#endif // NAPI_VERSION >= 8

#ifdef NAPI_EXPERIMENTAL
typedef enum {
node_api_reftype_complex,
node_api_reftype_persistent,
} node_api_reftype;
#endif

#endif // SRC_JS_NATIVE_API_TYPES_H_
163 changes: 85 additions & 78 deletions src/js_native_api_v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,14 @@ class CallbackBundle {
bundle->env = env;

v8::Local<v8::Value> cbdata = v8::External::New(env->isolate, bundle);
Reference::New(env, cbdata, 0, true, Delete, bundle, nullptr);
Reference::New(env,
cbdata,
node_api_reftype_complex,
0,
true,
Delete,
bundle,
nullptr);
return cbdata;
}
napi_env env; // Necessary to invoke C++ NAPI callback
Expand Down Expand Up @@ -431,14 +438,21 @@ inline napi_status Wrap(napi_env env,
// before then, then the finalize callback will never be invoked.)
// Therefore a finalize callback is required when returning a reference.
CHECK_ARG(env, finalize_cb);
reference = v8impl::Reference::New(
env, obj, 0, false, finalize_cb, native_object, finalize_hint);
reference = v8impl::Reference::New(env,
obj,
node_api_reftype_complex,
0,
false,
finalize_cb,
native_object,
finalize_hint);
*result = reinterpret_cast<napi_ref>(reference);
} else {
// Create a self-deleting reference.
reference = v8impl::Reference::New(
env,
obj,
node_api_reftype_complex,
0,
true,
finalize_cb,
Expand Down Expand Up @@ -574,25 +588,34 @@ void RefBase::Finalize(bool is_env_teardown) {
}

template <typename... Args>
Reference::Reference(napi_env env, v8::Local<v8::Value> value, Args&&... args)
: RefBase(env, std::forward<Args>(args)...),
Reference::Reference(napi_env env,
v8::Local<v8::Value> value,
node_api_reftype reftype,
uint32_t initial_refcount,
Args&&... args)
: RefBase(env,
(reftype == node_api_reftype_complex) ? initial_refcount : 1,
std::forward<Args>(args)...),
_persistent(env->isolate, value),
_secondPassParameter(new SecondPassCallParameterRef(this)),
_secondPassScheduled(false) {
_secondPassScheduled(false),
_refType(reftype) {
if (RefCount() == 0) {
SetWeak();
}
}

Reference* Reference::New(napi_env env,
v8::Local<v8::Value> value,
node_api_reftype reftype,
uint32_t initial_refcount,
bool delete_self,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint) {
return new Reference(env,
value,
reftype,
initial_refcount,
delete_self,
finalize_callback,
Expand Down Expand Up @@ -621,7 +644,11 @@ uint32_t Reference::Unref() {
uint32_t old_refcount = RefCount();
uint32_t refcount = RefBase::Unref();
if (old_refcount == 1 && refcount == 0) {
SetWeak();
if (_refType == node_api_reftype_complex) {
SetWeak();
} else {
Delete(this);
}
}
return refcount;
}
Expand All @@ -634,6 +661,10 @@ v8::Local<v8::Value> Reference::Get() {
}
}

node_api_reftype Reference::RefType() noexcept {
return _refType;
}

void Reference::Finalize(bool is_env_teardown) {
// During env teardown, `~napi_env()` alone is responsible for finalizing.
// Thus, we don't want any stray gc passes to trigger a second call to
Expand Down Expand Up @@ -2368,8 +2399,14 @@ napi_status napi_create_external(napi_env env,

// The Reference object will delete itself after invoking the finalizer
// callback.
v8impl::Reference::New(
env, external_value, 0, true, finalize_cb, data, finalize_hint);
v8impl::Reference::New(env,
external_value,
node_api_reftype_complex,
0,
true,
finalize_cb,
data,
finalize_hint);

*result = v8impl::JsValueFromV8LocalValue(external_value);

Expand Down Expand Up @@ -2458,25 +2495,52 @@ napi_status napi_create_reference(napi_env env,
napi_value value,
uint32_t initial_refcount,
napi_ref* result) {
return node_api_create_reference(
env, value, node_api_reftype_complex, initial_refcount, result);
}

NAPI_EXTERN napi_status node_api_create_reference(napi_env env,
napi_value value,
node_api_reftype reftype,
uint32_t initial_refcount,
napi_ref* result) {
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
// JS exceptions.
CHECK_ENV(env);
CHECK_ARG(env, value);
CHECK_ARG(env, result);

v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(value);
if (!(v8_value->IsObject() || v8_value->IsFunction() ||
v8_value->IsSymbol())) {
return napi_set_last_error(env, napi_invalid_arg);
if (reftype == node_api_reftype_complex) {
if (!(v8_value->IsObject() || v8_value->IsFunction() ||
v8_value->IsSymbol())) {
return napi_set_last_error(env, napi_invalid_arg);
}
}

v8impl::Reference* reference =
v8impl::Reference::New(env, v8_value, initial_refcount, false);
v8impl::Reference* reference = v8impl::Reference::New(
env,
v8_value,
reftype,
initial_refcount,
/* delete_self: */ reftype == node_api_reftype_persistent);

*result = reinterpret_cast<napi_ref>(reference);
return napi_clear_last_error(env);
}

NAPI_EXTERN napi_status node_api_get_reference_type(napi_env env,
napi_ref ref,
node_api_reftype* result) {
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
// JS exceptions.
CHECK_ENV(env);
CHECK_ARG(env, ref);
CHECK_ARG(env, result);

*result = reinterpret_cast<v8impl::Reference*>(ref)->RefType();
return napi_clear_last_error(env);
}

// Deletes a reference. The referenced value is released, and may be GC'd unless
// there are other references to it.
napi_status napi_delete_reference(napi_env env, napi_ref ref) {
Expand All @@ -2485,7 +2549,12 @@ napi_status napi_delete_reference(napi_env env, napi_ref ref) {
CHECK_ENV(env);
CHECK_ARG(env, ref);

v8impl::Reference::Delete(reinterpret_cast<v8impl::Reference*>(ref));
v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
if (reference->RefType() == node_api_reftype_complex) {
v8impl::Reference::Delete(reference);
} else {
return napi_set_last_error(env, napi_generic_failure);
}

return napi_clear_last_error(env);
}
Expand Down Expand Up @@ -3210,65 +3279,3 @@ napi_status napi_is_detached_arraybuffer(napi_env env,

return napi_clear_last_error(env);
}

struct napi_persistent__ {
v8impl::Persistent<v8::Value> persistent_value;
uint32_t ref_count;
};

NAPI_EXTERN napi_status napi_create_persistent(napi_env env,
napi_value value,
napi_persistent* result) {
CHECK_ENV(env);
CHECK_ARG(env, value);
CHECK_ARG(env, result);

*result = new napi_persistent__{
v8impl::Persistent<v8::Value>(env->isolate,
v8impl::V8LocalValueFromJsValue(value)),
1};

return napi_clear_last_error(env);
}

NAPI_EXTERN napi_status napi_get_persistent_value(napi_env env,
napi_persistent persistent,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, persistent);
CHECK_ARG(env, result);

*result = v8impl::JsValueFromV8LocalValue(
v8::Local<v8::Value>::New(env->isolate, persistent->persistent_value));

return napi_clear_last_error(env);
}

NAPI_EXTERN napi_status napi_persistent_ref(napi_env env,
napi_persistent persistent) {
CHECK_ENV(env);
CHECK_ARG(env, persistent);

++persistent->ref_count;

return napi_clear_last_error(env);
}

NAPI_EXTERN napi_status napi_persistent_unref(napi_env env,
napi_persistent persistent,
bool* is_deleted) {
CHECK_ENV(env);
CHECK_ARG(env, persistent);

bool persistent_is_deleted = false;
if (--persistent->ref_count == 0) {
delete persistent;
persistent_is_deleted = true;
}

if (is_deleted != nullptr) {
*is_deleted = persistent_is_deleted;
}

return napi_clear_last_error(env);
}
Loading

0 comments on commit 516e36e

Please sign in to comment.