mirror of https://github.com/lukechilds/node.git
Browse Source
- 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>v6
Jason Ginchereau
8 years ago
committed by
Michael Dawson
5 changed files with 255 additions and 2 deletions
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"targets": [ |
||||
|
{ |
||||
|
"target_name": "test_reference", |
||||
|
"sources": [ "test_reference.c" ] |
||||
|
} |
||||
|
] |
||||
|
} |
@ -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); |
||||
|
} |
@ -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…
Reference in new issue