n-api: Reference and external tests
- Add a test project to addons-napi that covers the N-API reference and external APIs - Fix a bug in napi_typeof that was found by the new tests PR-URL: https://github.com/nodejs/node/pull/12551 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
This commit is contained in:
parent
892ce06dbd
commit
427125491f
@ -1464,6 +1464,10 @@ napi_status napi_typeof(napi_env env,
|
||||
// This test has to come before IsObject because IsFunction
|
||||
// implies IsObject
|
||||
*result = napi_function;
|
||||
} else if (v->IsExternal()) {
|
||||
// This test has to come before IsObject because IsExternal
|
||||
// implies IsObject
|
||||
*result = napi_external;
|
||||
} else if (v->IsObject()) {
|
||||
*result = napi_object;
|
||||
} else if (v->IsBoolean()) {
|
||||
@ -1474,8 +1478,6 @@ napi_status napi_typeof(napi_env env,
|
||||
*result = napi_symbol;
|
||||
} else if (v->IsNull()) {
|
||||
*result = napi_null;
|
||||
} else if (v->IsExternal()) {
|
||||
*result = napi_external;
|
||||
} else {
|
||||
// Should not get here unless V8 has added some new kind of value.
|
||||
return napi_set_last_error(env, napi_invalid_arg);
|
||||
|
@ -50,3 +50,6 @@
|
||||
|
||||
#define DECLARE_NAPI_PROPERTY(name, func) \
|
||||
{ (name), 0, (func), 0, 0, 0, napi_default, 0 }
|
||||
|
||||
#define DECLARE_NAPI_GETTER(name, func) \
|
||||
{ (name), 0, 0, (func), 0, 0, napi_default, 0 }
|
||||
|
8
test/addons-napi/test_reference/binding.gyp
Normal file
8
test/addons-napi/test_reference/binding.gyp
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "test_reference",
|
||||
"sources": [ "test_reference.c" ]
|
||||
}
|
||||
]
|
||||
}
|
87
test/addons-napi/test_reference/test.js
Normal file
87
test/addons-napi/test_reference/test.js
Normal file
@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
// Flags: --expose-gc
|
||||
|
||||
const common = require('../../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const test_reference = require(`./build/${common.buildType}/test_reference`);
|
||||
|
||||
// This test script uses external values with finalizer callbacks
|
||||
// in order to track when values get garbage-collected. Each invocation
|
||||
// of a finalizer callback increments the finalizeCount property.
|
||||
assert.strictEqual(test_reference.finalizeCount, 0);
|
||||
|
||||
{
|
||||
// External value without a finalizer
|
||||
let value = test_reference.createExternal();
|
||||
assert.strictEqual(test_reference.finalizeCount, 0);
|
||||
assert.strictEqual(typeof value, 'object');
|
||||
test_reference.checkExternal(value);
|
||||
value = null;
|
||||
global.gc();
|
||||
assert.strictEqual(test_reference.finalizeCount, 0);
|
||||
}
|
||||
|
||||
{
|
||||
// External value with a finalizer
|
||||
let value = test_reference.createExternalWithFinalize();
|
||||
assert.strictEqual(test_reference.finalizeCount, 0);
|
||||
assert.strictEqual(typeof value, 'object');
|
||||
test_reference.checkExternal(value);
|
||||
value = null;
|
||||
global.gc();
|
||||
assert.strictEqual(test_reference.finalizeCount, 1);
|
||||
}
|
||||
|
||||
{
|
||||
// Weak reference
|
||||
let value = test_reference.createExternalWithFinalize();
|
||||
assert.strictEqual(test_reference.finalizeCount, 0);
|
||||
test_reference.createReference(value, 0);
|
||||
assert.strictEqual(test_reference.referenceValue, value);
|
||||
value = null;
|
||||
global.gc(); // Value should be GC'd because there is only a weak ref
|
||||
assert.strictEqual(test_reference.referenceValue, undefined);
|
||||
assert.strictEqual(test_reference.finalizeCount, 1);
|
||||
test_reference.deleteReference();
|
||||
}
|
||||
|
||||
{
|
||||
// Strong reference
|
||||
let value = test_reference.createExternalWithFinalize();
|
||||
assert.strictEqual(test_reference.finalizeCount, 0);
|
||||
test_reference.createReference(value, 1);
|
||||
assert.strictEqual(test_reference.referenceValue, value);
|
||||
value = null;
|
||||
global.gc(); // Value should NOT be GC'd because there is a strong ref
|
||||
assert.strictEqual(test_reference.finalizeCount, 0);
|
||||
test_reference.deleteReference();
|
||||
global.gc(); // Value should be GC'd because the strong ref was deleted
|
||||
assert.strictEqual(test_reference.finalizeCount, 1);
|
||||
}
|
||||
|
||||
{
|
||||
// Strong reference, increment then decrement to weak reference
|
||||
let value = test_reference.createExternalWithFinalize();
|
||||
assert.strictEqual(test_reference.finalizeCount, 0);
|
||||
test_reference.createReference(value, 1);
|
||||
value = null;
|
||||
global.gc(); // Value should NOT be GC'd because there is a strong ref
|
||||
assert.strictEqual(test_reference.finalizeCount, 0);
|
||||
|
||||
assert.strictEqual(test_reference.incrementRefcount(), 2);
|
||||
global.gc(); // Value should NOT be GC'd because there is a strong ref
|
||||
assert.strictEqual(test_reference.finalizeCount, 0);
|
||||
|
||||
assert.strictEqual(test_reference.decrementRefcount(), 1);
|
||||
global.gc(); // Value should NOT be GC'd because there is a strong ref
|
||||
assert.strictEqual(test_reference.finalizeCount, 0);
|
||||
|
||||
assert.strictEqual(test_reference.decrementRefcount(), 0);
|
||||
global.gc(); // Value should be GC'd because the ref is now weak!
|
||||
assert.strictEqual(test_reference.finalizeCount, 1);
|
||||
|
||||
test_reference.deleteReference();
|
||||
global.gc(); // Value was already GC'd
|
||||
assert.strictEqual(test_reference.finalizeCount, 1);
|
||||
}
|
153
test/addons-napi/test_reference/test_reference.c
Normal file
153
test/addons-napi/test_reference/test_reference.c
Normal file
@ -0,0 +1,153 @@
|
||||
#include <node_api.h>
|
||||
#include "../common.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
static int test_value = 1;
|
||||
static int finalize_count = 0;
|
||||
static napi_ref test_reference = NULL;
|
||||
|
||||
napi_value GetFinalizeCount(napi_env env, napi_callback_info info) {
|
||||
napi_value result;
|
||||
NAPI_CALL(env, napi_create_number(env, finalize_count, &result));
|
||||
return result;
|
||||
}
|
||||
|
||||
void FinalizeExternal(napi_env env, void* data, void* hint) {
|
||||
free(data);
|
||||
finalize_count++;
|
||||
}
|
||||
|
||||
napi_value CreateExternal(napi_env env, napi_callback_info info) {
|
||||
int* data = &test_value;
|
||||
|
||||
napi_value result;
|
||||
NAPI_CALL(env,
|
||||
napi_create_external(env,
|
||||
data,
|
||||
NULL, /* finalize_cb */
|
||||
NULL, /* finalize_hint */
|
||||
&result));
|
||||
|
||||
finalize_count = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
napi_value CreateExternalWithFinalize(napi_env env, napi_callback_info info) {
|
||||
int* data = malloc(sizeof(int));
|
||||
*data = test_value;
|
||||
|
||||
napi_value result;
|
||||
NAPI_CALL(env,
|
||||
napi_create_external(env,
|
||||
data,
|
||||
FinalizeExternal,
|
||||
NULL, /* finalize_hint */
|
||||
&result));
|
||||
|
||||
finalize_count = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
napi_value CheckExternal(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 1;
|
||||
napi_value arg;
|
||||
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &arg, NULL, NULL));
|
||||
|
||||
NAPI_ASSERT(env, argc == 1, "Expected one argument.");
|
||||
|
||||
napi_valuetype argtype;
|
||||
NAPI_CALL(env, napi_typeof(env, arg, &argtype));
|
||||
|
||||
NAPI_ASSERT(env, argtype == napi_external, "Expected an external value.")
|
||||
|
||||
int* data;
|
||||
NAPI_CALL(env, napi_get_value_external(env, arg, &data));
|
||||
|
||||
NAPI_ASSERT(env, data != NULL && *data == test_value,
|
||||
"An external data value of 1 was expected.")
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
napi_value CreateReference(napi_env env, napi_callback_info info) {
|
||||
NAPI_ASSERT(env, test_reference == NULL,
|
||||
"The test allows only one reference at a time.");
|
||||
|
||||
size_t argc = 2;
|
||||
napi_value args[2];
|
||||
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
|
||||
NAPI_ASSERT(env, argc == 2, "Expected two arguments.");
|
||||
|
||||
uint32_t initial_refcount;
|
||||
NAPI_CALL(env, napi_get_value_uint32(env, args[1], &initial_refcount));
|
||||
|
||||
NAPI_CALL(env,
|
||||
napi_create_reference(env, args[0], initial_refcount, &test_reference));
|
||||
|
||||
NAPI_ASSERT(env, test_reference != NULL,
|
||||
"A reference should have been created.");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
napi_value DeleteReference(napi_env env, napi_callback_info info) {
|
||||
NAPI_ASSERT(env, test_reference != NULL,
|
||||
"A reference must have been created.");
|
||||
|
||||
NAPI_CALL(env, napi_delete_reference(env, test_reference));
|
||||
test_reference = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
napi_value IncrementRefcount(napi_env env, napi_callback_info info) {
|
||||
NAPI_ASSERT(env, test_reference != NULL,
|
||||
"A reference must have been created.");
|
||||
|
||||
uint32_t refcount;
|
||||
NAPI_CALL(env, napi_reference_ref(env, test_reference, &refcount));
|
||||
|
||||
napi_value result;
|
||||
NAPI_CALL(env, napi_create_number(env, refcount, &result));
|
||||
return result;
|
||||
}
|
||||
|
||||
napi_value DecrementRefcount(napi_env env, napi_callback_info info) {
|
||||
NAPI_ASSERT(env, test_reference != NULL,
|
||||
"A reference must have been created.");
|
||||
|
||||
uint32_t refcount;
|
||||
NAPI_CALL(env, napi_reference_unref(env, test_reference, &refcount));
|
||||
|
||||
napi_value result;
|
||||
NAPI_CALL(env, napi_create_number(env, refcount, &result));
|
||||
return result;
|
||||
}
|
||||
|
||||
napi_value GetReferenceValue(napi_env env, napi_callback_info info) {
|
||||
NAPI_ASSERT(env, test_reference != NULL,
|
||||
"A reference must have been created.");
|
||||
|
||||
napi_value result;
|
||||
NAPI_CALL(env, napi_get_reference_value(env, test_reference, &result));
|
||||
return result;
|
||||
}
|
||||
|
||||
void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
|
||||
napi_property_descriptor descriptors[] = {
|
||||
DECLARE_NAPI_GETTER("finalizeCount", GetFinalizeCount),
|
||||
DECLARE_NAPI_PROPERTY("createExternal", CreateExternal),
|
||||
DECLARE_NAPI_PROPERTY("createExternalWithFinalize",
|
||||
CreateExternalWithFinalize),
|
||||
DECLARE_NAPI_PROPERTY("checkExternal", CheckExternal),
|
||||
DECLARE_NAPI_PROPERTY("createReference", CreateReference),
|
||||
DECLARE_NAPI_PROPERTY("deleteReference", DeleteReference),
|
||||
DECLARE_NAPI_PROPERTY("incrementRefcount", IncrementRefcount),
|
||||
DECLARE_NAPI_PROPERTY("decrementRefcount", DecrementRefcount),
|
||||
DECLARE_NAPI_GETTER("referenceValue", GetReferenceValue),
|
||||
};
|
||||
|
||||
NAPI_CALL_RETURN_VOID(env, napi_define_properties(
|
||||
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
|
||||
}
|
||||
|
||||
NAPI_MODULE(addon, Init)
|
Loading…
x
Reference in New Issue
Block a user