Skip to content

Conversation

@kraenhansen
Copy link
Contributor

@kraenhansen kraenhansen commented Dec 10, 2025

Merging this PR will:

  • Add and inject a Node.js specific loadAddon function into tests, based on the process.dlopen function.
  • Add a harness test to check the injection of the loadAddon function.
  • Add a root CMakeLists.txt declaring a add_node_api_cts_addon helper called from the test directory setup a target for the test's addon.
  • Copy over the 2_function_arguments.c,common-inl.h and entry_point.h as is (with the exception of one change in the latter).

Comment on lines +1 to +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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +1 to +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_
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +1 to +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_
Copy link
Contributor Author

@kraenhansen kraenhansen Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

napi_value Init(napi_env env, napi_value exports);
EXTERN_C_END

NAPI_MODULE(ADDON_NAME, Init)
Copy link
Contributor Author

@kraenhansen kraenhansen Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this from

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

Since we're not using Node GYP.

Comment on lines +1 to +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(ADDON_NAME, Init)

#endif // JS_NATIVE_API_ENTRY_POINT_H_
Copy link
Contributor Author

@kraenhansen kraenhansen Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copied with a single line changed from nodejs/node/test/js-native-api/entry_point.h

Comment on lines +1 to +6
const addon = loadAddon('2_function_arguments');

const { add } = addon;

assert(typeof add === "function");
assert(add(3, 5) === 8);
Copy link
Contributor Author

@kraenhansen kraenhansen Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kraenhansen kraenhansen marked this pull request as draft December 10, 2025 10:20
@kraenhansen

This comment was marked as outdated.

@kraenhansen kraenhansen force-pushed the engine-function-arguments branch from 168d39e to 14a2fbe Compare December 12, 2025 07:44
@kraenhansen kraenhansen marked this pull request as ready for review December 12, 2025 07:49
@kraenhansen
Copy link
Contributor Author

Instead of getting the test directory using the call-sites, I changed the implementation to spawn the test file with the file's parent directory as current working directory and updated the load-addon.js to rely on that.

@kraenhansen kraenhansen force-pushed the engine-function-arguments branch from 8b56135 to 255abb8 Compare December 12, 2025 08:27
Comment on lines 42 to 44
run: npm run node:test
env:
NODE_OPTIONS: --import=${{ github.workspace }}/tools/strip.js

This comment was marked as outdated.

@kraenhansen kraenhansen force-pushed the engine-function-arguments branch from 255abb8 to bc89a1e Compare December 12, 2025 15:56
This was referenced Dec 12, 2025
@legendecas legendecas moved this from Need Triage to In Progress in Node-API Team Project Dec 12, 2025
@kraenhansen kraenhansen force-pushed the engine-function-arguments branch from fd0155f to a005ed3 Compare December 13, 2025 07:27
@kraenhansen kraenhansen force-pushed the engine-function-arguments branch 3 times, most recently from 3ddc612 to 875fff9 Compare December 14, 2025 20:01
@kraenhansen kraenhansen force-pushed the engine-function-arguments branch from a6ba54c to e666eab Compare December 14, 2025 22:22
Copy link
Member

@legendecas legendecas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@legendecas legendecas merged commit 468d868 into nodejs:main Dec 22, 2025
13 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in Node-API Team Project Dec 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

2 participants