Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

node-api: allow retrieval of add-on file name #37195

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions doc/api/n-api.md
Expand Up @@ -5958,6 +5958,31 @@ idempotent.

This API may only be called from the main thread.

## Miscellaneous utilities

## node_api_get_module_file_name

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

```c
NAPI_EXTERN napi_status
node_api_get_module_file_name(napi_env env, const char** result);

```

* `[in] env`: The environment that the API is invoked under.
* `[out] result`: A URL containing the absolute path of the
location from which the add-on was loaded. For a file on the local
file system it will start with `file://`. The string is null-terminated and
owned by `env` and must thus not be modified or freed.

`result` may be an empty string if the add-on loading process fails to establish
the add-on's file name during loading.

[ABI Stability]: https://nodejs.org/en/docs/guides/abi-stability/
[AppVeyor]: https://www.appveyor.com
[C++ Addons]: addons.md
Expand Down
1 change: 1 addition & 0 deletions src/env.h
Expand Up @@ -253,6 +253,7 @@ constexpr size_t kFsStatsBufferLength =
V(fd_string, "fd") \
V(fields_string, "fields") \
V(file_string, "file") \
V(filename_string, "filename") \
V(fingerprint256_string, "fingerprint256") \
V(fingerprint_string, "fingerprint") \
V(flags_string, "flags") \
Expand Down
45 changes: 39 additions & 6 deletions src/node_api.cc
Expand Up @@ -15,8 +15,9 @@
#include <memory>

struct node_napi_env__ : public napi_env__ {
explicit node_napi_env__(v8::Local<v8::Context> context):
napi_env__(context) {
explicit node_napi_env__(v8::Local<v8::Context> context,
const std::string& module_filename):
napi_env__(context), filename(module_filename) {
CHECK_NOT_NULL(node_env());
}

Expand Down Expand Up @@ -46,6 +47,10 @@ struct node_napi_env__ : public napi_env__ {
});
});
}

const char* GetFilename() const { return filename.c_str(); }

std::string filename;
};

typedef node_napi_env__* node_napi_env;
Expand Down Expand Up @@ -87,10 +92,11 @@ class BufferFinalizer : private Finalizer {
};
};

static inline napi_env NewEnv(v8::Local<v8::Context> context) {
static inline napi_env
NewEnv(v8::Local<v8::Context> context, const std::string& module_filename) {
node_napi_env result;

result = new node_napi_env__(context);
result = new node_napi_env__(context, module_filename);
// TODO(addaleax): There was previously code that tried to delete the
// napi_env when its v8::Context was garbage collected;
// However, as long as N-API addons using this napi_env are in place,
Expand Down Expand Up @@ -552,16 +558,35 @@ void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context,
napi_addon_register_func init) {
node::Environment* node_env = node::Environment::GetCurrent(context);
std::string module_filename = "";
if (init == nullptr) {
node::Environment* node_env = node::Environment::GetCurrent(context);
CHECK_NOT_NULL(node_env);
node_env->ThrowError(
"Module has no declared entry point.");
return;
}

// We set `env->filename` from `module.filename` here, but we could just as
// easily add a private property to `exports` in `process.dlopen`, which
// receives the file name from JS, and retrieve *that* here. Thus, we are not
// endorsing commonjs here by making use of `module.filename`.
v8::Local<v8::Value> filename_js;
v8::Local<v8::Object> modobj;
if (module->ToObject(context).ToLocal(&modobj) &&
modobj->Get(context, node_env->filename_string()).ToLocal(&filename_js) &&
filename_js->IsString()) {
node::Utf8Value filename(node_env->isolate(), filename_js); // Cast

// Turn the absolute path into a URL. Currently the absolute path is always
// a file system path.
// TODO(gabrielschulhof): Pass the `filename` through unchanged if/when we
// receive it as a URL already.
module_filename = std::string("file://") + (*filename);
}

// Create a new napi_env for this specific module.
napi_env env = v8impl::NewEnv(context);
napi_env env = v8impl::NewEnv(context, module_filename);

napi_value _exports;
env->CallIntoModule([&](napi_env env) {
Expand Down Expand Up @@ -1257,3 +1282,11 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
CHECK_NOT_NULL(func);
return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Ref();
}

napi_status node_api_get_module_file_name(napi_env env, const char** result) {
CHECK_ENV(env);
CHECK_ARG(env, result);

*result = static_cast<node_napi_env>(env)->GetFilename();
return napi_clear_last_error(env);
}
3 changes: 3 additions & 0 deletions src/node_api.h
Expand Up @@ -261,6 +261,9 @@ NAPI_EXTERN napi_status napi_add_async_cleanup_hook(
NAPI_EXTERN napi_status napi_remove_async_cleanup_hook(
napi_async_cleanup_hook_handle remove_handle);

NAPI_EXTERN napi_status
node_api_get_module_file_name(napi_env env, const char** result);

#endif // NAPI_EXPERIMENTAL

EXTERN_C_END
Expand Down
7 changes: 6 additions & 1 deletion test/node-api/test_general/test.js
@@ -1,9 +1,14 @@
'use strict';

const common = require('../../common');
const test_general = require(`./build/${common.buildType}/test_general`);
const filename = require.resolve(`./build/${common.buildType}/test_general`);
const test_general = require(filename);
const assert = require('assert');

// TODO(gabrielschulhof): This test may need updating if/when the filename
// becomes a full-fledged URL.
assert.strictEqual(test_general.filename, `file://${filename}`);

const [ major, minor, patch, release ] = test_general.testGetNodeVersion();
assert.strictEqual(process.version.split('-')[0],
`v${major}.${minor}.${patch}`);
Expand Down
13 changes: 13 additions & 0 deletions test/node-api/test_general/test_general.c
@@ -1,3 +1,4 @@
#define NAPI_EXPERIMENTAL
#include <node_api.h>
#include <stdlib.h>
#include "../../js-native-api/common.h"
Expand All @@ -20,9 +21,21 @@ static napi_value testGetNodeVersion(napi_env env, napi_callback_info info) {
return result;
}

static napi_value GetFilename(napi_env env, napi_callback_info info) {
const char* filename;
napi_value result;

NODE_API_CALL(env, node_api_get_module_file_name(env, &filename));
NODE_API_CALL(env,
napi_create_string_utf8(env, filename, NAPI_AUTO_LENGTH, &result));

return result;
}

static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("testGetNodeVersion", testGetNodeVersion),
DECLARE_NODE_API_GETTER("filename", GetFilename),
};

NODE_API_CALL(env, napi_define_properties(
Expand Down