Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

7
test/js-native-api/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.buildstamp
.docbuildstamp
Makefile
*.Makefile
*.mk
gyp-mac-tool
/*/build

View File

@ -0,0 +1,39 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value Add(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 2, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
napi_valuetype valuetype1;
NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
NODE_API_ASSERT(env, valuetype0 == napi_number && valuetype1 == napi_number,
"Wrong argument type. Numbers expected.");
double value0;
NODE_API_CALL(env, napi_get_value_double(env, args[0], &value0));
double value1;
NODE_API_CALL(env, napi_get_value_double(env, args[1], &value1));
napi_value sum;
NODE_API_CALL(env, napi_create_double(env, value0 + value1, &sum));
return sum;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc = DECLARE_NODE_API_PROPERTY("add", Add);
NODE_API_CALL(env, napi_define_properties(env, exports, 1, &desc));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "2_function_arguments",
"sources": [
"2_function_arguments.c"
]
}
]
}

View File

@ -0,0 +1,6 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/2_function_arguments`);
assert.strictEqual(addon.add(3, 5), 8);

View File

@ -0,0 +1,58 @@
#include <js_native_api.h>
#include <string.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value RunCallback(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc == 1,
"Wrong number of arguments. Expects a single argument.");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_function,
"Wrong type of arguments. Expects a function as first argument.");
napi_valuetype valuetype1;
NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
NODE_API_ASSERT(env, valuetype1 == napi_undefined,
"Additional arguments should be undefined.");
napi_value argv[1];
const char* str = "hello world";
size_t str_len = strlen(str);
NODE_API_CALL(env, napi_create_string_utf8(env, str, str_len, argv));
napi_value global;
NODE_API_CALL(env, napi_get_global(env, &global));
napi_value cb = args[0];
NODE_API_CALL(env, napi_call_function(env, global, cb, 1, argv, NULL));
return NULL;
}
static napi_value RunCallbackWithRecv(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value cb = args[0];
napi_value recv = args[1];
NODE_API_CALL(env, napi_call_function(env, recv, cb, 0, NULL, NULL));
return NULL;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[2] = {
DECLARE_NODE_API_PROPERTY("RunCallback", RunCallback),
DECLARE_NODE_API_PROPERTY("RunCallbackWithRecv", RunCallbackWithRecv),
};
NODE_API_CALL(env, napi_define_properties(env, exports, 2, desc));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "3_callbacks",
"sources": [
"3_callbacks.c"
]
}
]
}

View File

@ -0,0 +1,22 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/3_callbacks`);
addon.RunCallback(function(msg) {
assert.strictEqual(msg, 'hello world');
});
function testRecv(desiredRecv) {
addon.RunCallbackWithRecv(function() {
assert.strictEqual(this, desiredRecv);
}, desiredRecv);
}
testRecv(undefined);
testRecv(null);
testRecv(5);
testRecv(true);
testRecv('Hello');
testRecv([]);
testRecv({});

View File

@ -0,0 +1,24 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value CreateObject(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value obj;
NODE_API_CALL(env, napi_create_object(env, &obj));
NODE_API_CALL(env, napi_set_named_property(env, obj, "msg", args[0]));
return obj;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
NODE_API_CALL(env,
napi_create_function(env, "exports", -1, CreateObject, NULL, &exports));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "4_object_factory",
"sources": [
"4_object_factory.c"
]
}
]
}

View File

@ -0,0 +1,8 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/4_object_factory`);
const obj1 = addon('hello');
const obj2 = addon('world');
assert.strictEqual(`${obj1.msg} ${obj2.msg}`, 'hello world');

View File

@ -0,0 +1,24 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value MyFunction(napi_env env, napi_callback_info info) {
napi_value str;
NODE_API_CALL(env, napi_create_string_utf8(env, "hello world", -1, &str));
return str;
}
static napi_value CreateFunction(napi_env env, napi_callback_info info) {
napi_value fn;
NODE_API_CALL(env,
napi_create_function(env, "theFunction", -1, MyFunction, NULL, &fn));
return fn;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
NODE_API_CALL(env,
napi_create_function(env, "exports", -1, CreateFunction, NULL, &exports));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "5_function_factory",
"sources": [
"5_function_factory.c"
]
}
]
}

View File

@ -0,0 +1,7 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/5_function_factory`);
const fn = addon();
assert.strictEqual(fn(), 'hello world'); // 'hello world'

View File

@ -0,0 +1,28 @@
{
"targets": [
{
"target_name": "myobject",
"sources": [
"myobject.cc",
"myobject.h",
]
},
{
"target_name": "myobject_basic_finalizer",
"defines": [ "NAPI_EXPERIMENTAL" ],
"sources": [
"myobject.cc",
"myobject.h",
]
},
{
"target_name": "nested_wrap",
# Test without basic finalizers as it schedules differently.
"defines": [ "NAPI_VERSION=10" ],
"sources": [
"nested_wrap.cc",
"nested_wrap.h",
],
},
]
}

View File

@ -0,0 +1,270 @@
#include "myobject.h"
#include "../common.h"
#include "../entry_point.h"
#include "assert.h"
typedef int32_t FinalizerData;
napi_ref MyObject::constructor;
MyObject::MyObject(double value)
: value_(value), env_(nullptr), wrapper_(nullptr) {}
MyObject::~MyObject() {
napi_delete_reference(env_, wrapper_);
}
void MyObject::Destructor(node_api_basic_env env,
void* nativeObject,
void* /*finalize_hint*/) {
MyObject* obj = static_cast<MyObject*>(nativeObject);
delete obj;
FinalizerData* data;
NODE_API_BASIC_CALL_RETURN_VOID(
env, napi_get_instance_data(env, reinterpret_cast<void**>(&data)));
*data += 1;
}
void MyObject::Init(napi_env env, napi_value exports) {
napi_property_descriptor properties[] = {
{"value", nullptr, nullptr, GetValue, SetValue, 0, napi_default, 0},
{"valueReadonly",
nullptr,
nullptr,
GetValue,
nullptr,
0,
napi_default,
0},
DECLARE_NODE_API_PROPERTY("plusOne", PlusOne),
DECLARE_NODE_API_PROPERTY("multiply", Multiply),
};
napi_value cons;
NODE_API_CALL_RETURN_VOID(
env,
napi_define_class(env,
"MyObject",
-1,
New,
nullptr,
sizeof(properties) / sizeof(napi_property_descriptor),
properties,
&cons));
NODE_API_CALL_RETURN_VOID(env,
napi_create_reference(env, cons, 1, &constructor));
NODE_API_CALL_RETURN_VOID(
env, napi_set_named_property(env, exports, "MyObject", cons));
}
napi_value MyObject::New(napi_env env, napi_callback_info info) {
napi_value new_target;
NODE_API_CALL(env, napi_get_new_target(env, info, &new_target));
bool is_constructor = (new_target != nullptr);
size_t argc = 1;
napi_value args[1];
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
if (is_constructor) {
// Invoked as constructor: `new MyObject(...)`
double value = 0;
napi_valuetype valuetype;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
if (valuetype != napi_undefined) {
NODE_API_CALL(env, napi_get_value_double(env, args[0], &value));
}
MyObject* obj = new MyObject(value);
obj->env_ = env;
NODE_API_CALL(env,
napi_wrap(env,
_this,
obj,
MyObject::Destructor,
nullptr /* finalize_hint */,
&obj->wrapper_));
return _this;
}
// Invoked as plain function `MyObject(...)`, turn into construct call.
argc = 1;
napi_value argv[1] = {args[0]};
napi_value cons;
NODE_API_CALL(env, napi_get_reference_value(env, constructor, &cons));
napi_value instance;
NODE_API_CALL(env, napi_new_instance(env, cons, argc, argv, &instance));
return instance;
}
napi_value MyObject::GetValue(napi_env env, napi_callback_info info) {
napi_value _this;
NODE_API_CALL(env,
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
MyObject* obj;
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
napi_value num;
NODE_API_CALL(env, napi_create_double(env, obj->value_, &num));
return num;
}
napi_value MyObject::SetValue(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
MyObject* obj;
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
NODE_API_CALL(env, napi_get_value_double(env, args[0], &obj->value_));
return nullptr;
}
napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) {
napi_value _this;
NODE_API_CALL(env,
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
MyObject* obj;
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
obj->value_ += 1;
napi_value num;
NODE_API_CALL(env, napi_create_double(env, obj->value_, &num));
return num;
}
napi_value MyObject::Multiply(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
double multiple = 1;
if (argc >= 1) {
NODE_API_CALL(env, napi_get_value_double(env, args[0], &multiple));
}
MyObject* obj;
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
napi_value cons;
NODE_API_CALL(env, napi_get_reference_value(env, constructor, &cons));
const int kArgCount = 1;
napi_value argv[kArgCount];
NODE_API_CALL(env, napi_create_double(env, obj->value_ * multiple, argv));
napi_value instance;
NODE_API_CALL(env, napi_new_instance(env, cons, kArgCount, argv, &instance));
return instance;
}
// This finalizer should never be invoked.
void ObjectWrapDanglingReferenceFinalizer(node_api_basic_env env,
void* finalize_data,
void* finalize_hint) {
assert(0 && "unreachable");
}
napi_ref dangling_ref;
napi_value ObjectWrapDanglingReference(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env,
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
// Create a napi_wrap and remove it immediately, whilst leaving the out-param
// ref dangling (not deleted).
NODE_API_CALL(env,
napi_wrap(env,
args[0],
nullptr,
ObjectWrapDanglingReferenceFinalizer,
nullptr,
&dangling_ref));
NODE_API_CALL(env, napi_remove_wrap(env, args[0], nullptr));
return args[0];
}
napi_value ObjectWrapDanglingReferenceTest(napi_env env,
napi_callback_info info) {
napi_value out;
napi_value ret;
NODE_API_CALL(env, napi_get_reference_value(env, dangling_ref, &out));
if (out == nullptr) {
// If the napi_ref has been invalidated, delete it.
NODE_API_CALL(env, napi_delete_reference(env, dangling_ref));
NODE_API_CALL(env, napi_get_boolean(env, true, &ret));
} else {
// The dangling napi_ref is still valid.
NODE_API_CALL(env, napi_get_boolean(env, false, &ret));
}
return ret;
}
static napi_value GetFinalizerCallCount(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value argv[1];
FinalizerData* data;
napi_value result;
NODE_API_CALL(env,
napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
NODE_API_CALL(env,
napi_get_instance_data(env, reinterpret_cast<void**>(&data)));
NODE_API_CALL(env, napi_create_int32(env, *data, &result));
return result;
}
static void finalizeData(napi_env env, void* data, void* hint) {
delete reinterpret_cast<FinalizerData*>(data);
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
FinalizerData* data = new FinalizerData;
*data = 0;
NODE_API_CALL(env, napi_set_instance_data(env, data, finalizeData, nullptr));
MyObject::Init(env, exports);
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("objectWrapDanglingReference",
ObjectWrapDanglingReference),
DECLARE_NODE_API_PROPERTY("objectWrapDanglingReferenceTest",
ObjectWrapDanglingReferenceTest),
DECLARE_NODE_API_PROPERTY("getFinalizerCallCount", GetFinalizerCallCount),
};
NODE_API_CALL(
env,
napi_define_properties(env,
exports,
sizeof(descriptors) / sizeof(*descriptors),
descriptors));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,28 @@
#ifndef TEST_JS_NATIVE_API_6_OBJECT_WRAP_MYOBJECT_H_
#define TEST_JS_NATIVE_API_6_OBJECT_WRAP_MYOBJECT_H_
#include <js_native_api.h>
class MyObject {
public:
static void Init(napi_env env, napi_value exports);
static void Destructor(node_api_basic_env env,
void* nativeObject,
void* finalize_hint);
private:
explicit MyObject(double value_ = 0);
~MyObject();
static napi_value New(napi_env env, napi_callback_info info);
static napi_value GetValue(napi_env env, napi_callback_info info);
static napi_value SetValue(napi_env env, napi_callback_info info);
static napi_value PlusOne(napi_env env, napi_callback_info info);
static napi_value Multiply(napi_env env, napi_callback_info info);
static napi_ref constructor;
double value_;
napi_env env_;
napi_ref wrapper_;
};
#endif // TEST_JS_NATIVE_API_6_OBJECT_WRAP_MYOBJECT_H_

View File

@ -0,0 +1,99 @@
#include "nested_wrap.h"
#include "../common.h"
#include "../entry_point.h"
napi_ref NestedWrap::constructor{};
static int finalization_count = 0;
NestedWrap::NestedWrap() {}
NestedWrap::~NestedWrap() {
napi_delete_reference(env_, wrapper_);
// Delete the nested reference as well.
napi_delete_reference(env_, nested_);
}
void NestedWrap::Destructor(node_api_basic_env env,
void* nativeObject,
void* /*finalize_hint*/) {
// Once this destructor is called, it cancels all pending
// finalizers for the object by deleting the references.
NestedWrap* obj = static_cast<NestedWrap*>(nativeObject);
delete obj;
finalization_count++;
}
void NestedWrap::Init(napi_env env, napi_value exports) {
napi_value cons;
NODE_API_CALL_RETURN_VOID(
env,
napi_define_class(
env, "NestedWrap", -1, New, nullptr, 0, nullptr, &cons));
NODE_API_CALL_RETURN_VOID(env,
napi_create_reference(env, cons, 1, &constructor));
NODE_API_CALL_RETURN_VOID(
env, napi_set_named_property(env, exports, "NestedWrap", cons));
}
napi_value NestedWrap::New(napi_env env, napi_callback_info info) {
napi_value new_target;
NODE_API_CALL(env, napi_get_new_target(env, info, &new_target));
bool is_constructor = (new_target != nullptr);
NODE_API_BASIC_ASSERT_BASE(
is_constructor, "Constructor called without new", nullptr);
napi_value this_val;
NODE_API_CALL(env,
napi_get_cb_info(env, info, 0, nullptr, &this_val, nullptr));
NestedWrap* obj = new NestedWrap();
obj->env_ = env;
NODE_API_CALL(env,
napi_wrap(env,
this_val,
obj,
NestedWrap::Destructor,
nullptr /* finalize_hint */,
&obj->wrapper_));
// Create a second napi_ref to be deleted in the destructor.
NODE_API_CALL(env,
napi_add_finalizer(env,
this_val,
obj,
NestedWrap::Destructor,
nullptr /* finalize_hint */,
&obj->nested_));
return this_val;
}
static napi_value GetFinalizerCallCount(napi_env env, napi_callback_info info) {
napi_value result;
NODE_API_CALL(env, napi_create_int32(env, finalization_count, &result));
return result;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
NestedWrap::Init(env, exports);
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("getFinalizerCallCount", GetFinalizerCallCount),
};
NODE_API_CALL(
env,
napi_define_properties(env,
exports,
sizeof(descriptors) / sizeof(*descriptors),
descriptors));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,33 @@
#ifndef TEST_JS_NATIVE_API_6_OBJECT_WRAP_NESTED_WRAP_H_
#define TEST_JS_NATIVE_API_6_OBJECT_WRAP_NESTED_WRAP_H_
#include <js_native_api.h>
/**
* Test that an napi_ref can be nested inside another ObjectWrap.
*
* This test shows a critical case where a finalizer deletes an napi_ref
* whose finalizer is also scheduled.
*/
class NestedWrap {
public:
static void Init(napi_env env, napi_value exports);
static void Destructor(node_api_basic_env env,
void* nativeObject,
void* finalize_hint);
private:
explicit NestedWrap();
~NestedWrap();
static napi_value New(napi_env env, napi_callback_info info);
static napi_ref constructor;
napi_env env_{};
napi_ref wrapper_{};
napi_ref nested_{};
};
#endif // TEST_JS_NATIVE_API_6_OBJECT_WRAP_NESTED_WRAP_H_

View File

@ -0,0 +1,20 @@
// Flags: --expose-gc
'use strict';
const common = require('../../common');
const { gcUntil } = require('../../common/gc');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/nested_wrap`);
// This test verifies that ObjectWrap and napi_ref can be nested and finalized
// correctly with a non-basic finalizer.
(() => {
let obj = new addon.NestedWrap();
obj = null;
// Silent eslint about unused variables.
assert.strictEqual(obj, null);
})();
gcUntil('object-wrap-ref', () => {
return addon.getFinalizerCallCount() === 1;
});

View File

@ -0,0 +1,24 @@
// Flags: --expose-gc
'use strict';
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/myobject_basic_finalizer`);
// This test verifies that ObjectWrap can be correctly finalized with a node_api_basic_finalizer
// in the current JS loop tick
(() => {
let obj = new addon.MyObject(9);
obj = null;
// Silent eslint about unused variables.
assert.strictEqual(obj, null);
})();
for (let i = 0; i < 10; ++i) {
global.gc();
if (addon.getFinalizerCallCount() === 1) {
break;
}
}
assert.strictEqual(addon.getFinalizerCallCount(), 1);

View File

@ -0,0 +1,14 @@
// Flags: --expose-gc
'use strict';
const common = require('../../common');
const addon = require(`./build/${common.buildType}/myobject`);
const { gcUntil } = require('../../common/gc');
(function scope() {
addon.objectWrapDanglingReference({});
})();
gcUntil('object-wrap-ref', () => {
return addon.objectWrapDanglingReferenceTest();
});

View File

@ -0,0 +1,48 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/myobject`);
const getterOnlyErrorRE =
/^TypeError: Cannot set property .* of #<.*> which has only a getter$/;
const valueDescriptor = Object.getOwnPropertyDescriptor(
addon.MyObject.prototype, 'value');
const valueReadonlyDescriptor = Object.getOwnPropertyDescriptor(
addon.MyObject.prototype, 'valueReadonly');
const plusOneDescriptor = Object.getOwnPropertyDescriptor(
addon.MyObject.prototype, 'plusOne');
assert.strictEqual(typeof valueDescriptor.get, 'function');
assert.strictEqual(typeof valueDescriptor.set, 'function');
assert.strictEqual(valueDescriptor.value, undefined);
assert.strictEqual(valueDescriptor.enumerable, false);
assert.strictEqual(valueDescriptor.configurable, false);
assert.strictEqual(typeof valueReadonlyDescriptor.get, 'function');
assert.strictEqual(valueReadonlyDescriptor.set, undefined);
assert.strictEqual(valueReadonlyDescriptor.value, undefined);
assert.strictEqual(valueReadonlyDescriptor.enumerable, false);
assert.strictEqual(valueReadonlyDescriptor.configurable, false);
assert.strictEqual(plusOneDescriptor.get, undefined);
assert.strictEqual(plusOneDescriptor.set, undefined);
assert.strictEqual(typeof plusOneDescriptor.value, 'function');
assert.strictEqual(plusOneDescriptor.enumerable, false);
assert.strictEqual(plusOneDescriptor.configurable, false);
const obj = new addon.MyObject(9);
assert.strictEqual(obj.value, 9);
obj.value = 10;
assert.strictEqual(obj.value, 10);
assert.strictEqual(obj.valueReadonly, 10);
assert.throws(() => { obj.valueReadonly = 14; }, getterOnlyErrorRE);
assert.strictEqual(obj.plusOne(), 11);
assert.strictEqual(obj.plusOne(), 12);
assert.strictEqual(obj.plusOne(), 13);
assert.strictEqual(obj.multiply().value, 13);
assert.strictEqual(obj.multiply(10).value, 130);
const newobj = obj.multiply(-1);
assert.strictEqual(newobj.value, -13);
assert.strictEqual(newobj.valueReadonly, -13);
assert.notStrictEqual(obj, newobj);

View File

@ -0,0 +1,32 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
#include "myobject.h"
napi_value CreateObject(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env,
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
napi_value instance;
NODE_API_CALL(env, MyObject::NewInstance(env, args[0], &instance));
return instance;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
NODE_API_CALL(env, MyObject::Init(env));
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_GETTER("finalizeCount", MyObject::GetFinalizeCount),
DECLARE_NODE_API_PROPERTY("createObject", CreateObject),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "7_factory_wrap",
"sources": [
"7_factory_wrap.cc",
"myobject.cc"
]
}
]
}

View File

@ -0,0 +1,101 @@
#include "myobject.h"
#include "../common.h"
static int finalize_count = 0;
MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {}
MyObject::~MyObject() { napi_delete_reference(env_, wrapper_); }
void MyObject::Destructor(napi_env env,
void* nativeObject,
void* /*finalize_hint*/) {
++finalize_count;
MyObject* obj = static_cast<MyObject*>(nativeObject);
delete obj;
}
napi_value MyObject::GetFinalizeCount(napi_env env, napi_callback_info info) {
napi_value result;
NODE_API_CALL(env, napi_create_int32(env, finalize_count, &result));
return result;
}
napi_ref MyObject::constructor;
napi_status MyObject::Init(napi_env env) {
napi_status status;
napi_property_descriptor properties[] = {
DECLARE_NODE_API_PROPERTY("plusOne", PlusOne),
};
napi_value cons;
status = napi_define_class(
env, "MyObject", -1, New, nullptr, 1, properties, &cons);
if (status != napi_ok) return status;
status = napi_create_reference(env, cons, 1, &constructor);
if (status != napi_ok) return status;
return napi_ok;
}
napi_value MyObject::New(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
napi_valuetype valuetype;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
MyObject* obj = new MyObject();
if (valuetype == napi_undefined) {
obj->counter_ = 0;
} else {
NODE_API_CALL(env, napi_get_value_uint32(env, args[0], &obj->counter_));
}
obj->env_ = env;
NODE_API_CALL(env,
napi_wrap(
env, _this, obj, MyObject::Destructor, nullptr /* finalize_hint */,
&obj->wrapper_));
return _this;
}
napi_status MyObject::NewInstance(napi_env env,
napi_value arg,
napi_value* instance) {
napi_status status;
const int argc = 1;
napi_value argv[argc] = {arg};
napi_value cons;
status = napi_get_reference_value(env, constructor, &cons);
if (status != napi_ok) return status;
status = napi_new_instance(env, cons, argc, argv, instance);
if (status != napi_ok) return status;
return napi_ok;
}
napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) {
napi_value _this;
NODE_API_CALL(env,
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
MyObject* obj;
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
obj->counter_ += 1;
napi_value num;
NODE_API_CALL(env, napi_create_uint32(env, obj->counter_, &num));
return num;
}

View File

@ -0,0 +1,27 @@
#ifndef TEST_JS_NATIVE_API_7_FACTORY_WRAP_MYOBJECT_H_
#define TEST_JS_NATIVE_API_7_FACTORY_WRAP_MYOBJECT_H_
#include <js_native_api.h>
class MyObject {
public:
static napi_status Init(napi_env env);
static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);
static napi_value GetFinalizeCount(napi_env env, napi_callback_info info);
static napi_status NewInstance(napi_env env,
napi_value arg,
napi_value* instance);
private:
MyObject();
~MyObject();
static napi_ref constructor;
static napi_value New(napi_env env, napi_callback_info info);
static napi_value PlusOne(napi_env env, napi_callback_info info);
uint32_t counter_;
napi_env env_;
napi_ref wrapper_;
};
#endif // TEST_JS_NATIVE_API_7_FACTORY_WRAP_MYOBJECT_H_

View File

@ -0,0 +1,27 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const assert = require('assert');
const test = require(`./build/${common.buildType}/7_factory_wrap`);
const { gcUntil } = require('../../common/gc');
assert.strictEqual(test.finalizeCount, 0);
async function runGCTests() {
(() => {
const obj = test.createObject(10);
assert.strictEqual(obj.plusOne(), 11);
assert.strictEqual(obj.plusOne(), 12);
assert.strictEqual(obj.plusOne(), 13);
})();
await gcUntil('test 1', () => (test.finalizeCount === 1));
(() => {
const obj2 = test.createObject(20);
assert.strictEqual(obj2.plusOne(), 21);
assert.strictEqual(obj2.plusOne(), 22);
assert.strictEqual(obj2.plusOne(), 23);
})();
await gcUntil('test 2', () => (test.finalizeCount === 2));
}
runGCTests();

View File

@ -0,0 +1,61 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
#include "myobject.h"
extern size_t finalize_count;
static napi_value CreateObject(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env,
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
napi_value instance;
NODE_API_CALL(env, MyObject::NewInstance(env, args[0], &instance));
return instance;
}
static napi_value Add(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env,
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
MyObject* obj1;
NODE_API_CALL(env,
napi_unwrap(env, args[0], reinterpret_cast<void**>(&obj1)));
MyObject* obj2;
NODE_API_CALL(env,
napi_unwrap(env, args[1], reinterpret_cast<void**>(&obj2)));
napi_value sum;
NODE_API_CALL(env, napi_create_double(env, obj1->Val() + obj2->Val(), &sum));
return sum;
}
static napi_value FinalizeCount(napi_env env, napi_callback_info info) {
napi_value return_value;
NODE_API_CALL(env, napi_create_uint32(env, finalize_count, &return_value));
return return_value;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
MyObject::Init(env);
napi_property_descriptor desc[] = {
DECLARE_NODE_API_PROPERTY("createObject", CreateObject),
DECLARE_NODE_API_PROPERTY("add", Add),
DECLARE_NODE_API_PROPERTY("finalizeCount", FinalizeCount),
};
NODE_API_CALL(env,
napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "8_passing_wrapped",
"sources": [
"8_passing_wrapped.cc",
"myobject.cc"
]
}
]
}

View File

@ -0,0 +1,80 @@
#include "myobject.h"
#include "../common.h"
size_t finalize_count = 0;
MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {}
MyObject::~MyObject() {
finalize_count++;
napi_delete_reference(env_, wrapper_);
}
void MyObject::Destructor(
napi_env env, void* nativeObject, void* /*finalize_hint*/) {
MyObject* obj = static_cast<MyObject*>(nativeObject);
delete obj;
}
napi_ref MyObject::constructor;
napi_status MyObject::Init(napi_env env) {
napi_status status;
napi_value cons;
status = napi_define_class(
env, "MyObject", -1, New, nullptr, 0, nullptr, &cons);
if (status != napi_ok) return status;
status = napi_create_reference(env, cons, 1, &constructor);
if (status != napi_ok) return status;
return napi_ok;
}
napi_value MyObject::New(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
MyObject* obj = new MyObject();
napi_valuetype valuetype;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
if (valuetype == napi_undefined) {
obj->val_ = 0;
} else {
NODE_API_CALL(env, napi_get_value_double(env, args[0], &obj->val_));
}
obj->env_ = env;
// The below call to napi_wrap() must request a reference to the wrapped
// object via the out-parameter, because this ensures that we test the code
// path that deals with a reference that is destroyed from its own finalizer.
NODE_API_CALL(env,
napi_wrap(env, _this, obj, MyObject::Destructor,
nullptr /* finalize_hint */, &obj->wrapper_));
return _this;
}
napi_status MyObject::NewInstance(napi_env env,
napi_value arg,
napi_value* instance) {
napi_status status;
const int argc = 1;
napi_value argv[argc] = {arg};
napi_value cons;
status = napi_get_reference_value(env, constructor, &cons);
if (status != napi_ok) return status;
status = napi_new_instance(env, cons, argc, argv, instance);
if (status != napi_ok) return status;
return napi_ok;
}

View File

@ -0,0 +1,26 @@
#ifndef TEST_JS_NATIVE_API_8_PASSING_WRAPPED_MYOBJECT_H_
#define TEST_JS_NATIVE_API_8_PASSING_WRAPPED_MYOBJECT_H_
#include <js_native_api.h>
class MyObject {
public:
static napi_status Init(napi_env env);
static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);
static napi_status NewInstance(napi_env env,
napi_value arg,
napi_value* instance);
double Val() const { return val_; }
private:
MyObject();
~MyObject();
static napi_ref constructor;
static napi_value New(napi_env env, napi_callback_info info);
double val_;
napi_env env_;
napi_ref wrapper_;
};
#endif // TEST_JS_NATIVE_API_8_PASSING_WRAPPED_MYOBJECT_H_

View File

@ -0,0 +1,21 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/8_passing_wrapped`);
const { gcUntil } = require('../../common/gc');
async function runTest() {
let obj1 = addon.createObject(10);
let obj2 = addon.createObject(20);
const result = addon.add(obj1, obj2);
assert.strictEqual(result, 30);
// Make sure the native destructor gets called.
obj1 = null;
obj2 = null;
await gcUntil('8_passing_wrapped',
() => (addon.finalizeCount() === 2));
}
runTest();

View File

@ -0,0 +1,71 @@
#ifndef JS_NATIVE_API_COMMON_INL_H_
#define JS_NATIVE_API_COMMON_INL_H_
#include <js_native_api.h>
#include "common.h"
#include <stdio.h>
inline void add_returned_status(napi_env env,
const char* key,
napi_value object,
char* expected_message,
napi_status expected_status,
napi_status actual_status) {
char napi_message_string[100] = "";
napi_value prop_value;
if (actual_status != expected_status) {
snprintf(napi_message_string,
sizeof(napi_message_string),
"Invalid status [%d]",
actual_status);
}
NODE_API_CALL_RETURN_VOID(
env,
napi_create_string_utf8(
env,
(actual_status == expected_status ? expected_message
: napi_message_string),
NAPI_AUTO_LENGTH,
&prop_value));
NODE_API_CALL_RETURN_VOID(
env, napi_set_named_property(env, object, key, prop_value));
}
inline void add_last_status(napi_env env,
const char* key,
napi_value return_value) {
napi_value prop_value;
napi_value exception;
const napi_extended_error_info* p_last_error;
NODE_API_CALL_RETURN_VOID(env, napi_get_last_error_info(env, &p_last_error));
// Content of p_last_error can be updated in subsequent node-api calls.
// Retrieve it immediately.
const char* error_message = p_last_error->error_message == NULL
? "napi_ok"
: p_last_error->error_message;
bool is_exception_pending;
NODE_API_CALL_RETURN_VOID(
env, napi_is_exception_pending(env, &is_exception_pending));
if (is_exception_pending) {
NODE_API_CALL_RETURN_VOID(
env, napi_get_and_clear_last_exception(env, &exception));
char exception_key[50];
snprintf(exception_key, sizeof(exception_key), "%s%s", key, "Exception");
NODE_API_CALL_RETURN_VOID(
env,
napi_set_named_property(env, return_value, exception_key, exception));
}
NODE_API_CALL_RETURN_VOID(
env,
napi_create_string_utf8(
env, error_message, NAPI_AUTO_LENGTH, &prop_value));
NODE_API_CALL_RETURN_VOID(
env, napi_set_named_property(env, return_value, key, prop_value));
}
#endif // JS_NATIVE_API_COMMON_INL_H_

132
test/js-native-api/common.h Normal file
View File

@ -0,0 +1,132 @@
#ifndef JS_NATIVE_API_COMMON_H_
#define JS_NATIVE_API_COMMON_H_
#include <js_native_api.h>
#include <stdlib.h> // abort()
// Empty value so that macros here are able to return NULL or void
#define NODE_API_RETVAL_NOTHING // Intentionally blank #define
#define GET_AND_THROW_LAST_ERROR(env) \
do { \
const napi_extended_error_info *error_info; \
napi_get_last_error_info((env), &error_info); \
bool is_pending; \
const char* err_message = error_info->error_message; \
napi_is_exception_pending((env), &is_pending); \
/* If an exception is already pending, don't rethrow it */ \
if (!is_pending) { \
const char* error_message = err_message != NULL ? \
err_message : \
"empty error message"; \
napi_throw_error((env), NULL, error_message); \
} \
} while (0)
// The basic version of GET_AND_THROW_LAST_ERROR. We cannot access any
// exceptions and we cannot fail by way of JS exception, so we abort.
#define FATALLY_FAIL_WITH_LAST_ERROR(env) \
do { \
const napi_extended_error_info* error_info; \
napi_get_last_error_info((env), &error_info); \
const char* err_message = error_info->error_message; \
const char* error_message = \
err_message != NULL ? err_message : "empty error message"; \
fprintf(stderr, "%s\n", error_message); \
abort(); \
} while (0)
#define NODE_API_ASSERT_BASE(env, assertion, message, ret_val) \
do { \
if (!(assertion)) { \
napi_throw_error( \
(env), \
NULL, \
"assertion (" #assertion ") failed: " message); \
return ret_val; \
} \
} while (0)
#define NODE_API_BASIC_ASSERT_BASE(assertion, message, ret_val) \
do { \
if (!(assertion)) { \
fprintf(stderr, "assertion (" #assertion ") failed: " message); \
abort(); \
return ret_val; \
} \
} while (0)
// Returns NULL on failed assertion.
// This is meant to be used inside napi_callback methods.
#define NODE_API_ASSERT(env, assertion, message) \
NODE_API_ASSERT_BASE(env, assertion, message, NULL)
// Returns empty on failed assertion.
// This is meant to be used inside functions with void return type.
#define NODE_API_ASSERT_RETURN_VOID(env, assertion, message) \
NODE_API_ASSERT_BASE(env, assertion, message, NODE_API_RETVAL_NOTHING)
#define NODE_API_BASIC_ASSERT_RETURN_VOID(assertion, message) \
NODE_API_BASIC_ASSERT_BASE(assertion, message, NODE_API_RETVAL_NOTHING)
#define NODE_API_CALL_BASE(env, the_call, ret_val) \
do { \
if ((the_call) != napi_ok) { \
GET_AND_THROW_LAST_ERROR((env)); \
return ret_val; \
} \
} while (0)
#define NODE_API_BASIC_CALL_BASE(env, the_call, ret_val) \
do { \
if ((the_call) != napi_ok) { \
FATALLY_FAIL_WITH_LAST_ERROR((env)); \
return ret_val; \
} \
} while (0)
// Returns NULL if the_call doesn't return napi_ok.
#define NODE_API_CALL(env, the_call) \
NODE_API_CALL_BASE(env, the_call, NULL)
// Returns empty if the_call doesn't return napi_ok.
#define NODE_API_CALL_RETURN_VOID(env, the_call) \
NODE_API_CALL_BASE(env, the_call, NODE_API_RETVAL_NOTHING)
#define NODE_API_BASIC_CALL_RETURN_VOID(env, the_call) \
NODE_API_BASIC_CALL_BASE(env, the_call, NODE_API_RETVAL_NOTHING)
#define NODE_API_CHECK_STATUS(the_call) \
do { \
napi_status status = (the_call); \
if (status != napi_ok) { \
return status; \
} \
} while (0)
#define NODE_API_ASSERT_STATUS(env, assertion, message) \
NODE_API_ASSERT_BASE(env, assertion, message, napi_generic_failure)
#define DECLARE_NODE_API_PROPERTY(name, func) \
{ (name), NULL, (func), NULL, NULL, NULL, napi_default, NULL }
#define DECLARE_NODE_API_GETTER(name, func) \
{ (name), NULL, NULL, (func), NULL, NULL, napi_default, NULL }
#define DECLARE_NODE_API_PROPERTY_VALUE(name, value) \
{ (name), NULL, NULL, NULL, NULL, (value), napi_default, NULL }
static inline void add_returned_status(napi_env env,
const char* key,
napi_value object,
char* expected_message,
napi_status expected_status,
napi_status actual_status);
static inline void add_last_status(napi_env env,
const char* key,
napi_value return_value);
#include "common-inl.h"
#endif // JS_NATIVE_API_COMMON_H_

View File

@ -0,0 +1,12 @@
#ifndef JS_NATIVE_API_ENTRY_POINT_H_
#define JS_NATIVE_API_ENTRY_POINT_H_
#include <node_api.h>
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports);
EXTERN_C_END
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
#endif // JS_NATIVE_API_ENTRY_POINT_H_

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "test_array",
"sources": [
"test_array.c"
]
}
]
}

View File

@ -0,0 +1,60 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
// Testing api calls for arrays
const test_array = require(`./build/${common.buildType}/test_array`);
const array = [
1,
9,
48,
13493,
9459324,
{ name: 'hello' },
[
'world',
'node',
'abi',
],
];
assert.throws(
() => {
test_array.TestGetElement(array, array.length + 1);
},
/^Error: assertion \(\(\(uint32_t\)index < length\)\) failed: Index out of bounds!$/,
);
assert.throws(
() => {
test_array.TestGetElement(array, -2);
},
/^Error: assertion \(index >= 0\) failed: Invalid index\. Expects a positive integer\.$/,
);
array.forEach(function(element, index) {
assert.strictEqual(test_array.TestGetElement(array, index), element);
});
assert.deepStrictEqual(test_array.New(array), array);
assert(test_array.TestHasElement(array, 0));
assert.strictEqual(test_array.TestHasElement(array, array.length + 1), false);
assert(test_array.NewWithLength(0) instanceof Array);
assert(test_array.NewWithLength(1) instanceof Array);
// Check max allowed length for an array 2^32 -1
assert(test_array.NewWithLength(4294967295) instanceof Array);
{
// Verify that array elements can be deleted.
const arr = ['a', 'b', 'c', 'd'];
assert.strictEqual(arr.length, 4);
assert.strictEqual(2 in arr, true);
assert.strictEqual(test_array.TestDeleteElement(arr, 2), true);
assert.strictEqual(arr.length, 4);
assert.strictEqual(2 in arr, false);
}

View File

@ -0,0 +1,188 @@
#include <js_native_api.h>
#include <string.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value TestGetElement(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 2, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_object,
"Wrong type of arguments. Expects an array as first argument.");
napi_valuetype valuetype1;
NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
NODE_API_ASSERT(env, valuetype1 == napi_number,
"Wrong type of arguments. Expects an integer as second argument.");
napi_value array = args[0];
int32_t index;
NODE_API_CALL(env, napi_get_value_int32(env, args[1], &index));
NODE_API_ASSERT(env, index >= 0, "Invalid index. Expects a positive integer.");
bool isarray;
NODE_API_CALL(env, napi_is_array(env, array, &isarray));
if (!isarray) {
return NULL;
}
uint32_t length;
NODE_API_CALL(env, napi_get_array_length(env, array, &length));
NODE_API_ASSERT(env, ((uint32_t)index < length), "Index out of bounds!");
napi_value ret;
NODE_API_CALL(env, napi_get_element(env, array, index, &ret));
return ret;
}
static napi_value TestHasElement(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 2, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_object,
"Wrong type of arguments. Expects an array as first argument.");
napi_valuetype valuetype1;
NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
NODE_API_ASSERT(env, valuetype1 == napi_number,
"Wrong type of arguments. Expects an integer as second argument.");
napi_value array = args[0];
int32_t index;
NODE_API_CALL(env, napi_get_value_int32(env, args[1], &index));
bool isarray;
NODE_API_CALL(env, napi_is_array(env, array, &isarray));
if (!isarray) {
return NULL;
}
bool has_element;
NODE_API_CALL(env, napi_has_element(env, array, index, &has_element));
napi_value ret;
NODE_API_CALL(env, napi_get_boolean(env, has_element, &ret));
return ret;
}
static napi_value TestDeleteElement(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc == 2, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_object,
"Wrong type of arguments. Expects an array as first argument.");
napi_valuetype valuetype1;
NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
NODE_API_ASSERT(env, valuetype1 == napi_number,
"Wrong type of arguments. Expects an integer as second argument.");
napi_value array = args[0];
int32_t index;
bool result;
napi_value ret;
NODE_API_CALL(env, napi_get_value_int32(env, args[1], &index));
NODE_API_CALL(env, napi_is_array(env, array, &result));
if (!result) {
return NULL;
}
NODE_API_CALL(env, napi_delete_element(env, array, index, &result));
NODE_API_CALL(env, napi_get_boolean(env, result, &ret));
return ret;
}
static napi_value New(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_object,
"Wrong type of arguments. Expects an array as first argument.");
napi_value ret;
NODE_API_CALL(env, napi_create_array(env, &ret));
uint32_t i, length;
NODE_API_CALL(env, napi_get_array_length(env, args[0], &length));
for (i = 0; i < length; i++) {
napi_value e;
NODE_API_CALL(env, napi_get_element(env, args[0], i, &e));
NODE_API_CALL(env, napi_set_element(env, ret, i, e));
}
return ret;
}
static napi_value NewWithLength(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_number,
"Wrong type of arguments. Expects an integer the first argument.");
int32_t array_length;
NODE_API_CALL(env, napi_get_value_int32(env, args[0], &array_length));
napi_value ret;
NODE_API_CALL(env, napi_create_array_with_length(env, array_length, &ret));
return ret;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("TestGetElement", TestGetElement),
DECLARE_NODE_API_PROPERTY("TestHasElement", TestHasElement),
DECLARE_NODE_API_PROPERTY("TestDeleteElement", TestDeleteElement),
DECLARE_NODE_API_PROPERTY("New", New),
DECLARE_NODE_API_PROPERTY("NewWithLength", NewWithLength),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "test_bigint",
"sources": [
"test_bigint.c"
]
}
]
}

View File

@ -0,0 +1,52 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const {
IsLossless,
TestInt64,
TestUint64,
TestWords,
CreateTooBigBigInt,
MakeBigIntWordsThrow,
} = require(`./build/${common.buildType}/test_bigint`);
[
0n,
-0n,
1n,
-1n,
100n,
2121n,
-1233n,
986583n,
-976675n,
98765432213456789876546896323445679887645323232436587988766545658n,
-4350987086545760976737453646576078997096876957864353245245769809n,
].forEach((num) => {
if (num > -(2n ** 63n) && num < 2n ** 63n) {
assert.strictEqual(TestInt64(num), num);
assert.strictEqual(IsLossless(num, true), true);
} else {
assert.strictEqual(IsLossless(num, true), false);
}
if (num >= 0 && num < 2n ** 64n) {
assert.strictEqual(TestUint64(num), num);
assert.strictEqual(IsLossless(num, false), true);
} else {
assert.strictEqual(IsLossless(num, false), false);
}
assert.strictEqual(num, TestWords(num));
});
assert.throws(() => CreateTooBigBigInt(), {
name: 'Error',
message: 'Invalid argument',
});
// Test that we correctly forward exceptions from the engine.
assert.throws(() => MakeBigIntWordsThrow(), {
name: 'RangeError',
message: 'Maximum BigInt size exceeded',
});

View File

@ -0,0 +1,159 @@
#include <inttypes.h>
#include <js_native_api.h>
#include <limits.h>
#include <stdio.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value IsLossless(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
bool is_signed;
NODE_API_CALL(env, napi_get_value_bool(env, args[1], &is_signed));
bool lossless;
if (is_signed) {
int64_t input;
NODE_API_CALL(env, napi_get_value_bigint_int64(env, args[0], &input, &lossless));
} else {
uint64_t input;
NODE_API_CALL(env, napi_get_value_bigint_uint64(env, args[0], &input, &lossless));
}
napi_value output;
NODE_API_CALL(env, napi_get_boolean(env, lossless, &output));
return output;
}
static napi_value TestInt64(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_bigint,
"Wrong type of arguments. Expects a bigint as first argument.");
int64_t input;
bool lossless;
NODE_API_CALL(env, napi_get_value_bigint_int64(env, args[0], &input, &lossless));
napi_value output;
NODE_API_CALL(env, napi_create_bigint_int64(env, input, &output));
return output;
}
static napi_value TestUint64(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_bigint,
"Wrong type of arguments. Expects a bigint as first argument.");
uint64_t input;
bool lossless;
NODE_API_CALL(env, napi_get_value_bigint_uint64(
env, args[0], &input, &lossless));
napi_value output;
NODE_API_CALL(env, napi_create_bigint_uint64(env, input, &output));
return output;
}
static napi_value TestWords(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_bigint,
"Wrong type of arguments. Expects a bigint as first argument.");
size_t expected_word_count;
NODE_API_CALL(env, napi_get_value_bigint_words(
env, args[0], NULL, &expected_word_count, NULL));
int sign_bit;
size_t word_count = 10;
uint64_t words[10];
NODE_API_CALL(env, napi_get_value_bigint_words(
env, args[0], &sign_bit, &word_count, words));
NODE_API_ASSERT(env, word_count == expected_word_count,
"word counts do not match");
napi_value output;
NODE_API_CALL(env, napi_create_bigint_words(
env, sign_bit, word_count, words, &output));
return output;
}
// throws RangeError
static napi_value CreateTooBigBigInt(napi_env env, napi_callback_info info) {
int sign_bit = 0;
size_t word_count = SIZE_MAX;
uint64_t words[10] = {0};
napi_value output;
NODE_API_CALL(env, napi_create_bigint_words(
env, sign_bit, word_count, words, &output));
return output;
}
// Test that we correctly forward exceptions from the engine.
static napi_value MakeBigIntWordsThrow(napi_env env, napi_callback_info info) {
uint64_t words[10] = {0};
napi_value output;
napi_status status = napi_create_bigint_words(env,
0,
INT_MAX,
words,
&output);
if (status != napi_pending_exception)
napi_throw_error(env, NULL, "Expected status `napi_pending_exception`");
return NULL;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("IsLossless", IsLossless),
DECLARE_NODE_API_PROPERTY("TestInt64", TestInt64),
DECLARE_NODE_API_PROPERTY("TestUint64", TestUint64),
DECLARE_NODE_API_PROPERTY("TestWords", TestWords),
DECLARE_NODE_API_PROPERTY("CreateTooBigBigInt", CreateTooBigBigInt),
DECLARE_NODE_API_PROPERTY("MakeBigIntWordsThrow", MakeBigIntWordsThrow),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,18 @@
{
"targets": [
{
"target_name": "test_cannot_run_js",
"sources": [
"test_cannot_run_js.c"
],
"defines": [ "NAPI_VERSION=10" ],
},
{
"target_name": "test_pending_exception",
"sources": [
"test_cannot_run_js.c"
],
"defines": [ "NAPI_VERSION=9" ],
}
]
}

View File

@ -0,0 +1,24 @@
'use strict';
// Test that `napi_call_function()` returns `napi_cannot_run_js` in experimental
// mode and `napi_pending_exception` otherwise. This test calls the add-on's
// `createRef()` method, which creates a strong reference to a JS function. When
// the process exits, it calls all reference finalizers. The finalizer for the
// strong reference created herein will attempt to call `napi_get_property()` on
// a property of the global object and will abort the process if the API doesn't
// return the correct status.
const { buildType, mustNotCall } = require('../../common');
const addon_v8 = require(`./build/${buildType}/test_pending_exception`);
const addon_new = require(`./build/${buildType}/test_cannot_run_js`);
function runTests(addon, isVersion8) {
addon.createRef(mustNotCall());
}
function runAllTests() {
runTests(addon_v8, /* isVersion8 */ true);
runTests(addon_new, /* isVersion8 */ false);
}
runAllTests();

View File

@ -0,0 +1,66 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
#include "stdlib.h"
static void Finalize(napi_env env, void* data, void* hint) {
napi_value global, set_timeout;
napi_ref* ref = data;
NODE_API_BASIC_ASSERT_RETURN_VOID(
napi_delete_reference(env, *ref) == napi_ok,
"deleting reference in finalizer should succeed");
NODE_API_BASIC_ASSERT_RETURN_VOID(
napi_get_global(env, &global) == napi_ok,
"getting global reference in finalizer should succeed");
napi_status result =
napi_get_named_property(env, global, "setTimeout", &set_timeout);
// The finalizer could be invoked either from check callbacks (as native
// immediates) if the event loop is still running (where napi_ok is returned)
// or during environment shutdown (where napi_cannot_run_js or
// napi_pending_exception is returned). This is not deterministic from
// the point of view of the addon.
#if NAPI_VERSION > 9
NODE_API_BASIC_ASSERT_RETURN_VOID(
result == napi_cannot_run_js || result == napi_ok,
"getting named property from global in finalizer should succeed "
"or return napi_cannot_run_js");
#else
NODE_API_BASIC_ASSERT_RETURN_VOID(
result == napi_pending_exception || result == napi_ok,
"getting named property from global in finalizer should succeed "
"or return napi_pending_exception");
#endif // NAPI_VERSION > 9
free(ref);
}
static napi_value CreateRef(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value cb;
napi_valuetype value_type;
napi_ref* ref = malloc(sizeof(*ref));
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &cb, NULL, NULL));
NODE_API_ASSERT(env, argc == 1, "Function takes only one argument");
NODE_API_CALL(env, napi_typeof(env, cb, &value_type));
NODE_API_ASSERT(
env, value_type == napi_function, "argument must be function");
NODE_API_CALL(env, napi_add_finalizer(env, cb, ref, Finalize, NULL, ref));
return cb;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor properties[] = {
DECLARE_NODE_API_PROPERTY("createRef", CreateRef),
};
NODE_API_CALL(
env,
napi_define_properties(
env, exports, sizeof(properties) / sizeof(*properties), properties));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_constructor",
"sources": [
"test_constructor.c",
"test_null.c",
]
}
]
}

View File

@ -0,0 +1,62 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const getterOnlyErrorRE =
/^TypeError: Cannot set property .* of #<.*> which has only a getter$/;
// Testing api calls for a constructor that defines properties
const TestConstructor = require(`./build/${common.buildType}/test_constructor`);
const test_object = new TestConstructor();
assert.strictEqual(test_object.echo('hello'), 'hello');
test_object.readwriteValue = 1;
assert.strictEqual(test_object.readwriteValue, 1);
test_object.readwriteValue = 2;
assert.strictEqual(test_object.readwriteValue, 2);
assert.throws(() => { test_object.readonlyValue = 3; },
/^TypeError: Cannot assign to read only property 'readonlyValue' of object '#<MyObject>'$/);
assert.ok(test_object.hiddenValue);
// Properties with napi_enumerable attribute should be enumerable.
const propertyNames = [];
for (const name in test_object) {
propertyNames.push(name);
}
assert.ok(propertyNames.includes('echo'));
assert.ok(propertyNames.includes('readwriteValue'));
assert.ok(propertyNames.includes('readonlyValue'));
assert.ok(!propertyNames.includes('hiddenValue'));
assert.ok(!propertyNames.includes('readwriteAccessor1'));
assert.ok(!propertyNames.includes('readwriteAccessor2'));
assert.ok(!propertyNames.includes('readonlyAccessor1'));
assert.ok(!propertyNames.includes('readonlyAccessor2'));
// The napi_writable attribute should be ignored for accessors.
test_object.readwriteAccessor1 = 1;
assert.strictEqual(test_object.readwriteAccessor1, 1);
assert.strictEqual(test_object.readonlyAccessor1, 1);
assert.throws(() => { test_object.readonlyAccessor1 = 3; }, getterOnlyErrorRE);
test_object.readwriteAccessor2 = 2;
assert.strictEqual(test_object.readwriteAccessor2, 2);
assert.strictEqual(test_object.readonlyAccessor2, 2);
assert.throws(() => { test_object.readonlyAccessor2 = 3; }, getterOnlyErrorRE);
// Validate that static properties are on the class as opposed
// to the instance
assert.strictEqual(TestConstructor.staticReadonlyAccessor1, 10);
assert.strictEqual(test_object.staticReadonlyAccessor1, undefined);
// Verify that passing NULL to napi_define_class() results in the correct
// error.
assert.deepStrictEqual(TestConstructor.TestDefineClass(), {
envIsNull: 'Invalid argument',
nameIsNull: 'Invalid argument',
cbIsNull: 'Invalid argument',
cbDataIsNull: 'napi_ok',
propertiesIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
});

View File

@ -0,0 +1,8 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
// Testing api calls for a constructor that defines properties
const TestConstructor =
require(`./build/${common.buildType}/test_constructor`).constructorName;
assert.strictEqual(TestConstructor.name, 'MyObject');

View File

@ -0,0 +1,200 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
#include "test_null.h"
static double value_ = 1;
static double static_value_ = 10;
static napi_value TestDefineClass(napi_env env,
napi_callback_info info) {
napi_status status;
napi_value result, return_value;
napi_property_descriptor property_descriptor = {
"TestDefineClass",
NULL,
TestDefineClass,
NULL,
NULL,
NULL,
napi_enumerable | napi_static,
NULL};
NODE_API_CALL(env, napi_create_object(env, &return_value));
status = napi_define_class(NULL,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestDefineClass,
NULL,
1,
&property_descriptor,
&result);
add_returned_status(env,
"envIsNull",
return_value,
"Invalid argument",
napi_invalid_arg,
status);
napi_define_class(env,
NULL,
NAPI_AUTO_LENGTH,
TestDefineClass,
NULL,
1,
&property_descriptor,
&result);
add_last_status(env, "nameIsNull", return_value);
napi_define_class(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
NULL,
NULL,
1,
&property_descriptor,
&result);
add_last_status(env, "cbIsNull", return_value);
napi_define_class(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestDefineClass,
NULL,
1,
&property_descriptor,
&result);
add_last_status(env, "cbDataIsNull", return_value);
napi_define_class(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestDefineClass,
NULL,
1,
NULL,
&result);
add_last_status(env, "propertiesIsNull", return_value);
napi_define_class(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestDefineClass,
NULL,
1,
&property_descriptor,
NULL);
add_last_status(env, "resultIsNull", return_value);
return return_value;
}
static napi_value GetValue(napi_env env, napi_callback_info info) {
size_t argc = 0;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, NULL, NULL, NULL));
NODE_API_ASSERT(env, argc == 0, "Wrong number of arguments");
napi_value number;
NODE_API_CALL(env, napi_create_double(env, value_, &number));
return number;
}
static napi_value SetValue(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
NODE_API_CALL(env, napi_get_value_double(env, args[0], &value_));
return NULL;
}
static napi_value Echo(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
return args[0];
}
static napi_value New(napi_env env, napi_callback_info info) {
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &_this, NULL));
return _this;
}
static napi_value GetStaticValue(napi_env env, napi_callback_info info) {
size_t argc = 0;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, NULL, NULL, NULL));
NODE_API_ASSERT(env, argc == 0, "Wrong number of arguments");
napi_value number;
NODE_API_CALL(env, napi_create_double(env, static_value_, &number));
return number;
}
static napi_value NewExtra(napi_env env, napi_callback_info info) {
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &_this, NULL));
return _this;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_value number, cons;
NODE_API_CALL(env, napi_create_double(env, value_, &number));
NODE_API_CALL(env, napi_define_class(
env, "MyObject_Extra", 8, NewExtra, NULL, 0, NULL, &cons));
napi_property_descriptor properties[] = {
{ "echo", NULL, Echo, NULL, NULL, NULL, napi_enumerable, NULL },
{ "readwriteValue", NULL, NULL, NULL, NULL, number,
napi_enumerable | napi_writable, NULL },
{ "readonlyValue", NULL, NULL, NULL, NULL, number, napi_enumerable,
NULL },
{ "hiddenValue", NULL, NULL, NULL, NULL, number, napi_default, NULL },
{ "readwriteAccessor1", NULL, NULL, GetValue, SetValue, NULL, napi_default,
NULL },
{ "readwriteAccessor2", NULL, NULL, GetValue, SetValue, NULL,
napi_writable, NULL },
{ "readonlyAccessor1", NULL, NULL, GetValue, NULL, NULL, napi_default,
NULL },
{ "readonlyAccessor2", NULL, NULL, GetValue, NULL, NULL, napi_writable,
NULL },
{ "staticReadonlyAccessor1", NULL, NULL, GetStaticValue, NULL, NULL,
napi_default | napi_static, NULL},
{ "constructorName", NULL, NULL, NULL, NULL, cons,
napi_enumerable | napi_static, NULL },
{ "TestDefineClass", NULL, TestDefineClass, NULL, NULL, NULL,
napi_enumerable | napi_static, NULL },
};
NODE_API_CALL(env, napi_define_class(env, "MyObject", NAPI_AUTO_LENGTH, New,
NULL, sizeof(properties)/sizeof(*properties), properties, &cons));
init_test_null(env, cons);
return cons;
}
EXTERN_C_END

View File

@ -0,0 +1,111 @@
#include <js_native_api.h>
#include "../common.h"
#include "test_null.h"
static int some_data = 0;
static napi_value TestConstructor(napi_env env, napi_callback_info info) {
return NULL;
}
static napi_value TestDefineClass(napi_env env, napi_callback_info info) {
napi_value return_value, cons;
const napi_property_descriptor prop =
DECLARE_NODE_API_PROPERTY("testConstructor", TestConstructor);
NODE_API_CALL(env, napi_create_object(env, &return_value));
add_returned_status(env,
"envIsNull",
return_value,
"Invalid argument",
napi_invalid_arg,
napi_define_class(NULL,
"TestClass",
NAPI_AUTO_LENGTH,
TestConstructor,
&some_data,
1,
&prop,
&cons));
napi_define_class(env,
NULL,
NAPI_AUTO_LENGTH,
TestConstructor,
&some_data,
1,
&prop,
&cons);
add_last_status(env, "nameIsNull", return_value);
napi_define_class(
env, "TestClass", 0, TestConstructor, &some_data, 1, &prop, &cons);
add_last_status(env, "lengthIsZero", return_value);
napi_define_class(
env, "TestClass", NAPI_AUTO_LENGTH, NULL, &some_data, 1, &prop, &cons);
add_last_status(env, "nativeSideIsNull", return_value);
napi_define_class(env,
"TestClass",
NAPI_AUTO_LENGTH,
TestConstructor,
NULL,
1,
&prop,
&cons);
add_last_status(env, "dataIsNull", return_value);
napi_define_class(env,
"TestClass",
NAPI_AUTO_LENGTH,
TestConstructor,
&some_data,
0,
&prop,
&cons);
add_last_status(env, "propsLengthIsZero", return_value);
napi_define_class(env,
"TestClass",
NAPI_AUTO_LENGTH,
TestConstructor,
&some_data,
1,
NULL,
&cons);
add_last_status(env, "propsIsNull", return_value);
napi_define_class(env,
"TestClass",
NAPI_AUTO_LENGTH,
TestConstructor,
&some_data,
1,
&prop,
NULL);
add_last_status(env, "resultIsNull", return_value);
return return_value;
}
void init_test_null(napi_env env, napi_value exports) {
napi_value test_null;
const napi_property_descriptor test_null_props[] = {
DECLARE_NODE_API_PROPERTY("testDefineClass", TestDefineClass),
};
NODE_API_CALL_RETURN_VOID(env, napi_create_object(env, &test_null));
NODE_API_CALL_RETURN_VOID(
env,
napi_define_properties(env,
test_null,
sizeof(test_null_props) / sizeof(*test_null_props),
test_null_props));
NODE_API_CALL_RETURN_VOID(
env, napi_set_named_property(env, exports, "testNull", test_null));
}

View File

@ -0,0 +1,8 @@
#ifndef TEST_JS_NATIVE_API_TEST_OBJECT_TEST_NULL_H_
#define TEST_JS_NATIVE_API_TEST_OBJECT_TEST_NULL_H_
#include <js_native_api.h>
void init_test_null(napi_env env, napi_value exports);
#endif // TEST_JS_NATIVE_API_TEST_OBJECT_TEST_NULL_H_

View File

@ -0,0 +1,18 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
// Test passing NULL to object-related N-APIs.
const { testNull } = require(`./build/${common.buildType}/test_constructor`);
const expectedResult = {
envIsNull: 'Invalid argument',
nameIsNull: 'Invalid argument',
lengthIsZero: 'napi_ok',
nativeSideIsNull: 'Invalid argument',
dataIsNull: 'napi_ok',
propsLengthIsZero: 'napi_ok',
propsIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
};
assert.deepStrictEqual(testNull.testDefineClass(), expectedResult);

View File

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_conversions",
"sources": [
"test_conversions.c",
"test_null.c",
]
}
]
}

View File

@ -0,0 +1,218 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const test = require(`./build/${common.buildType}/test_conversions`);
const boolExpected = /boolean was expected/;
const numberExpected = /number was expected/;
const stringExpected = /string was expected/;
const testSym = Symbol('test');
assert.strictEqual(test.asBool(false), false);
assert.strictEqual(test.asBool(true), true);
assert.throws(() => test.asBool(undefined), boolExpected);
assert.throws(() => test.asBool(null), boolExpected);
assert.throws(() => test.asBool(Number.NaN), boolExpected);
assert.throws(() => test.asBool(0), boolExpected);
assert.throws(() => test.asBool(''), boolExpected);
assert.throws(() => test.asBool('0'), boolExpected);
assert.throws(() => test.asBool(1), boolExpected);
assert.throws(() => test.asBool('1'), boolExpected);
assert.throws(() => test.asBool('true'), boolExpected);
assert.throws(() => test.asBool({}), boolExpected);
assert.throws(() => test.asBool([]), boolExpected);
assert.throws(() => test.asBool(testSym), boolExpected);
[test.asInt32, test.asUInt32, test.asInt64].forEach((asInt) => {
assert.strictEqual(asInt(0), 0);
assert.strictEqual(asInt(1), 1);
assert.strictEqual(asInt(1.0), 1);
assert.strictEqual(asInt(1.1), 1);
assert.strictEqual(asInt(1.9), 1);
assert.strictEqual(asInt(0.9), 0);
assert.strictEqual(asInt(999.9), 999);
assert.strictEqual(asInt(Number.NaN), 0);
assert.throws(() => asInt(undefined), numberExpected);
assert.throws(() => asInt(null), numberExpected);
assert.throws(() => asInt(false), numberExpected);
assert.throws(() => asInt(''), numberExpected);
assert.throws(() => asInt('1'), numberExpected);
assert.throws(() => asInt({}), numberExpected);
assert.throws(() => asInt([]), numberExpected);
assert.throws(() => asInt(testSym), numberExpected);
});
assert.strictEqual(test.asInt32(-1), -1);
assert.strictEqual(test.asInt64(-1), -1);
assert.strictEqual(test.asUInt32(-1), Math.pow(2, 32) - 1);
assert.strictEqual(test.asDouble(0), 0);
assert.strictEqual(test.asDouble(1), 1);
assert.strictEqual(test.asDouble(1.0), 1.0);
assert.strictEqual(test.asDouble(1.1), 1.1);
assert.strictEqual(test.asDouble(1.9), 1.9);
assert.strictEqual(test.asDouble(0.9), 0.9);
assert.strictEqual(test.asDouble(999.9), 999.9);
assert.strictEqual(test.asDouble(-1), -1);
assert.ok(Number.isNaN(test.asDouble(Number.NaN)));
assert.throws(() => test.asDouble(undefined), numberExpected);
assert.throws(() => test.asDouble(null), numberExpected);
assert.throws(() => test.asDouble(false), numberExpected);
assert.throws(() => test.asDouble(''), numberExpected);
assert.throws(() => test.asDouble('1'), numberExpected);
assert.throws(() => test.asDouble({}), numberExpected);
assert.throws(() => test.asDouble([]), numberExpected);
assert.throws(() => test.asDouble(testSym), numberExpected);
assert.strictEqual(test.asString(''), '');
assert.strictEqual(test.asString('test'), 'test');
assert.throws(() => test.asString(undefined), stringExpected);
assert.throws(() => test.asString(null), stringExpected);
assert.throws(() => test.asString(false), stringExpected);
assert.throws(() => test.asString(1), stringExpected);
assert.throws(() => test.asString(1.1), stringExpected);
assert.throws(() => test.asString(Number.NaN), stringExpected);
assert.throws(() => test.asString({}), stringExpected);
assert.throws(() => test.asString([]), stringExpected);
assert.throws(() => test.asString(testSym), stringExpected);
assert.strictEqual(test.toBool(true), true);
assert.strictEqual(test.toBool(1), true);
assert.strictEqual(test.toBool(-1), true);
assert.strictEqual(test.toBool('true'), true);
assert.strictEqual(test.toBool('false'), true);
assert.strictEqual(test.toBool({}), true);
assert.strictEqual(test.toBool([]), true);
assert.strictEqual(test.toBool(testSym), true);
assert.strictEqual(test.toBool(false), false);
assert.strictEqual(test.toBool(undefined), false);
assert.strictEqual(test.toBool(null), false);
assert.strictEqual(test.toBool(0), false);
assert.strictEqual(test.toBool(Number.NaN), false);
assert.strictEqual(test.toBool(''), false);
assert.strictEqual(test.toNumber(0), 0);
assert.strictEqual(test.toNumber(1), 1);
assert.strictEqual(test.toNumber(1.1), 1.1);
assert.strictEqual(test.toNumber(-1), -1);
assert.strictEqual(test.toNumber('0'), 0);
assert.strictEqual(test.toNumber('1'), 1);
assert.strictEqual(test.toNumber('1.1'), 1.1);
assert.strictEqual(test.toNumber([]), 0);
assert.strictEqual(test.toNumber(false), 0);
assert.strictEqual(test.toNumber(null), 0);
assert.strictEqual(test.toNumber(''), 0);
assert.ok(Number.isNaN(test.toNumber(Number.NaN)));
assert.ok(Number.isNaN(test.toNumber({})));
assert.ok(Number.isNaN(test.toNumber(undefined)));
assert.throws(() => test.toNumber(testSym), TypeError);
assert.deepStrictEqual({}, test.toObject({}));
assert.deepStrictEqual({ 'test': 1 }, test.toObject({ 'test': 1 }));
assert.deepStrictEqual([], test.toObject([]));
assert.deepStrictEqual([ 1, 2, 3 ], test.toObject([ 1, 2, 3 ]));
assert.deepStrictEqual(new Boolean(false), test.toObject(false));
assert.deepStrictEqual(new Boolean(true), test.toObject(true));
assert.deepStrictEqual(new String(''), test.toObject(''));
assert.deepStrictEqual(new Number(0), test.toObject(0));
assert.deepStrictEqual(new Number(Number.NaN), test.toObject(Number.NaN));
assert.deepStrictEqual(new Object(testSym), test.toObject(testSym));
assert.notStrictEqual(test.toObject(false), false);
assert.notStrictEqual(test.toObject(true), true);
assert.notStrictEqual(test.toObject(''), '');
assert.notStrictEqual(test.toObject(0), 0);
assert.ok(!Number.isNaN(test.toObject(Number.NaN)));
assert.strictEqual(test.toString(''), '');
assert.strictEqual(test.toString('test'), 'test');
assert.strictEqual(test.toString(undefined), 'undefined');
assert.strictEqual(test.toString(null), 'null');
assert.strictEqual(test.toString(false), 'false');
assert.strictEqual(test.toString(true), 'true');
assert.strictEqual(test.toString(0), '0');
assert.strictEqual(test.toString(1.1), '1.1');
assert.strictEqual(test.toString(Number.NaN), 'NaN');
assert.strictEqual(test.toString({}), '[object Object]');
assert.strictEqual(test.toString({ toString: () => 'test' }), 'test');
assert.strictEqual(test.toString([]), '');
assert.strictEqual(test.toString([ 1, 2, 3 ]), '1,2,3');
assert.throws(() => test.toString(testSym), TypeError);
assert.deepStrictEqual(test.testNull.getValueBool(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'A boolean was expected',
});
assert.deepStrictEqual(test.testNull.getValueInt32(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'A number was expected',
});
assert.deepStrictEqual(test.testNull.getValueUint32(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'A number was expected',
});
assert.deepStrictEqual(test.testNull.getValueInt64(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'A number was expected',
});
assert.deepStrictEqual(test.testNull.getValueDouble(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'A number was expected',
});
assert.deepStrictEqual(test.testNull.coerceToBool(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'napi_ok',
});
assert.deepStrictEqual(test.testNull.coerceToObject(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'napi_ok',
});
assert.deepStrictEqual(test.testNull.coerceToString(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'napi_ok',
});
assert.deepStrictEqual(test.testNull.getValueStringUtf8(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
wrongTypeIn: 'A string was expected',
bufAndOutLengthIsNull: 'Invalid argument',
});
assert.deepStrictEqual(test.testNull.getValueStringLatin1(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
wrongTypeIn: 'A string was expected',
bufAndOutLengthIsNull: 'Invalid argument',
});
assert.deepStrictEqual(test.testNull.getValueStringUtf16(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
wrongTypeIn: 'A string was expected',
bufAndOutLengthIsNull: 'Invalid argument',
});

View File

@ -0,0 +1,158 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
#include "test_null.h"
static napi_value AsBool(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
bool value;
NODE_API_CALL(env, napi_get_value_bool(env, args[0], &value));
napi_value output;
NODE_API_CALL(env, napi_get_boolean(env, value, &output));
return output;
}
static napi_value AsInt32(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
int32_t value;
NODE_API_CALL(env, napi_get_value_int32(env, args[0], &value));
napi_value output;
NODE_API_CALL(env, napi_create_int32(env, value, &output));
return output;
}
static napi_value AsUInt32(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
uint32_t value;
NODE_API_CALL(env, napi_get_value_uint32(env, args[0], &value));
napi_value output;
NODE_API_CALL(env, napi_create_uint32(env, value, &output));
return output;
}
static napi_value AsInt64(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
int64_t value;
NODE_API_CALL(env, napi_get_value_int64(env, args[0], &value));
napi_value output;
NODE_API_CALL(env, napi_create_int64(env, (double)value, &output));
return output;
}
static napi_value AsDouble(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
double value;
NODE_API_CALL(env, napi_get_value_double(env, args[0], &value));
napi_value output;
NODE_API_CALL(env, napi_create_double(env, value, &output));
return output;
}
static napi_value AsString(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
char value[100];
NODE_API_CALL(env,
napi_get_value_string_utf8(env, args[0], value, sizeof(value), NULL));
napi_value output;
NODE_API_CALL(env, napi_create_string_utf8(
env, value, NAPI_AUTO_LENGTH, &output));
return output;
}
static napi_value ToBool(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value output;
NODE_API_CALL(env, napi_coerce_to_bool(env, args[0], &output));
return output;
}
static napi_value ToNumber(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value output;
NODE_API_CALL(env, napi_coerce_to_number(env, args[0], &output));
return output;
}
static napi_value ToObject(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value output;
NODE_API_CALL(env, napi_coerce_to_object(env, args[0], &output));
return output;
}
static napi_value ToString(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value output;
NODE_API_CALL(env, napi_coerce_to_string(env, args[0], &output));
return output;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("asBool", AsBool),
DECLARE_NODE_API_PROPERTY("asInt32", AsInt32),
DECLARE_NODE_API_PROPERTY("asUInt32", AsUInt32),
DECLARE_NODE_API_PROPERTY("asInt64", AsInt64),
DECLARE_NODE_API_PROPERTY("asDouble", AsDouble),
DECLARE_NODE_API_PROPERTY("asString", AsString),
DECLARE_NODE_API_PROPERTY("toBool", ToBool),
DECLARE_NODE_API_PROPERTY("toNumber", ToNumber),
DECLARE_NODE_API_PROPERTY("toObject", ToObject),
DECLARE_NODE_API_PROPERTY("toString", ToString),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
init_test_null(env, exports);
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,102 @@
#include <js_native_api.h>
#include "../common.h"
#include "test_null.h"
#define GEN_NULL_CHECK_BINDING(binding_name, output_type, api) \
static napi_value binding_name(napi_env env, napi_callback_info info) { \
napi_value return_value; \
output_type result; \
NODE_API_CALL(env, napi_create_object(env, &return_value)); \
add_returned_status(env, \
"envIsNull", \
return_value, \
"Invalid argument", \
napi_invalid_arg, \
api(NULL, return_value, &result)); \
api(env, NULL, &result); \
add_last_status(env, "valueIsNull", return_value); \
api(env, return_value, NULL); \
add_last_status(env, "resultIsNull", return_value); \
api(env, return_value, &result); \
add_last_status(env, "inputTypeCheck", return_value); \
return return_value; \
}
GEN_NULL_CHECK_BINDING(GetValueBool, bool, napi_get_value_bool)
GEN_NULL_CHECK_BINDING(GetValueInt32, int32_t, napi_get_value_int32)
GEN_NULL_CHECK_BINDING(GetValueUint32, uint32_t, napi_get_value_uint32)
GEN_NULL_CHECK_BINDING(GetValueInt64, int64_t, napi_get_value_int64)
GEN_NULL_CHECK_BINDING(GetValueDouble, double, napi_get_value_double)
GEN_NULL_CHECK_BINDING(CoerceToBool, napi_value, napi_coerce_to_bool)
GEN_NULL_CHECK_BINDING(CoerceToObject, napi_value, napi_coerce_to_object)
GEN_NULL_CHECK_BINDING(CoerceToString, napi_value, napi_coerce_to_string)
#define GEN_NULL_CHECK_STRING_BINDING(binding_name, arg_type, api) \
static napi_value binding_name(napi_env env, napi_callback_info info) { \
napi_value return_value; \
NODE_API_CALL(env, napi_create_object(env, &return_value)); \
arg_type buf1[4]; \
size_t length1 = 3; \
add_returned_status(env, \
"envIsNull", \
return_value, \
"Invalid argument", \
napi_invalid_arg, \
api(NULL, return_value, buf1, length1, &length1)); \
arg_type buf2[4]; \
size_t length2 = 3; \
api(env, NULL, buf2, length2, &length2); \
add_last_status(env, "valueIsNull", return_value); \
api(env, return_value, NULL, 3, NULL); \
add_last_status(env, "wrongTypeIn", return_value); \
napi_value string; \
NODE_API_CALL(env, \
napi_create_string_utf8(env, \
"Something", \
NAPI_AUTO_LENGTH, \
&string)); \
api(env, string, NULL, 3, NULL); \
add_last_status(env, "bufAndOutLengthIsNull", return_value); \
return return_value; \
}
GEN_NULL_CHECK_STRING_BINDING(GetValueStringUtf8,
char,
napi_get_value_string_utf8)
GEN_NULL_CHECK_STRING_BINDING(GetValueStringLatin1,
char,
napi_get_value_string_latin1)
GEN_NULL_CHECK_STRING_BINDING(GetValueStringUtf16,
char16_t,
napi_get_value_string_utf16)
void init_test_null(napi_env env, napi_value exports) {
napi_value test_null;
const napi_property_descriptor test_null_props[] = {
DECLARE_NODE_API_PROPERTY("getValueBool", GetValueBool),
DECLARE_NODE_API_PROPERTY("getValueInt32", GetValueInt32),
DECLARE_NODE_API_PROPERTY("getValueUint32", GetValueUint32),
DECLARE_NODE_API_PROPERTY("getValueInt64", GetValueInt64),
DECLARE_NODE_API_PROPERTY("getValueDouble", GetValueDouble),
DECLARE_NODE_API_PROPERTY("coerceToBool", CoerceToBool),
DECLARE_NODE_API_PROPERTY("coerceToObject", CoerceToObject),
DECLARE_NODE_API_PROPERTY("coerceToString", CoerceToString),
DECLARE_NODE_API_PROPERTY("getValueStringUtf8", GetValueStringUtf8),
DECLARE_NODE_API_PROPERTY("getValueStringLatin1", GetValueStringLatin1),
DECLARE_NODE_API_PROPERTY("getValueStringUtf16", GetValueStringUtf16),
};
NODE_API_CALL_RETURN_VOID(env, napi_create_object(env, &test_null));
NODE_API_CALL_RETURN_VOID(env, napi_define_properties(
env, test_null, sizeof(test_null_props) / sizeof(*test_null_props),
test_null_props));
const napi_property_descriptor test_null_set = {
"testNull", NULL, NULL, NULL, NULL, test_null, napi_enumerable, NULL
};
NODE_API_CALL_RETURN_VOID(env,
napi_define_properties(env, exports, 1, &test_null_set));
}

View File

@ -0,0 +1,8 @@
#ifndef TEST_JS_NATIVE_API_TEST_CONVERSIONS_TEST_NULL_H_
#define TEST_JS_NATIVE_API_TEST_CONVERSIONS_TEST_NULL_H_
#include <js_native_api.h>
void init_test_null(napi_env env, napi_value exports);
#endif // TEST_JS_NATIVE_API_TEST_CONVERSIONS_TEST_NULL_H_

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "test_dataview",
"sources": [
"test_dataview.c"
]
}
]
}

View File

@ -0,0 +1,24 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
// Testing api calls for arrays
const test_dataview = require(`./build/${common.buildType}/test_dataview`);
// Test for creating dataview
{
const buffer = new ArrayBuffer(128);
const template = Reflect.construct(DataView, [buffer]);
const theDataview = test_dataview.CreateDataViewFromJSDataView(template);
assert.ok(theDataview instanceof DataView,
`Expect ${theDataview} to be a DataView`);
}
// Test for creating dataview with invalid range
{
const buffer = new ArrayBuffer(128);
assert.throws(() => {
test_dataview.CreateDataView(buffer, 10, 200);
}, RangeError);
}

View File

@ -0,0 +1,102 @@
#include <js_native_api.h>
#include <string.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value CreateDataView(napi_env env, napi_callback_info info) {
size_t argc = 3;
napi_value args [3];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc == 3, "Wrong number of arguments");
napi_valuetype valuetype0;
napi_value arraybuffer = args[0];
NODE_API_CALL(env, napi_typeof(env, arraybuffer, &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_object,
"Wrong type of arguments. Expects a ArrayBuffer as the first "
"argument.");
bool is_arraybuffer;
NODE_API_CALL(env, napi_is_arraybuffer(env, arraybuffer, &is_arraybuffer));
NODE_API_ASSERT(env, is_arraybuffer,
"Wrong type of arguments. Expects a ArrayBuffer as the first "
"argument.");
napi_valuetype valuetype1;
NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
NODE_API_ASSERT(env, valuetype1 == napi_number,
"Wrong type of arguments. Expects a number as second argument.");
size_t byte_offset = 0;
NODE_API_CALL(env, napi_get_value_uint32(env, args[1], (uint32_t*)(&byte_offset)));
napi_valuetype valuetype2;
NODE_API_CALL(env, napi_typeof(env, args[2], &valuetype2));
NODE_API_ASSERT(env, valuetype2 == napi_number,
"Wrong type of arguments. Expects a number as third argument.");
size_t length = 0;
NODE_API_CALL(env, napi_get_value_uint32(env, args[2], (uint32_t*)(&length)));
napi_value output_dataview;
NODE_API_CALL(env,
napi_create_dataview(env, length, arraybuffer,
byte_offset, &output_dataview));
return output_dataview;
}
static napi_value CreateDataViewFromJSDataView(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args [1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
napi_valuetype valuetype;
napi_value input_dataview = args[0];
NODE_API_CALL(env, napi_typeof(env, input_dataview, &valuetype));
NODE_API_ASSERT(env, valuetype == napi_object,
"Wrong type of arguments. Expects a DataView as the first "
"argument.");
bool is_dataview;
NODE_API_CALL(env, napi_is_dataview(env, input_dataview, &is_dataview));
NODE_API_ASSERT(env, is_dataview,
"Wrong type of arguments. Expects a DataView as the first "
"argument.");
size_t byte_offset = 0;
size_t length = 0;
napi_value buffer;
NODE_API_CALL(env,
napi_get_dataview_info(env, input_dataview, &length, NULL,
&buffer, &byte_offset));
napi_value output_dataview;
NODE_API_CALL(env,
napi_create_dataview(env, length, buffer,
byte_offset, &output_dataview));
return output_dataview;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("CreateDataView", CreateDataView),
DECLARE_NODE_API_PROPERTY("CreateDataViewFromJSDataView",
CreateDataViewFromJSDataView)
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "test_date",
"sources": [
"test_date.c"
]
}
]
}

View File

@ -0,0 +1,21 @@
'use strict';
const common = require('../../common');
// This tests the date-related n-api calls
const assert = require('assert');
const test_date = require(`./build/${common.buildType}/test_date`);
const dateTypeTestDate = test_date.createDate(1549183351);
assert.strictEqual(test_date.isDate(dateTypeTestDate), true);
assert.strictEqual(test_date.isDate(new Date(1549183351)), true);
assert.strictEqual(test_date.isDate(2.4), false);
assert.strictEqual(test_date.isDate('not a date'), false);
assert.strictEqual(test_date.isDate(undefined), false);
assert.strictEqual(test_date.isDate(null), false);
assert.strictEqual(test_date.isDate({}), false);
assert.strictEqual(test_date.getDateValue(new Date(1549183351)), 1549183351);

View File

@ -0,0 +1,64 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value createDate(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_number,
"Wrong type of arguments. Expects a number as first argument.");
double time;
NODE_API_CALL(env, napi_get_value_double(env, args[0], &time));
napi_value date;
NODE_API_CALL(env, napi_create_date(env, time, &date));
return date;
}
static napi_value isDate(napi_env env, napi_callback_info info) {
napi_value date, result;
size_t argc = 1;
bool is_date;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &date, NULL, NULL));
NODE_API_CALL(env, napi_is_date(env, date, &is_date));
NODE_API_CALL(env, napi_get_boolean(env, is_date, &result));
return result;
}
static napi_value getDateValue(napi_env env, napi_callback_info info) {
napi_value date, result;
size_t argc = 1;
double value;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &date, NULL, NULL));
NODE_API_CALL(env, napi_get_date_value(env, date, &value));
NODE_API_CALL(env, napi_create_double(env, value, &result));
return result;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("createDate", createDate),
DECLARE_NODE_API_PROPERTY("isDate", isDate),
DECLARE_NODE_API_PROPERTY("getDateValue", getDateValue),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "test_error",
"sources": [
"test_error.c"
]
}
]
}

View File

@ -0,0 +1,148 @@
'use strict';
const common = require('../../common');
const test_error = require(`./build/${common.buildType}/test_error`);
const assert = require('assert');
const theError = new Error('Some error');
const theTypeError = new TypeError('Some type error');
const theSyntaxError = new SyntaxError('Some syntax error');
const theRangeError = new RangeError('Some type error');
const theReferenceError = new ReferenceError('Some reference error');
const theURIError = new URIError('Some URI error');
const theEvalError = new EvalError('Some eval error');
class MyError extends Error { }
const myError = new MyError('Some MyError');
// Test that native error object is correctly classed
assert.strictEqual(test_error.checkError(theError), true);
// Test that native type error object is correctly classed
assert.strictEqual(test_error.checkError(theTypeError), true);
// Test that native syntax error object is correctly classed
assert.strictEqual(test_error.checkError(theSyntaxError), true);
// Test that native range error object is correctly classed
assert.strictEqual(test_error.checkError(theRangeError), true);
// Test that native reference error object is correctly classed
assert.strictEqual(test_error.checkError(theReferenceError), true);
// Test that native URI error object is correctly classed
assert.strictEqual(test_error.checkError(theURIError), true);
// Test that native eval error object is correctly classed
assert.strictEqual(test_error.checkError(theEvalError), true);
// Test that class derived from native error is correctly classed
assert.strictEqual(test_error.checkError(myError), true);
// Test that non-error object is correctly classed
assert.strictEqual(test_error.checkError({}), false);
// Test that non-error primitive is correctly classed
assert.strictEqual(test_error.checkError('non-object'), false);
assert.throws(() => {
test_error.throwExistingError();
}, /^Error: existing error$/);
assert.throws(() => {
test_error.throwError();
}, /^Error: error$/);
assert.throws(() => {
test_error.throwRangeError();
}, /^RangeError: range error$/);
assert.throws(() => {
test_error.throwTypeError();
}, /^TypeError: type error$/);
assert.throws(() => {
test_error.throwSyntaxError();
}, /^SyntaxError: syntax error$/);
[42, {}, [], Symbol('xyzzy'), true, 'ball', undefined, null, NaN]
.forEach((value) => assert.throws(
() => test_error.throwArbitrary(value),
(err) => {
assert.strictEqual(err, value);
return true;
},
));
assert.throws(
() => test_error.throwErrorCode(),
{
code: 'ERR_TEST_CODE',
message: 'Error [error]',
});
assert.throws(
() => test_error.throwRangeErrorCode(),
{
code: 'ERR_TEST_CODE',
message: 'RangeError [range error]',
});
assert.throws(
() => test_error.throwTypeErrorCode(),
{
code: 'ERR_TEST_CODE',
message: 'TypeError [type error]',
});
assert.throws(
() => test_error.throwSyntaxErrorCode(),
{
code: 'ERR_TEST_CODE',
message: 'SyntaxError [syntax error]',
});
let error = test_error.createError();
assert.ok(error instanceof Error, 'expected error to be an instance of Error');
assert.strictEqual(error.message, 'error');
error = test_error.createRangeError();
assert.ok(error instanceof RangeError,
'expected error to be an instance of RangeError');
assert.strictEqual(error.message, 'range error');
error = test_error.createTypeError();
assert.ok(error instanceof TypeError,
'expected error to be an instance of TypeError');
assert.strictEqual(error.message, 'type error');
error = test_error.createSyntaxError();
assert.ok(error instanceof SyntaxError,
'expected error to be an instance of SyntaxError');
assert.strictEqual(error.message, 'syntax error');
error = test_error.createErrorCode();
assert.ok(error instanceof Error, 'expected error to be an instance of Error');
assert.strictEqual(error.code, 'ERR_TEST_CODE');
assert.strictEqual(error.message, 'Error [error]');
assert.strictEqual(error.name, 'Error');
error = test_error.createRangeErrorCode();
assert.ok(error instanceof RangeError,
'expected error to be an instance of RangeError');
assert.strictEqual(error.message, 'RangeError [range error]');
assert.strictEqual(error.code, 'ERR_TEST_CODE');
assert.strictEqual(error.name, 'RangeError');
error = test_error.createTypeErrorCode();
assert.ok(error instanceof TypeError,
'expected error to be an instance of TypeError');
assert.strictEqual(error.message, 'TypeError [type error]');
assert.strictEqual(error.code, 'ERR_TEST_CODE');
assert.strictEqual(error.name, 'TypeError');
error = test_error.createSyntaxErrorCode();
assert.ok(error instanceof SyntaxError,
'expected error to be an instance of SyntaxError');
assert.strictEqual(error.message, 'SyntaxError [syntax error]');
assert.strictEqual(error.code, 'ERR_TEST_CODE');
assert.strictEqual(error.name, 'SyntaxError');

View File

@ -0,0 +1,197 @@
#define NAPI_VERSION 9
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value checkError(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
bool r;
NODE_API_CALL(env, napi_is_error(env, args[0], &r));
napi_value result;
NODE_API_CALL(env, napi_get_boolean(env, r, &result));
return result;
}
static napi_value throwExistingError(napi_env env, napi_callback_info info) {
napi_value message;
napi_value error;
NODE_API_CALL(env, napi_create_string_utf8(
env, "existing error", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_error(env, NULL, message, &error));
NODE_API_CALL(env, napi_throw(env, error));
return NULL;
}
static napi_value throwError(napi_env env, napi_callback_info info) {
NODE_API_CALL(env, napi_throw_error(env, NULL, "error"));
return NULL;
}
static napi_value throwRangeError(napi_env env, napi_callback_info info) {
NODE_API_CALL(env, napi_throw_range_error(env, NULL, "range error"));
return NULL;
}
static napi_value throwTypeError(napi_env env, napi_callback_info info) {
NODE_API_CALL(env, napi_throw_type_error(env, NULL, "type error"));
return NULL;
}
static napi_value throwSyntaxError(napi_env env, napi_callback_info info) {
NODE_API_CALL(env, node_api_throw_syntax_error(env, NULL, "syntax error"));
return NULL;
}
static napi_value throwErrorCode(napi_env env, napi_callback_info info) {
NODE_API_CALL(env, napi_throw_error(env, "ERR_TEST_CODE", "Error [error]"));
return NULL;
}
static napi_value throwRangeErrorCode(napi_env env, napi_callback_info info) {
NODE_API_CALL(env,
napi_throw_range_error(env, "ERR_TEST_CODE", "RangeError [range error]"));
return NULL;
}
static napi_value throwTypeErrorCode(napi_env env, napi_callback_info info) {
NODE_API_CALL(env,
napi_throw_type_error(env, "ERR_TEST_CODE", "TypeError [type error]"));
return NULL;
}
static napi_value throwSyntaxErrorCode(napi_env env, napi_callback_info info) {
NODE_API_CALL(env,
node_api_throw_syntax_error(env, "ERR_TEST_CODE", "SyntaxError [syntax error]"));
return NULL;
}
static napi_value createError(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
NODE_API_CALL(env, napi_create_string_utf8(
env, "error", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_error(env, NULL, message, &result));
return result;
}
static napi_value createRangeError(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
NODE_API_CALL(env, napi_create_string_utf8(
env, "range error", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_range_error(env, NULL, message, &result));
return result;
}
static napi_value createTypeError(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
NODE_API_CALL(env, napi_create_string_utf8(
env, "type error", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_type_error(env, NULL, message, &result));
return result;
}
static napi_value createSyntaxError(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
NODE_API_CALL(env, napi_create_string_utf8(
env, "syntax error", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, node_api_create_syntax_error(env, NULL, message, &result));
return result;
}
static napi_value createErrorCode(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
napi_value code;
NODE_API_CALL(env, napi_create_string_utf8(
env, "Error [error]", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_string_utf8(
env, "ERR_TEST_CODE", NAPI_AUTO_LENGTH, &code));
NODE_API_CALL(env, napi_create_error(env, code, message, &result));
return result;
}
static napi_value createRangeErrorCode(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
napi_value code;
NODE_API_CALL(env,
napi_create_string_utf8(
env, "RangeError [range error]", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_string_utf8(
env, "ERR_TEST_CODE", NAPI_AUTO_LENGTH, &code));
NODE_API_CALL(env, napi_create_range_error(env, code, message, &result));
return result;
}
static napi_value createTypeErrorCode(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
napi_value code;
NODE_API_CALL(env,
napi_create_string_utf8(
env, "TypeError [type error]", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_string_utf8(
env, "ERR_TEST_CODE", NAPI_AUTO_LENGTH, &code));
NODE_API_CALL(env, napi_create_type_error(env, code, message, &result));
return result;
}
static napi_value createSyntaxErrorCode(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
napi_value code;
NODE_API_CALL(env,
napi_create_string_utf8(
env, "SyntaxError [syntax error]", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_string_utf8(
env, "ERR_TEST_CODE", NAPI_AUTO_LENGTH, &code));
NODE_API_CALL(env, node_api_create_syntax_error(env, code, message, &result));
return result;
}
static napi_value throwArbitrary(napi_env env, napi_callback_info info) {
napi_value arbitrary;
size_t argc = 1;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &arbitrary, NULL, NULL));
NODE_API_CALL(env, napi_throw(env, arbitrary));
return NULL;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("checkError", checkError),
DECLARE_NODE_API_PROPERTY("throwExistingError", throwExistingError),
DECLARE_NODE_API_PROPERTY("throwError", throwError),
DECLARE_NODE_API_PROPERTY("throwRangeError", throwRangeError),
DECLARE_NODE_API_PROPERTY("throwTypeError", throwTypeError),
DECLARE_NODE_API_PROPERTY("throwSyntaxError", throwSyntaxError),
DECLARE_NODE_API_PROPERTY("throwErrorCode", throwErrorCode),
DECLARE_NODE_API_PROPERTY("throwRangeErrorCode", throwRangeErrorCode),
DECLARE_NODE_API_PROPERTY("throwTypeErrorCode", throwTypeErrorCode),
DECLARE_NODE_API_PROPERTY("throwSyntaxErrorCode", throwSyntaxErrorCode),
DECLARE_NODE_API_PROPERTY("throwArbitrary", throwArbitrary),
DECLARE_NODE_API_PROPERTY("createError", createError),
DECLARE_NODE_API_PROPERTY("createRangeError", createRangeError),
DECLARE_NODE_API_PROPERTY("createTypeError", createTypeError),
DECLARE_NODE_API_PROPERTY("createSyntaxError", createSyntaxError),
DECLARE_NODE_API_PROPERTY("createErrorCode", createErrorCode),
DECLARE_NODE_API_PROPERTY("createRangeErrorCode", createRangeErrorCode),
DECLARE_NODE_API_PROPERTY("createTypeErrorCode", createTypeErrorCode),
DECLARE_NODE_API_PROPERTY("createSyntaxErrorCode", createSyntaxErrorCode),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "test_exception",
"sources": [
"test_exception.c"
]
}
]
}

View File

@ -0,0 +1,115 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const assert = require('assert');
const theError = new Error('Some error');
// The test module throws an error during Init, but in order for its exports to
// not be lost, it attaches them to the error's "bindings" property. This way,
// we can make sure that exceptions thrown during the module initialization
// phase are propagated through require() into JavaScript.
// https://github.com/nodejs/node/issues/19437
const test_exception = (function() {
let resultingException;
try {
require(`./build/${common.buildType}/test_exception`);
} catch (anException) {
resultingException = anException;
}
assert.strictEqual(resultingException.message, 'Error during Init');
return resultingException.binding;
})();
{
const throwTheError = () => { throw theError; };
// Test that the native side successfully captures the exception
let returnedError = test_exception.returnException(throwTheError);
assert.strictEqual(returnedError, theError);
// Test that the native side passes the exception through
assert.throws(
() => { test_exception.allowException(throwTheError); },
(err) => err === theError,
);
// Test that the exception thrown above was marked as pending
// before it was handled on the JS side
const exception_pending = test_exception.wasPending();
assert.strictEqual(exception_pending, true,
'Exception not pending as expected,' +
` .wasPending() returned ${exception_pending}`);
// Test that the native side does not capture a non-existing exception
returnedError = test_exception.returnException(common.mustCall());
assert.strictEqual(returnedError, undefined,
'Returned error should be undefined when no exception is' +
` thrown, but ${returnedError} was passed`);
}
{
const throwTheError = class { constructor() { throw theError; } };
// Test that the native side successfully captures the exception
let returnedError = test_exception.constructReturnException(throwTheError);
assert.strictEqual(returnedError, theError);
// Test that the native side passes the exception through
assert.throws(
() => { test_exception.constructAllowException(throwTheError); },
(err) => err === theError,
);
// Test that the exception thrown above was marked as pending
// before it was handled on the JS side
const exception_pending = test_exception.wasPending();
assert.strictEqual(exception_pending, true,
'Exception not pending as expected,' +
` .wasPending() returned ${exception_pending}`);
// Test that the native side does not capture a non-existing exception
returnedError = test_exception.constructReturnException(common.mustCall());
assert.strictEqual(returnedError, undefined,
'Returned error should be undefined when no exception is' +
` thrown, but ${returnedError} was passed`);
}
{
// Test that no exception appears that was not thrown by us
let caughtError;
try {
test_exception.allowException(common.mustCall());
} catch (anError) {
caughtError = anError;
}
assert.strictEqual(caughtError, undefined,
'No exception originated on the native side, but' +
` ${caughtError} was passed`);
// Test that the exception state remains clear when no exception is thrown
const exception_pending = test_exception.wasPending();
assert.strictEqual(exception_pending, false,
'Exception state did not remain clear as expected,' +
` .wasPending() returned ${exception_pending}`);
}
{
// Test that no exception appears that was not thrown by us
let caughtError;
try {
test_exception.constructAllowException(common.mustCall());
} catch (anError) {
caughtError = anError;
}
assert.strictEqual(caughtError, undefined,
'No exception originated on the native side, but' +
` ${caughtError} was passed`);
// Test that the exception state remains clear when no exception is thrown
const exception_pending = test_exception.wasPending();
assert.strictEqual(exception_pending, false,
'Exception state did not remain clear as expected,' +
` .wasPending() returned ${exception_pending}`);
}

View File

@ -0,0 +1,32 @@
'use strict';
if (process.argv[2] === 'child') {
const common = require('../../common');
// Trying, catching the exception, and finding the bindings at the `Error`'s
// `binding` property is done intentionally, because we're also testing what
// happens when the add-on entry point throws. See test.js.
try {
require(`./build/${common.buildType}/test_exception`);
} catch (anException) {
anException.binding.createExternal();
}
// Collect garbage 10 times. At least one of those should throw the exception
// and cause the whole process to bail with it, its text printed to stderr and
// asserted by the parent process to match expectations.
let gcCount = 10;
(function gcLoop() {
global.gc();
if (--gcCount > 0) {
setImmediate(() => gcLoop());
}
})();
return;
}
const assert = require('assert');
const { spawnSync } = require('child_process');
const child = spawnSync(process.execPath, [
'--expose-gc', __filename, 'child',
]);
assert.strictEqual(child.signal, null);
assert.match(child.stderr.toString(), /Error during Finalize/);

View File

@ -0,0 +1,116 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
static bool exceptionWasPending = false;
static int num = 0x23432;
static napi_value returnException(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value global;
NODE_API_CALL(env, napi_get_global(env, &global));
napi_value result;
napi_status status = napi_call_function(env, global, args[0], 0, 0, &result);
if (status == napi_pending_exception) {
napi_value ex;
NODE_API_CALL(env, napi_get_and_clear_last_exception(env, &ex));
return ex;
}
return NULL;
}
static napi_value constructReturnException(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value result;
napi_status status = napi_new_instance(env, args[0], 0, 0, &result);
if (status == napi_pending_exception) {
napi_value ex;
NODE_API_CALL(env, napi_get_and_clear_last_exception(env, &ex));
return ex;
}
return NULL;
}
static napi_value allowException(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value global;
NODE_API_CALL(env, napi_get_global(env, &global));
napi_value result;
napi_call_function(env, global, args[0], 0, 0, &result);
// Ignore status and check napi_is_exception_pending() instead.
NODE_API_CALL(env, napi_is_exception_pending(env, &exceptionWasPending));
return NULL;
}
static napi_value constructAllowException(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value result;
napi_new_instance(env, args[0], 0, 0, &result);
// Ignore status and check napi_is_exception_pending() instead.
NODE_API_CALL(env, napi_is_exception_pending(env, &exceptionWasPending));
return NULL;
}
static napi_value wasPending(napi_env env, napi_callback_info info) {
napi_value result;
NODE_API_CALL(env, napi_get_boolean(env, exceptionWasPending, &result));
return result;
}
static void finalizer(napi_env env, void *data, void *hint) {
NODE_API_CALL_RETURN_VOID(env,
napi_throw_error(env, NULL, "Error during Finalize"));
}
static napi_value createExternal(napi_env env, napi_callback_info info) {
napi_value external;
NODE_API_CALL(env,
napi_create_external(env, &num, finalizer, NULL, &external));
return external;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("returnException", returnException),
DECLARE_NODE_API_PROPERTY("allowException", allowException),
DECLARE_NODE_API_PROPERTY("constructReturnException", constructReturnException),
DECLARE_NODE_API_PROPERTY("constructAllowException", constructAllowException),
DECLARE_NODE_API_PROPERTY("wasPending", wasPending),
DECLARE_NODE_API_PROPERTY("createExternal", createExternal),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
napi_value error, code, message;
NODE_API_CALL(env, napi_create_string_utf8(env, "Error during Init",
NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_string_utf8(env, "", NAPI_AUTO_LENGTH, &code));
NODE_API_CALL(env, napi_create_error(env, code, message, &error));
NODE_API_CALL(env, napi_set_named_property(env, error, "binding", exports));
NODE_API_CALL(env, napi_throw(env, error));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_finalizer",
"defines": [ "NAPI_EXPERIMENTAL" ],
"sources": [
"test_finalizer.c"
]
}
]
}

View File

@ -0,0 +1,45 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const test_finalizer = require(`./build/${common.buildType}/test_finalizer`);
const assert = require('assert');
const { gcUntil } = require('../../common/gc');
// The goal of this test is to show that we can run "pure" finalizers in the
// current JS loop tick. Thus, we do not use gcUntil function works
// asynchronously using micro tasks.
// We use IIFE for the obj scope instead of {} to be compatible with
// non-V8 JS engines that do not support scoped variables.
(() => {
const obj = {};
test_finalizer.addFinalizer(obj);
})();
for (let i = 0; i < 10; ++i) {
global.gc();
if (test_finalizer.getFinalizerCallCount() === 1) {
break;
}
}
assert.strictEqual(test_finalizer.getFinalizerCallCount(), 1);
// The finalizer that access JS cannot run synchronously. They are run in the
// next JS loop tick. Thus, we must use gcUntil.
async function runAsyncTests() {
// We do not use common.mustCall() because we want to see the finalizer
// called in response to GC and not as a part of env destruction.
let js_is_called = false;
// We use IIFE for the obj scope instead of {} to be compatible with
// non-V8 JS engines that do not support scoped variables.
(() => {
const obj = {};
test_finalizer.addFinalizerWithJS(obj, () => { js_is_called = true; });
})();
await gcUntil('ensure JS finalizer called',
() => (test_finalizer.getFinalizerCallCount() === 2));
assert(js_is_called);
}
runAsyncTests();

View File

@ -0,0 +1,31 @@
'use strict';
const common = require('../../common');
if (process.argv[2] === 'child') {
const test_finalizer = require(`./build/${common.buildType}/test_finalizer`);
(() => {
const obj = {};
test_finalizer.addFinalizerFailOnJS(obj);
})();
// Collect garbage 10 times. At least one of those should throw the exception
// and cause the whole process to bail with it, its text printed to stderr and
// asserted by the parent process to match expectations.
let gcCount = 10;
(function gcLoop() {
global.gc();
if (--gcCount > 0) {
setImmediate(() => gcLoop());
}
})();
return;
}
const assert = require('assert');
const { spawnSync } = require('child_process');
const child = spawnSync(process.execPath, [
'--expose-gc', __filename, 'child',
]);
assert(common.nodeProcessAborted(child.status, child.signal));
assert.match(child.stderr.toString(), /Finalizer is calling a function that may affect GC state/);

View File

@ -0,0 +1,148 @@
#include <js_native_api.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../common.h"
#include "../entry_point.h"
typedef struct {
int32_t finalize_count;
napi_ref js_func;
} FinalizerData;
static void finalizerOnlyCallback(node_api_basic_env env,
void* finalize_data,
void* finalize_hint) {
FinalizerData* data = (FinalizerData*)finalize_data;
int32_t count = ++data->finalize_count;
// It is safe to access instance data
NODE_API_BASIC_CALL_RETURN_VOID(env,
napi_get_instance_data(env, (void**)&data));
NODE_API_BASIC_ASSERT_RETURN_VOID(count = data->finalize_count,
"Expected to be the same FinalizerData");
}
static void finalizerCallingJSCallback(napi_env env,
void* finalize_data,
void* finalize_hint) {
napi_value js_func, undefined;
FinalizerData* data = (FinalizerData*)finalize_data;
NODE_API_CALL_RETURN_VOID(
env, napi_get_reference_value(env, data->js_func, &js_func));
NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
NODE_API_CALL_RETURN_VOID(
env, napi_call_function(env, undefined, js_func, 0, NULL, NULL));
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, data->js_func));
data->js_func = NULL;
++data->finalize_count;
}
// Schedule async finalizer to run JavaScript-touching code.
static void finalizerWithJSCallback(node_api_basic_env env,
void* finalize_data,
void* finalize_hint) {
NODE_API_BASIC_CALL_RETURN_VOID(
env,
node_api_post_finalizer(
env, finalizerCallingJSCallback, finalize_data, finalize_hint));
}
static void finalizerWithFailedJSCallback(node_api_basic_env basic_env,
void* finalize_data,
void* finalize_hint) {
// Intentionally cast to a napi_env to test the fatal failure.
napi_env env = (napi_env)basic_env;
napi_value obj;
FinalizerData* data = (FinalizerData*)finalize_data;
++data->finalize_count;
NODE_API_CALL_RETURN_VOID(env, napi_create_object(env, &obj));
}
static napi_value addFinalizer(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value argv[1] = {0};
FinalizerData* data;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
NODE_API_CALL(env,
napi_add_finalizer(
env, argv[0], data, finalizerOnlyCallback, NULL, NULL));
return NULL;
}
// This finalizer is going to call JavaScript from finalizer and succeed.
static napi_value addFinalizerWithJS(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value argv[2] = {0};
napi_valuetype arg_type;
FinalizerData* data;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
NODE_API_CALL(env, napi_typeof(env, argv[1], &arg_type));
NODE_API_ASSERT(
env, arg_type == napi_function, "Expected function as the second arg");
NODE_API_CALL(env, napi_create_reference(env, argv[1], 1, &data->js_func));
NODE_API_CALL(env,
napi_add_finalizer(
env, argv[0], data, finalizerWithJSCallback, NULL, NULL));
return NULL;
}
// This finalizer is going to call JavaScript from finalizer and fail.
static napi_value addFinalizerFailOnJS(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value argv[1] = {0};
FinalizerData* data;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
NODE_API_CALL(
env,
napi_add_finalizer(
env, argv[0], data, finalizerWithFailedJSCallback, NULL, NULL));
return NULL;
}
static napi_value getFinalizerCallCount(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value argv[1];
FinalizerData* data;
napi_value result;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
NODE_API_CALL(env, napi_create_int32(env, data->finalize_count, &result));
return result;
}
static void finalizeData(napi_env env, void* data, void* hint) {
free(data);
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
FinalizerData* data = (FinalizerData*)malloc(sizeof(FinalizerData));
NODE_API_ASSERT(env, data != NULL, "Failed to allocate memory");
memset(data, 0, sizeof(FinalizerData));
NODE_API_CALL(env, napi_set_instance_data(env, data, finalizeData, NULL));
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("addFinalizer", addFinalizer),
DECLARE_NODE_API_PROPERTY("addFinalizerWithJS", addFinalizerWithJS),
DECLARE_NODE_API_PROPERTY("addFinalizerFailOnJS", addFinalizerFailOnJS),
DECLARE_NODE_API_PROPERTY("getFinalizerCallCount",
getFinalizerCallCount)};
NODE_API_CALL(
env,
napi_define_properties(env,
exports,
sizeof(descriptors) / sizeof(*descriptors),
descriptors));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "test_function",
"sources": [
"test_function.c"
]
}
]
}

View File

@ -0,0 +1,52 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const assert = require('assert');
// Testing api calls for function
const test_function = require(`./build/${common.buildType}/test_function`);
function func1() {
return 1;
}
assert.strictEqual(test_function.TestCall(func1), 1);
function func2() {
console.log('hello world!');
return null;
}
assert.strictEqual(test_function.TestCall(func2), null);
function func3(input) {
return input + 1;
}
assert.strictEqual(test_function.TestCall(func3, 1), 2);
function func4(input) {
return func3(input);
}
assert.strictEqual(test_function.TestCall(func4, 1), 2);
assert.strictEqual(test_function.TestName.name, 'Name');
assert.strictEqual(test_function.TestNameShort.name, 'Name_');
let tracked_function = test_function.MakeTrackedFunction(common.mustCall());
assert(!!tracked_function);
tracked_function = null;
global.gc();
assert.deepStrictEqual(test_function.TestCreateFunctionParameters(), {
envIsNull: 'Invalid argument',
nameIsNull: 'napi_ok',
cbIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
});
assert.throws(
() => test_function.TestBadReturnExceptionPending(),
{
code: 'throwing exception',
name: 'Error',
},
);

View File

@ -0,0 +1,204 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value TestCreateFunctionParameters(napi_env env,
napi_callback_info info) {
napi_status status;
napi_value result, return_value;
NODE_API_CALL(env, napi_create_object(env, &return_value));
status = napi_create_function(NULL,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestCreateFunctionParameters,
NULL,
&result);
add_returned_status(env,
"envIsNull",
return_value,
"Invalid argument",
napi_invalid_arg,
status);
napi_create_function(env,
NULL,
NAPI_AUTO_LENGTH,
TestCreateFunctionParameters,
NULL,
&result);
add_last_status(env, "nameIsNull", return_value);
napi_create_function(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
NULL,
NULL,
&result);
add_last_status(env, "cbIsNull", return_value);
napi_create_function(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestCreateFunctionParameters,
NULL,
NULL);
add_last_status(env, "resultIsNull", return_value);
return return_value;
}
static napi_value TestCallFunction(napi_env env, napi_callback_info info) {
size_t argc = 10;
napi_value args[10];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc > 0, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_function,
"Wrong type of arguments. Expects a function as first argument.");
napi_value* argv = args + 1;
argc = argc - 1;
napi_value global;
NODE_API_CALL(env, napi_get_global(env, &global));
napi_value result;
NODE_API_CALL(env, napi_call_function(env, global, args[0], argc, argv, &result));
return result;
}
static napi_value TestFunctionName(napi_env env, napi_callback_info info) {
return NULL;
}
static void finalize_function(napi_env env, void* data, void* hint) {
napi_ref ref = data;
// Retrieve the JavaScript undefined value.
napi_value undefined;
NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
// Retrieve the JavaScript function we must call.
napi_value js_function;
NODE_API_CALL_RETURN_VOID(env, napi_get_reference_value(env, ref, &js_function));
// Call the JavaScript function to indicate that the generated JavaScript
// function is about to be gc-ed.
NODE_API_CALL_RETURN_VOID(env,
napi_call_function(env, undefined, js_function, 0, NULL, NULL));
// Destroy the persistent reference to the function we just called so as to
// properly clean up.
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, ref));
}
static napi_value MakeTrackedFunction(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value js_finalize_cb;
napi_valuetype arg_type;
// Retrieve and validate from the arguments the function we will use to
// indicate to JavaScript that the function we are about to create is about to
// be gc-ed.
NODE_API_CALL(env,
napi_get_cb_info(env, info, &argc, &js_finalize_cb, NULL, NULL));
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
NODE_API_CALL(env, napi_typeof(env, js_finalize_cb, &arg_type));
NODE_API_ASSERT(env, arg_type == napi_function, "Argument must be a function");
// Dynamically create a function.
napi_value result;
NODE_API_CALL(env,
napi_create_function(
env, "TrackedFunction", NAPI_AUTO_LENGTH, TestFunctionName, NULL,
&result));
// Create a strong reference to the function we will call when the tracked
// function is about to be gc-ed.
napi_ref js_finalize_cb_ref;
NODE_API_CALL(env,
napi_create_reference(env, js_finalize_cb, 1, &js_finalize_cb_ref));
// Attach a finalizer to the dynamically created function and pass it the
// strong reference we created in the previous step.
NODE_API_CALL(env,
napi_wrap(
env, result, js_finalize_cb_ref, finalize_function, NULL, NULL));
return result;
}
static napi_value TestBadReturnExceptionPending(napi_env env, napi_callback_info info) {
napi_throw_error(env, "throwing exception", "throwing exception");
// addons should only ever return a valid napi_value even if an
// exception occurs, but we have seen that the C++ wrapper
// with exceptions enabled sometimes returns an invalid value
// when an exception is thrown. Test that we ignore the return
// value then an exception is pending. We use 0xFFFFFFFF as a value
// that should never be a valid napi_value and node seems to
// crash if it is not ignored indicating that it is indeed invalid.
return (napi_value)(0xFFFFFFFFF);
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_value fn1;
NODE_API_CALL(env, napi_create_function(
env, NULL, NAPI_AUTO_LENGTH, TestCallFunction, NULL, &fn1));
napi_value fn2;
NODE_API_CALL(env, napi_create_function(
env, "Name", NAPI_AUTO_LENGTH, TestFunctionName, NULL, &fn2));
napi_value fn3;
NODE_API_CALL(env, napi_create_function(
env, "Name_extra", 5, TestFunctionName, NULL, &fn3));
napi_value fn4;
NODE_API_CALL(env,
napi_create_function(
env, "MakeTrackedFunction", NAPI_AUTO_LENGTH, MakeTrackedFunction,
NULL, &fn4));
napi_value fn5;
NODE_API_CALL(env,
napi_create_function(
env, "TestCreateFunctionParameters", NAPI_AUTO_LENGTH,
TestCreateFunctionParameters, NULL, &fn5));
napi_value fn6;
NODE_API_CALL(env,
napi_create_function(
env, "TestBadReturnExceptionPending", NAPI_AUTO_LENGTH,
TestBadReturnExceptionPending, NULL, &fn6));
NODE_API_CALL(env, napi_set_named_property(env, exports, "TestCall", fn1));
NODE_API_CALL(env, napi_set_named_property(env, exports, "TestName", fn2));
NODE_API_CALL(env,
napi_set_named_property(env, exports, "TestNameShort", fn3));
NODE_API_CALL(env,
napi_set_named_property(env, exports, "MakeTrackedFunction", fn4));
NODE_API_CALL(env,
napi_set_named_property(
env, exports, "TestCreateFunctionParameters", fn5));
NODE_API_CALL(env,
napi_set_named_property(
env, exports, "TestBadReturnExceptionPending", fn6));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "test_general",
"sources": [
"test_general.c"
]
}
]
}

View File

@ -0,0 +1,97 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const test_general = require(`./build/${common.buildType}/test_general`);
const assert = require('assert');
const { gcUntil } = require('../../common/gc');
const val1 = '1';
const val2 = 1;
const val3 = 1;
class BaseClass {
}
class ExtendedClass extends BaseClass {
}
const baseObject = new BaseClass();
const extendedObject = new ExtendedClass();
// Test napi_strict_equals
assert.ok(test_general.testStrictEquals(val1, val1));
assert.strictEqual(test_general.testStrictEquals(val1, val2), false);
assert.ok(test_general.testStrictEquals(val2, val3));
// Test napi_get_prototype
assert.strictEqual(test_general.testGetPrototype(baseObject),
Object.getPrototypeOf(baseObject));
assert.strictEqual(test_general.testGetPrototype(extendedObject),
Object.getPrototypeOf(extendedObject));
// Prototypes for base and extended should be different.
assert.notStrictEqual(test_general.testGetPrototype(baseObject),
test_general.testGetPrototype(extendedObject));
// Test version management functions
assert.strictEqual(test_general.testGetVersion(), 10);
[
123,
'test string',
function() {},
new Object(),
true,
undefined,
Symbol(),
].forEach((val) => {
assert.strictEqual(test_general.testNapiTypeof(val), typeof val);
});
// Since typeof in js return object need to validate specific case
// for null
assert.strictEqual(test_general.testNapiTypeof(null), 'null');
// Assert that wrapping twice fails.
const x = {};
test_general.wrap(x);
assert.throws(() => test_general.wrap(x),
{ name: 'Error', message: 'Invalid argument' });
// Clean up here, otherwise derefItemWasCalled() will be polluted.
test_general.removeWrap(x);
// Ensure that wrapping, removing the wrap, and then wrapping again works.
const y = {};
test_general.wrap(y);
test_general.removeWrap(y);
// Wrapping twice succeeds if a remove_wrap() separates the instances
test_general.wrap(y);
// Clean up here, otherwise derefItemWasCalled() will be polluted.
test_general.removeWrap(y);
// Test napi_adjust_external_memory
const adjustedValue = test_general.testAdjustExternalMemory();
assert.strictEqual(typeof adjustedValue, 'number');
assert(adjustedValue > 0);
async function runGCTests() {
// Ensure that garbage collecting an object with a wrapped native item results
// in the finalize callback being called.
assert.strictEqual(test_general.derefItemWasCalled(), false);
(() => test_general.wrap({}))();
await gcUntil('deref_item() was called upon garbage collecting a ' +
'wrapped object.',
() => test_general.derefItemWasCalled());
// Ensure that removing a wrap and garbage collecting does not fire the
// finalize callback.
let z = {};
test_general.testFinalizeWrap(z);
test_general.removeWrap(z);
z = null;
await gcUntil(
'finalize callback was not called upon garbage collection.',
() => (!test_general.finalizeWasCalled()));
}
runGCTests();

View File

@ -0,0 +1,57 @@
'use strict';
if (process.argv[2] === 'child') {
const common = require('../../common');
const test_general = require(`./build/${common.buildType}/test_general`);
// The second argument to `envCleanupWrap()` is an index into the global
// static string array named `env_cleanup_finalizer_messages` on the native
// side. A reverse mapping is reproduced here for clarity.
const finalizerMessages = {
'simple wrap': 0,
'wrap, removeWrap': 1,
'first wrap': 2,
'second wrap': 3,
};
// We attach the three objects we will test to `module.exports` to ensure they
// will not be garbage-collected before the process exits.
// Make sure the finalizer for a simple wrap will be called at env cleanup.
module.exports['simple wrap'] =
test_general.envCleanupWrap({}, finalizerMessages['simple wrap']);
// Make sure that a removed wrap does not result in a call to its finalizer at
// env cleanup.
module.exports['wrap, removeWrap'] =
test_general.envCleanupWrap({}, finalizerMessages['wrap, removeWrap']);
test_general.removeWrap(module.exports['wrap, removeWrap']);
// Make sure that only the latest attached version of a re-wrapped item's
// finalizer gets called at env cleanup.
module.exports['first wrap'] =
test_general.envCleanupWrap({}, finalizerMessages['first wrap']);
test_general.removeWrap(module.exports['first wrap']);
test_general.envCleanupWrap(module.exports['first wrap'],
finalizerMessages['second wrap']);
} else {
const assert = require('assert');
const { spawnSync } = require('child_process');
const child = spawnSync(process.execPath, [__filename, 'child'], {
stdio: [ process.stdin, 'pipe', process.stderr ],
});
// Grab the child's output and construct an object whose keys are the rows of
// the output and whose values are `true`, so we can compare the output while
// ignoring the order in which the lines of it were produced.
assert.deepStrictEqual(
child.stdout.toString().split(/\r\n|\r|\n/g).reduce((obj, item) =>
Object.assign(obj, item ? { [item]: true } : {}), {}), {
'finalize at env cleanup for simple wrap': true,
'finalize at env cleanup for second wrap': true,
});
// Ensure that the child exited successfully.
assert.strictEqual(child.status, 0);
}

View File

@ -0,0 +1,37 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const test_general = require(`./build/${common.buildType}/test_general`);
const assert = require('assert');
const { gcUntil } = require('../../common/gc');
let finalized = {};
const callback = common.mustCall(2);
// Add two items to be finalized and ensure the callback is called for each.
test_general.addFinalizerOnly(finalized, callback);
test_general.addFinalizerOnly(finalized, callback);
// Ensure attached items cannot be retrieved.
assert.throws(() => test_general.unwrap(finalized),
{ name: 'Error', message: 'Invalid argument' });
// Ensure attached items cannot be removed.
assert.throws(() => test_general.removeWrap(finalized),
{ name: 'Error', message: 'Invalid argument' });
finalized = null;
global.gc();
// Add an item to an object that is already wrapped, and ensure that its
// finalizer as well as the wrap finalizer gets called.
async function testFinalizeAndWrap() {
assert.strictEqual(test_general.derefItemWasCalled(), false);
let finalizeAndWrap = {};
test_general.wrap(finalizeAndWrap);
test_general.addFinalizerOnly(finalizeAndWrap, common.mustCall());
finalizeAndWrap = null;
await gcUntil('test finalize and wrap',
() => test_general.derefItemWasCalled());
}
testFinalizeAndWrap();

View File

@ -0,0 +1,8 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const test_globals = require(`./build/${common.buildType}/test_general`);
assert.strictEqual(test_globals.getUndefined(), undefined);
assert.strictEqual(test_globals.getNull(), null);

View File

@ -0,0 +1,95 @@
'use strict';
const common = require('../../common');
const fs = require('fs');
const assert = require('assert');
// Addon is referenced through the eval expression in testFile
const addon = require(`./build/${common.buildType}/test_general`);
const path = require('path');
// This test depends on a number of V8 tests.
const v8TestsDir = path.resolve(__dirname, '..', '..', '..', 'deps', 'v8',
'test', 'mjsunit');
const v8TestsDirExists = fs.existsSync(v8TestsDir);
// The following assert functions are referenced by v8's unit tests
// See for instance deps/v8/test/mjsunit/instanceof.js
// eslint-disable-next-line no-unused-vars
function assertTrue(assertion) {
return assert.strictEqual(assertion, true);
}
// eslint-disable-next-line no-unused-vars
function assertFalse(assertion) {
assert.strictEqual(assertion, false);
}
// eslint-disable-next-line no-unused-vars
function assertEquals(leftHandSide, rightHandSide) {
assert.strictEqual(leftHandSide, rightHandSide);
}
// eslint-disable-next-line no-unused-vars
function assertThrows(statement) {
assert.throws(function() {
eval(statement);
}, Error);
}
function testFile(fileName) {
try {
const contents = fs.readFileSync(fileName, { encoding: 'utf8' });
eval(contents.replace(/[(]([^\s(]+)\s+instanceof\s+([^)]+)[)]/g,
'(addon.doInstanceOf($1, $2))'));
} catch (err) {
// This test depends on V8 test files, which may not exist in downloaded
// archives. Emit a warning if the tests cannot be found instead of failing.
if (err.code === 'ENOENT' && !v8TestsDirExists)
process.emitWarning(`test file ${fileName} does not exist.`);
else
throw err;
}
}
testFile(path.join(v8TestsDir, 'instanceof.js'));
testFile(path.join(v8TestsDir, 'instanceof-2.js'));
// We can only perform this test if we have a working Symbol.hasInstance
if (typeof Symbol !== 'undefined' && 'hasInstance' in Symbol &&
typeof Symbol.hasInstance === 'symbol') {
function compareToNative(theObject, theConstructor) {
assert.strictEqual(
addon.doInstanceOf(theObject, theConstructor),
(theObject instanceof theConstructor),
);
}
function MyClass() {}
Object.defineProperty(MyClass, Symbol.hasInstance, {
value: function(candidate) {
return 'mark' in candidate;
},
});
function MySubClass() {}
MySubClass.prototype = new MyClass();
let x = new MySubClass();
let y = new MySubClass();
x.mark = true;
compareToNative(x, MySubClass);
compareToNative(y, MySubClass);
compareToNative(x, MyClass);
compareToNative(y, MyClass);
x = new MyClass();
y = new MyClass();
x.mark = true;
compareToNative(x, MySubClass);
compareToNative(y, MySubClass);
compareToNative(x, MyClass);
compareToNative(y, MyClass);
}

View File

@ -0,0 +1,14 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
// `addon` is referenced through the eval expression in testFile
const addon = require(`./build/${common.buildType}/test_general`);
const testCase = '(41.92 + 0.08);';
const expected = 42;
const actual = addon.testNapiRun(testCase);
assert.strictEqual(actual, expected);
assert.throws(() => addon.testNapiRun({ abc: 'def' }), /string was expected/);

View File

@ -0,0 +1,8 @@
'use strict';
const common = require('../../common');
const addon = require(`./build/${common.buildType}/test_general`);
const assert = require('assert');
addon.createNapiError();
assert(addon.testNapiErrorCleanup(), 'napi_status cleaned up for second call');

View File

@ -0,0 +1,315 @@
// we define NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED here to
// validate that it can be used as a form of test itself. It is
// not related to any of the other tests
// defined in the file
#define NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
#include <js_native_api.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value testStrictEquals(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
bool bool_result;
napi_value result;
NODE_API_CALL(env, napi_strict_equals(env, args[0], args[1], &bool_result));
NODE_API_CALL(env, napi_get_boolean(env, bool_result, &result));
return result;
}
static napi_value testGetPrototype(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value result;
NODE_API_CALL(env, napi_get_prototype(env, args[0], &result));
return result;
}
static napi_value testGetVersion(napi_env env, napi_callback_info info) {
uint32_t version;
napi_value result;
NODE_API_CALL(env, napi_get_version(env, &version));
NODE_API_CALL(env, napi_create_uint32(env, version, &result));
return result;
}
static napi_value doInstanceOf(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
bool instanceof;
NODE_API_CALL(env, napi_instanceof(env, args[0], args[1], &instanceof));
napi_value result;
NODE_API_CALL(env, napi_get_boolean(env, instanceof, &result));
return result;
}
static napi_value getNull(napi_env env, napi_callback_info info) {
napi_value result;
NODE_API_CALL(env, napi_get_null(env, &result));
return result;
}
static napi_value getUndefined(napi_env env, napi_callback_info info) {
napi_value result;
NODE_API_CALL(env, napi_get_undefined(env, &result));
return result;
}
static napi_value createNapiError(napi_env env, napi_callback_info info) {
napi_value value;
NODE_API_CALL(env, napi_create_string_utf8(env, "xyz", 3, &value));
double double_value;
napi_status status = napi_get_value_double(env, value, &double_value);
NODE_API_ASSERT(env, status != napi_ok, "Failed to produce error condition");
const napi_extended_error_info *error_info = 0;
NODE_API_CALL(env, napi_get_last_error_info(env, &error_info));
NODE_API_ASSERT(env, error_info->error_code == status,
"Last error info code should match last status");
NODE_API_ASSERT(env, error_info->error_message,
"Last error info message should not be null");
return NULL;
}
static napi_value testNapiErrorCleanup(napi_env env, napi_callback_info info) {
const napi_extended_error_info *error_info = 0;
NODE_API_CALL(env, napi_get_last_error_info(env, &error_info));
napi_value result;
bool is_ok = error_info->error_code == napi_ok;
NODE_API_CALL(env, napi_get_boolean(env, is_ok, &result));
return result;
}
static napi_value testNapiTypeof(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_valuetype argument_type;
NODE_API_CALL(env, napi_typeof(env, args[0], &argument_type));
napi_value result = NULL;
if (argument_type == napi_number) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "number", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_string) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "string", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_function) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "function", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_object) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "object", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_boolean) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "boolean", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_undefined) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "undefined", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_symbol) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "symbol", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_null) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "null", NAPI_AUTO_LENGTH, &result));
}
return result;
}
static bool deref_item_called = false;
static void deref_item(napi_env env, void* data, void* hint) {
(void) hint;
NODE_API_ASSERT_RETURN_VOID(env, data == &deref_item_called,
"Finalize callback was called with the correct pointer");
deref_item_called = true;
}
static napi_value deref_item_was_called(napi_env env, napi_callback_info info) {
napi_value it_was_called;
NODE_API_CALL(env, napi_get_boolean(env, deref_item_called, &it_was_called));
return it_was_called;
}
static napi_value wrap_first_arg(napi_env env,
napi_callback_info info,
napi_finalize finalizer,
void* data) {
size_t argc = 1;
napi_value to_wrap;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &to_wrap, NULL, NULL));
NODE_API_CALL(env, napi_wrap(env, to_wrap, data, finalizer, NULL, NULL));
return to_wrap;
}
static napi_value wrap(napi_env env, napi_callback_info info) {
deref_item_called = false;
return wrap_first_arg(env, info, deref_item, &deref_item_called);
}
static napi_value unwrap(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value wrapped;
void* data;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &wrapped, NULL, NULL));
NODE_API_CALL(env, napi_unwrap(env, wrapped, &data));
return NULL;
}
static napi_value remove_wrap(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value wrapped;
void* data;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &wrapped, NULL, NULL));
NODE_API_CALL(env, napi_remove_wrap(env, wrapped, &data));
return NULL;
}
static bool finalize_called = false;
static void test_finalize(napi_env env, void* data, void* hint) {
finalize_called = true;
}
static napi_value test_finalize_wrap(napi_env env, napi_callback_info info) {
return wrap_first_arg(env, info, test_finalize, NULL);
}
static napi_value finalize_was_called(napi_env env, napi_callback_info info) {
napi_value it_was_called;
NODE_API_CALL(env, napi_get_boolean(env, finalize_called, &it_was_called));
return it_was_called;
}
static napi_value testAdjustExternalMemory(napi_env env, napi_callback_info info) {
napi_value result;
int64_t adjustedValue;
NODE_API_CALL(env, napi_adjust_external_memory(env, 1, &adjustedValue));
NODE_API_CALL(env, napi_create_double(env, (double)adjustedValue, &result));
return result;
}
static napi_value testNapiRun(napi_env env, napi_callback_info info) {
napi_value script, result;
size_t argc = 1;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &script, NULL, NULL));
NODE_API_CALL(env, napi_run_script(env, script, &result));
return result;
}
static void finalizer_only_callback(napi_env env, void* data, void* hint) {
napi_ref js_cb_ref = data;
napi_value js_cb, undefined;
NODE_API_CALL_RETURN_VOID(env, napi_get_reference_value(env, js_cb_ref, &js_cb));
NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
NODE_API_CALL_RETURN_VOID(env,
napi_call_function(env, undefined, js_cb, 0, NULL, NULL));
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, js_cb_ref));
}
static napi_value add_finalizer_only(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value argv[2];
napi_ref js_cb_ref;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
NODE_API_CALL(env, napi_create_reference(env, argv[1], 1, &js_cb_ref));
NODE_API_CALL(env,
napi_add_finalizer(
env, argv[0], js_cb_ref, finalizer_only_callback, NULL, NULL));
return NULL;
}
static const char* env_cleanup_finalizer_messages[] = {
"simple wrap",
"wrap, removeWrap",
"first wrap",
"second wrap"
};
static void cleanup_env_finalizer(napi_env env, void* data, void* hint) {
(void) env;
(void) hint;
printf("finalize at env cleanup for %s\n",
env_cleanup_finalizer_messages[(uintptr_t)data]);
}
static napi_value env_cleanup_wrap(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value argv[2];
uint32_t value;
uintptr_t ptr_value;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
NODE_API_CALL(env, napi_get_value_uint32(env, argv[1], &value));
ptr_value = value;
return wrap_first_arg(env, info, cleanup_env_finalizer, (void*)ptr_value);
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("testStrictEquals", testStrictEquals),
DECLARE_NODE_API_PROPERTY("testGetPrototype", testGetPrototype),
DECLARE_NODE_API_PROPERTY("testGetVersion", testGetVersion),
DECLARE_NODE_API_PROPERTY("testNapiRun", testNapiRun),
DECLARE_NODE_API_PROPERTY("doInstanceOf", doInstanceOf),
DECLARE_NODE_API_PROPERTY("getUndefined", getUndefined),
DECLARE_NODE_API_PROPERTY("getNull", getNull),
DECLARE_NODE_API_PROPERTY("createNapiError", createNapiError),
DECLARE_NODE_API_PROPERTY("testNapiErrorCleanup", testNapiErrorCleanup),
DECLARE_NODE_API_PROPERTY("testNapiTypeof", testNapiTypeof),
DECLARE_NODE_API_PROPERTY("wrap", wrap),
DECLARE_NODE_API_PROPERTY("envCleanupWrap", env_cleanup_wrap),
DECLARE_NODE_API_PROPERTY("unwrap", unwrap),
DECLARE_NODE_API_PROPERTY("removeWrap", remove_wrap),
DECLARE_NODE_API_PROPERTY("addFinalizerOnly", add_finalizer_only),
DECLARE_NODE_API_PROPERTY("testFinalizeWrap", test_finalize_wrap),
DECLARE_NODE_API_PROPERTY("finalizeWasCalled", finalize_was_called),
DECLARE_NODE_API_PROPERTY("derefItemWasCalled", deref_item_was_called),
DECLARE_NODE_API_PROPERTY("testAdjustExternalMemory", testAdjustExternalMemory)
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "test_handle_scope",
"sources": [
"test_handle_scope.c"
]
}
]
}

View File

@ -0,0 +1,19 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
// Testing handle scope api calls
const testHandleScope =
require(`./build/${common.buildType}/test_handle_scope`);
testHandleScope.NewScope();
assert.ok(testHandleScope.NewScopeEscape() instanceof Object);
testHandleScope.NewScopeEscapeTwice();
assert.throws(
() => {
testHandleScope.NewScopeWithException(() => { throw new RangeError(); });
},
RangeError);

View File

@ -0,0 +1,86 @@
#include <js_native_api.h>
#include <string.h>
#include "../common.h"
#include "../entry_point.h"
// these tests validate the handle scope functions in the normal
// flow. Forcing gc behavior to fully validate they are doing
// the right right thing would be quite hard so we keep it
// simple for now.
static napi_value NewScope(napi_env env, napi_callback_info info) {
napi_handle_scope scope;
napi_value output = NULL;
NODE_API_CALL(env, napi_open_handle_scope(env, &scope));
NODE_API_CALL(env, napi_create_object(env, &output));
NODE_API_CALL(env, napi_close_handle_scope(env, scope));
return NULL;
}
static napi_value NewScopeEscape(napi_env env, napi_callback_info info) {
napi_escapable_handle_scope scope;
napi_value output = NULL;
napi_value escapee = NULL;
NODE_API_CALL(env, napi_open_escapable_handle_scope(env, &scope));
NODE_API_CALL(env, napi_create_object(env, &output));
NODE_API_CALL(env, napi_escape_handle(env, scope, output, &escapee));
NODE_API_CALL(env, napi_close_escapable_handle_scope(env, scope));
return escapee;
}
static napi_value NewScopeEscapeTwice(napi_env env, napi_callback_info info) {
napi_escapable_handle_scope scope;
napi_value output = NULL;
napi_value escapee = NULL;
napi_status status;
NODE_API_CALL(env, napi_open_escapable_handle_scope(env, &scope));
NODE_API_CALL(env, napi_create_object(env, &output));
NODE_API_CALL(env, napi_escape_handle(env, scope, output, &escapee));
status = napi_escape_handle(env, scope, output, &escapee);
NODE_API_ASSERT(env, status == napi_escape_called_twice, "Escaping twice fails");
NODE_API_CALL(env, napi_close_escapable_handle_scope(env, scope));
return NULL;
}
static napi_value NewScopeWithException(napi_env env, napi_callback_info info) {
napi_handle_scope scope;
size_t argc;
napi_value exception_function;
napi_status status;
napi_value output = NULL;
NODE_API_CALL(env, napi_open_handle_scope(env, &scope));
NODE_API_CALL(env, napi_create_object(env, &output));
argc = 1;
NODE_API_CALL(env, napi_get_cb_info(
env, info, &argc, &exception_function, NULL, NULL));
status = napi_call_function(
env, output, exception_function, 0, NULL, NULL);
NODE_API_ASSERT(env, status == napi_pending_exception,
"Function should have thrown.");
// Closing a handle scope should still work while an exception is pending.
NODE_API_CALL(env, napi_close_handle_scope(env, scope));
return NULL;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor properties[] = {
DECLARE_NODE_API_PROPERTY("NewScope", NewScope),
DECLARE_NODE_API_PROPERTY("NewScopeEscape", NewScopeEscape),
DECLARE_NODE_API_PROPERTY("NewScopeEscapeTwice", NewScopeEscapeTwice),
DECLARE_NODE_API_PROPERTY("NewScopeWithException", NewScopeWithException),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(properties) / sizeof(*properties), properties));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "test_instance_data",
"sources": [
"test_instance_data.c"
]
}
]
}

View File

@ -0,0 +1,40 @@
'use strict';
// Test API calls for instance data.
const common = require('../../common');
const assert = require('assert');
if (module !== require.main) {
// When required as a module, run the tests.
const test_instance_data =
require(`./build/${common.buildType}/test_instance_data`);
// Print to stdout when the environment deletes the instance data. This output
// is checked by the parent process.
test_instance_data.setPrintOnDelete();
// Test that instance data can be accessed from a binding.
assert.strictEqual(test_instance_data.increment(), 42);
// Test that the instance data can be accessed from a finalizer.
test_instance_data.objectWithFinalizer(common.mustCall());
global.gc();
} else {
// When launched as a script, run tests in either a child process or in a
// worker thread.
const requireAs = require('../../common/require-as');
const runOptions = { stdio: ['inherit', 'pipe', 'inherit'] };
function checkOutput(child) {
assert.strictEqual(child.status, 0);
assert.strictEqual(
(child.stdout.toString().split(/\r\n?|\n/) || [])[0],
'deleting addon data');
}
// Run tests in a child process.
checkOutput(requireAs(__filename, ['--expose-gc'], runOptions, 'child'));
// Run tests in a worker thread in a child process.
checkOutput(requireAs(__filename, ['--expose-gc'], runOptions, 'worker'));
}

View File

@ -0,0 +1,96 @@
#include <js_native_api.h>
#include <stdio.h>
#include <stdlib.h>
#include "../common.h"
#include "../entry_point.h"
typedef struct {
size_t value;
bool print;
napi_ref js_cb_ref;
} AddonData;
static napi_value Increment(napi_env env, napi_callback_info info) {
AddonData* data;
napi_value result;
NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
NODE_API_CALL(env, napi_create_uint32(env, ++data->value, &result));
return result;
}
static void DeleteAddonData(napi_env env, void* raw_data, void* hint) {
AddonData* data = raw_data;
if (data->print) {
printf("deleting addon data\n");
}
if (data->js_cb_ref != NULL) {
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, data->js_cb_ref));
}
free(data);
}
static napi_value SetPrintOnDelete(napi_env env, napi_callback_info info) {
AddonData* data;
NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
data->print = true;
return NULL;
}
static void TestFinalizer(napi_env env, void* raw_data, void* hint) {
(void) raw_data;
(void) hint;
AddonData* data;
NODE_API_CALL_RETURN_VOID(env, napi_get_instance_data(env, (void**)&data));
napi_value js_cb, undefined;
NODE_API_CALL_RETURN_VOID(env,
napi_get_reference_value(env, data->js_cb_ref, &js_cb));
NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
NODE_API_CALL_RETURN_VOID(env,
napi_call_function(env, undefined, js_cb, 0, NULL, NULL));
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, data->js_cb_ref));
data->js_cb_ref = NULL;
}
static napi_value ObjectWithFinalizer(napi_env env, napi_callback_info info) {
AddonData* data;
napi_value result, js_cb;
size_t argc = 1;
NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
NODE_API_ASSERT(env, data->js_cb_ref == NULL, "reference must be NULL");
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &js_cb, NULL, NULL));
NODE_API_CALL(env, napi_create_object(env, &result));
NODE_API_CALL(env,
napi_add_finalizer(env, result, NULL, TestFinalizer, NULL, NULL));
NODE_API_CALL(env, napi_create_reference(env, js_cb, 1, &data->js_cb_ref));
return result;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
AddonData* data = malloc(sizeof(*data));
data->value = 41;
data->print = false;
data->js_cb_ref = NULL;
NODE_API_CALL(env, napi_set_instance_data(env, data, DeleteAddonData, NULL));
napi_property_descriptor props[] = {
DECLARE_NODE_API_PROPERTY("increment", Increment),
DECLARE_NODE_API_PROPERTY("setPrintOnDelete", SetPrintOnDelete),
DECLARE_NODE_API_PROPERTY("objectWithFinalizer", ObjectWithFinalizer),
};
NODE_API_CALL(env,
napi_define_properties(
env, exports, sizeof(props) / sizeof(*props), props));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,11 @@
{
'targets': [
{
'target_name': 'test_new_target',
'defines': [ 'V8_DEPRECATION_WARNINGS=1' ],
'sources': [
'test_new_target.c'
]
}
]
}

View File

@ -0,0 +1,21 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const binding = require(`./build/${common.buildType}/test_new_target`);
class Class extends binding.BaseClass {
constructor() {
super();
this.method();
}
method() {
this.ok = true;
}
}
assert.ok(new Class() instanceof binding.BaseClass);
assert.ok(new Class().ok);
assert.ok(binding.OrdinaryFunction());
assert.ok(
new binding.Constructor(binding.Constructor) instanceof binding.Constructor);

View File

@ -0,0 +1,70 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value BaseClass(napi_env env, napi_callback_info info) {
napi_value newTargetArg;
NODE_API_CALL(env, napi_get_new_target(env, info, &newTargetArg));
napi_value thisArg;
NODE_API_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &thisArg, NULL));
napi_value undefined;
NODE_API_CALL(env, napi_get_undefined(env, &undefined));
// this !== new.target since we are being invoked through super()
bool result;
NODE_API_CALL(env, napi_strict_equals(env, newTargetArg, thisArg, &result));
NODE_API_ASSERT(env, !result, "this !== new.target");
// new.target !== undefined because we should be called as a new expression
NODE_API_ASSERT(env, newTargetArg != NULL, "newTargetArg != NULL");
NODE_API_CALL(env, napi_strict_equals(env, newTargetArg, undefined, &result));
NODE_API_ASSERT(env, !result, "new.target !== undefined");
return thisArg;
}
static napi_value Constructor(napi_env env, napi_callback_info info) {
bool result;
napi_value newTargetArg;
NODE_API_CALL(env, napi_get_new_target(env, info, &newTargetArg));
size_t argc = 1;
napi_value argv;
napi_value thisArg;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &argv, &thisArg, NULL));
napi_value undefined;
NODE_API_CALL(env, napi_get_undefined(env, &undefined));
// new.target !== undefined because we should be called as a new expression
NODE_API_ASSERT(env, newTargetArg != NULL, "newTargetArg != NULL");
NODE_API_CALL(env, napi_strict_equals(env, newTargetArg, undefined, &result));
NODE_API_ASSERT(env, !result, "new.target !== undefined");
// arguments[0] should be Constructor itself (test harness passed it)
NODE_API_CALL(env, napi_strict_equals(env, newTargetArg, argv, &result));
NODE_API_ASSERT(env, result, "new.target === Constructor");
return thisArg;
}
static napi_value OrdinaryFunction(napi_env env, napi_callback_info info) {
napi_value newTargetArg;
NODE_API_CALL(env, napi_get_new_target(env, info, &newTargetArg));
NODE_API_ASSERT(env, newTargetArg == NULL, "newTargetArg == NULL");
napi_value _true;
NODE_API_CALL(env, napi_get_boolean(env, true, &_true));
return _true;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
const napi_property_descriptor desc[] = {
DECLARE_NODE_API_PROPERTY("BaseClass", BaseClass),
DECLARE_NODE_API_PROPERTY("OrdinaryFunction", OrdinaryFunction),
DECLARE_NODE_API_PROPERTY("Constructor", Constructor)
};
NODE_API_CALL(env, napi_define_properties(env, exports, 3, desc));
return exports;
}
EXTERN_C_END

View File

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_number",
"sources": [
"test_number.c",
"test_null.c",
]
}
]
}

View File

@ -0,0 +1,134 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const test_number = require(`./build/${common.buildType}/test_number`);
// Testing api calls for number
function testNumber(num) {
assert.strictEqual(num, test_number.Test(num));
}
testNumber(0);
testNumber(-0);
testNumber(1);
testNumber(-1);
testNumber(100);
testNumber(2121);
testNumber(-1233);
testNumber(986583);
testNumber(-976675);
/* eslint-disable no-loss-of-precision */
testNumber(
98765432213456789876546896323445679887645323232436587988766545658);
testNumber(
-4350987086545760976737453646576078997096876957864353245245769809);
/* eslint-enable no-loss-of-precision */
testNumber(Number.MIN_SAFE_INTEGER);
testNumber(Number.MAX_SAFE_INTEGER);
testNumber(Number.MAX_SAFE_INTEGER + 10);
testNumber(Number.MIN_VALUE);
testNumber(Number.MAX_VALUE);
testNumber(Number.MAX_VALUE + 10);
testNumber(Number.POSITIVE_INFINITY);
testNumber(Number.NEGATIVE_INFINITY);
testNumber(Number.NaN);
function testUint32(input, expected = input) {
assert.strictEqual(expected, test_number.TestUint32Truncation(input));
}
// Test zero
testUint32(0.0, 0);
testUint32(-0.0, 0);
// Test overflow scenarios
testUint32(4294967295);
testUint32(4294967296, 0);
testUint32(4294967297, 1);
testUint32(17 * 4294967296 + 1, 1);
testUint32(-1, 0xffffffff);
// Validate documented behavior when value is retrieved as 32-bit integer with
// `napi_get_value_int32`
function testInt32(input, expected = input) {
assert.strictEqual(expected, test_number.TestInt32Truncation(input));
}
// Test zero
testInt32(0.0, 0);
testInt32(-0.0, 0);
// Test min/max int32 range
testInt32(-Math.pow(2, 31));
testInt32(Math.pow(2, 31) - 1);
// Test overflow scenarios
testInt32(4294967297, 1);
testInt32(4294967296, 0);
testInt32(4294967295, -1);
testInt32(4294967296 * 5 + 3, 3);
// Test min/max safe integer range
testInt32(Number.MIN_SAFE_INTEGER, 1);
testInt32(Number.MAX_SAFE_INTEGER, -1);
// Test within int64_t range (with precision loss)
testInt32(-Math.pow(2, 63) + (Math.pow(2, 9) + 1), 1024);
testInt32(Math.pow(2, 63) - (Math.pow(2, 9) + 1), -1024);
// Test min/max double value
testInt32(-Number.MIN_VALUE, 0);
testInt32(Number.MIN_VALUE, 0);
testInt32(-Number.MAX_VALUE, 0);
testInt32(Number.MAX_VALUE, 0);
// Test outside int64_t range
testInt32(-Math.pow(2, 63) + (Math.pow(2, 9)), 0);
testInt32(Math.pow(2, 63) - (Math.pow(2, 9)), 0);
// Test non-finite numbers
testInt32(Number.POSITIVE_INFINITY, 0);
testInt32(Number.NEGATIVE_INFINITY, 0);
testInt32(Number.NaN, 0);
// Validate documented behavior when value is retrieved as 64-bit integer with
// `napi_get_value_int64`
function testInt64(input, expected = input) {
assert.strictEqual(expected, test_number.TestInt64Truncation(input));
}
// Both V8 and ChakraCore return a sentinel value of `0x8000000000000000` when
// the conversion goes out of range, but V8 treats it as unsigned in some cases.
const RANGEERROR_POSITIVE = Math.pow(2, 63);
const RANGEERROR_NEGATIVE = -Math.pow(2, 63);
// Test zero
testInt64(0.0, 0);
testInt64(-0.0, 0);
// Test min/max safe integer range
testInt64(Number.MIN_SAFE_INTEGER);
testInt64(Number.MAX_SAFE_INTEGER);
// Test within int64_t range (with precision loss)
testInt64(-Math.pow(2, 63) + (Math.pow(2, 9) + 1));
testInt64(Math.pow(2, 63) - (Math.pow(2, 9) + 1));
// Test min/max double value
testInt64(-Number.MIN_VALUE, 0);
testInt64(Number.MIN_VALUE, 0);
testInt64(-Number.MAX_VALUE, RANGEERROR_NEGATIVE);
testInt64(Number.MAX_VALUE, RANGEERROR_POSITIVE);
// Test outside int64_t range
testInt64(-Math.pow(2, 63) + (Math.pow(2, 9)), RANGEERROR_NEGATIVE);
testInt64(Math.pow(2, 63) - (Math.pow(2, 9)), RANGEERROR_POSITIVE);
// Test non-finite numbers
testInt64(Number.POSITIVE_INFINITY, 0);
testInt64(Number.NEGATIVE_INFINITY, 0);
testInt64(Number.NaN, 0);

View File

@ -0,0 +1,77 @@
#include <js_native_api.h>
#include "../common.h"
// Unifies the way the macros declare values.
typedef double double_t;
#define BINDING_FOR_CREATE(initial_capital, lowercase) \
static napi_value Create##initial_capital(napi_env env, \
napi_callback_info info) { \
napi_value return_value, call_result; \
lowercase##_t value = 42; \
NODE_API_CALL(env, napi_create_object(env, &return_value)); \
add_returned_status(env, \
"envIsNull", \
return_value, \
"Invalid argument", \
napi_invalid_arg, \
napi_create_##lowercase(NULL, value, &call_result)); \
napi_create_##lowercase(env, value, NULL); \
add_last_status(env, "resultIsNull", return_value); \
return return_value; \
}
#define BINDING_FOR_GET_VALUE(initial_capital, lowercase) \
static napi_value GetValue##initial_capital(napi_env env, \
napi_callback_info info) { \
napi_value return_value, call_result; \
lowercase##_t value = 42; \
NODE_API_CALL(env, napi_create_object(env, &return_value)); \
NODE_API_CALL(env, napi_create_##lowercase(env, value, &call_result)); \
add_returned_status( \
env, \
"envIsNull", \
return_value, \
"Invalid argument", \
napi_invalid_arg, \
napi_get_value_##lowercase(NULL, call_result, &value)); \
napi_get_value_##lowercase(env, NULL, &value); \
add_last_status(env, "valueIsNull", return_value); \
napi_get_value_##lowercase(env, call_result, NULL); \
add_last_status(env, "resultIsNull", return_value); \
return return_value; \
}
BINDING_FOR_CREATE(Double, double)
BINDING_FOR_CREATE(Int32, int32)
BINDING_FOR_CREATE(Uint32, uint32)
BINDING_FOR_CREATE(Int64, int64)
BINDING_FOR_GET_VALUE(Double, double)
BINDING_FOR_GET_VALUE(Int32, int32)
BINDING_FOR_GET_VALUE(Uint32, uint32)
BINDING_FOR_GET_VALUE(Int64, int64)
void init_test_null(napi_env env, napi_value exports) {
const napi_property_descriptor test_null_props[] = {
DECLARE_NODE_API_PROPERTY("createDouble", CreateDouble),
DECLARE_NODE_API_PROPERTY("createInt32", CreateInt32),
DECLARE_NODE_API_PROPERTY("createUint32", CreateUint32),
DECLARE_NODE_API_PROPERTY("createInt64", CreateInt64),
DECLARE_NODE_API_PROPERTY("getValueDouble", GetValueDouble),
DECLARE_NODE_API_PROPERTY("getValueInt32", GetValueInt32),
DECLARE_NODE_API_PROPERTY("getValueUint32", GetValueUint32),
DECLARE_NODE_API_PROPERTY("getValueInt64", GetValueInt64),
};
napi_value test_null;
NODE_API_CALL_RETURN_VOID(env, napi_create_object(env, &test_null));
NODE_API_CALL_RETURN_VOID(
env,
napi_define_properties(env,
test_null,
sizeof(test_null_props) / sizeof(*test_null_props),
test_null_props));
NODE_API_CALL_RETURN_VOID(
env, napi_set_named_property(env, exports, "testNull", test_null));
}

View File

@ -0,0 +1,8 @@
#ifndef TEST_JS_NATIVE_API_TEST_NUMBER_TEST_NULL_H_
#define TEST_JS_NATIVE_API_TEST_NUMBER_TEST_NULL_H_
#include <js_native_api.h>
void init_test_null(napi_env env, napi_value exports);
#endif // TEST_JS_NATIVE_API_TEST_NUMBER_TEST_NULL_H_

View File

@ -0,0 +1,18 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const { testNull } = require(`./build/${common.buildType}/test_number`);
const expectedCreateResult = {
envIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
};
const expectedGetValueResult = {
envIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
};
[ 'Double', 'Int32', 'Uint32', 'Int64' ].forEach((typeName) => {
assert.deepStrictEqual(testNull['create' + typeName](), expectedCreateResult);
assert.deepStrictEqual(testNull['getValue' + typeName](), expectedGetValueResult);
});

View File

@ -0,0 +1,110 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
#include "test_null.h"
static napi_value Test(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_number,
"Wrong type of arguments. Expects a number as first argument.");
double input;
NODE_API_CALL(env, napi_get_value_double(env, args[0], &input));
napi_value output;
NODE_API_CALL(env, napi_create_double(env, input, &output));
return output;
}
static napi_value TestUint32Truncation(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_number,
"Wrong type of arguments. Expects a number as first argument.");
uint32_t input;
NODE_API_CALL(env, napi_get_value_uint32(env, args[0], &input));
napi_value output;
NODE_API_CALL(env, napi_create_uint32(env, input, &output));
return output;
}
static napi_value TestInt32Truncation(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_number,
"Wrong type of arguments. Expects a number as first argument.");
int32_t input;
NODE_API_CALL(env, napi_get_value_int32(env, args[0], &input));
napi_value output;
NODE_API_CALL(env, napi_create_int32(env, input, &output));
return output;
}
static napi_value TestInt64Truncation(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_number,
"Wrong type of arguments. Expects a number as first argument.");
int64_t input;
NODE_API_CALL(env, napi_get_value_int64(env, args[0], &input));
napi_value output;
NODE_API_CALL(env, napi_create_int64(env, input, &output));
return output;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("Test", Test),
DECLARE_NODE_API_PROPERTY("TestInt32Truncation", TestInt32Truncation),
DECLARE_NODE_API_PROPERTY("TestUint32Truncation", TestUint32Truncation),
DECLARE_NODE_API_PROPERTY("TestInt64Truncation", TestInt64Truncation),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
init_test_null(env, exports);
return exports;
}
EXTERN_C_END

Some files were not shown because too many files have changed in this diff Show More