nodejs / abi-stable-node Goto Github PK
View Code? Open in Web Editor NEWRepository used by the Node-API team to manage work related to Node-API and node-addon-api
Repository used by the Node-API team to manage work related to Node-API and node-addon-api
C:\git\leveldown>..\NAPI-Node-Chakra\node.exe test\iterator-test.js
TAP version 13
# setUp common
ok 1 cleanup returned an error
# setUp db
TypeError: Object expected
at LevelDOWN (C:\git\leveldown\leveldown.js:15:3)
at LevelDOWN (C:\git\leveldown\leveldown.js:12:5)
at Anonymous function (C:\git\leveldown\node_modules\abstract-leveldown\abstract\iterator-test.js:26:5)
at bound (C:\git\leveldown\node_modules\tape\lib\test.js:61:21)
at Test.prototype.run (C:\git\leveldown\node_modules\tape\lib\test.js:80:5)
at bound (C:\git\leveldown\node_modules\tape\lib\test.js:61:21)
at next (C:\git\leveldown\node_modules\tape\lib\results.js:70:13)
at runCallback (timers.js:570:7)
at tryOnImmediate (timers.js:550:5)
at processImmediate (timers.js:529:5)
I found multiple problems with the current implementations of napi_get_string_from_value
:
memcpy
with the length of the string, where the length does not include theJsCopyStringUtf8
does copyThat last item in the list above causes the test_string
and test_object
test cases to fail in the NAPI
chakracore branch.
Michael and Sampson to get together to add 0.12.x port into the repo
NAPI is currently missing support for property accessors, to enable C++ callbacks for getters and setters of properties on a wrapped object.
NAN supports this via the Nan::SetAccessor()
function, which eventually calls v8::Object::SetAccessor()
or v8::ObjectTemplate::SetAccessor()
. NAPI currently has functions for getting and setting property values, but not for setting accessors.
From the data I collected, 5 of the top 12 native packages use one or both of those APIs: canvas, fibers, nodegit, sqlite3, zmq. The canvas module implements accessors for several properties like canvas.width
, canvas.height
, etc. The sqlite3 module only has a single getter for database.open
.
/cc: @gabrielschulhof
NAPI's call-function API is defined like this:
napi_value napi_call_function(napi_env e, napi_value scope,
napi_value func, int argc, napi_value* argv);
What is that scope parameter supposed to be? Both the V8 and JSRT implementations treat it as the this
argument for the function call, which makes sense except for the scope
parameter name. Is the parameter mis-named?
This name confusion apparently has caused a problem in the function test case, which passes a napi_escapable_handle_scope
as the scope parameter after casting it to a napi_value
. When running the test on a NAPI V8 branch, the test passes, I assume because the function itself does not reference its this
arg. But on the chakracore branch, the JSRT function call tries to validate that the parameter is an Object and returns an error code, so the function call fails.
When we use NAPI to write native module, the API is vm neutral and stable. But the compiling is still binding to vm implementation, like, using the vm shared library.
For example, "napi_set_return_value" needs to link with the its implementation in the node_jsvmapi.cc, which is vm related.
I think if it was better to separate the API implementation, make the compiling of native module totally vm neutral, and to be binary compatible for different vms?
Below are some simple ideas:
We may use the env pointer to store the API functions' pointers, and bind them to implemented functions in vm in runtime.
The API declaration and implementation look like:
node_jsvmapi.h:
// The API functions are stored in env.
struct napi_env__ {
...
void (*napi_set_return_value)(napi_env e, napi_func_cb_info cbinfo,
napi_value v)
...
};
and:
node_jsvmapi.cc:
// API VM implementations.
void napi_set_return_value(napi_env e, napi_func_cb_info cbinfo,
napi_value v) {
...
}
// Corresponding to functions in node_jsvmapi.h
struct napi_env__ api_functions = {
...
napi_set_return_value
...
};
when vm loads the module, we bind the API implementation to env pointer.
bind_api_functions_to_env(api_functions, env)
So finally, native method(hello.cc) looks like:
#include <node_jsvmapi.h>
void Method(napi_env env, napi_func_cb_info info) {
// Call using env.
env->napi_set_return_value(
info,
env->napi_create_string("world"));
}
Then we just need the only node_jsvmapi.h to compile and link native module(no need of vm shared library or environment).
While porting the canvas package to NAPI, I found the canvas code calls Nan::AdjustExternalMemory()
in a few places, to give some hints to the V8 GC. That NAN API is a wrapper around v8::Isolate::AdjustAmountOfExternalAllocatedMemory()
. It's not critical for the functionality, but I assume it improves GC behavior significantly when working with large amounts of externally-allocated memory (image buffers in the case of canvas).
This is a V8-specific API; JSRT does not appear to have an equivalent. Do we need to expose it in NAPI? Possibly it could be implemented as a no-op on other engines.
The error handling modifications fail to build with GCC.
Node v0.10 differs from rest of the later Node versions in Module registration and initialization functionality. NAPI module registration needs to account for node v0.10.
This is the hangout for the next meeting : https://plus.google.com/u/0/events/c0eevtrlajniu7h8cjrdk0f56c8
✅ Milestone 1 - Initial API Implementation (DONE except landing EP)
leveldown
and nanomsg
)✅ Milestone 2 - Ports across versions and runtimes
✅ Milestone 3 - Demo for Node Interactive
✅ Milestone 4 - Full error handling
canvas
✅ Milestone 5 - API feature complete (VM summit Readiness)
IoTivity
node-sass
canvas
✅ Milestone 6 - Land in master as Experimental - Targeting Node version 8.0
Milestone 7 - Community Outreach
Not sure if I'm on the right track, but here's some code that will call a module's init on all versions of node from 0.10 to 4.5.0 (at least). I haven't yet dealt with passing the arguments that Init receives in a node-agnostic fashion, but hence the WIP :)
#include <stdio.h>
void Init() {
printf("Init\n");
}
struct old_module {
int version;
void *aPointer;
const char *filename;
void (*entry_point)(void);
const char *module_name;
};
struct new_module {
int version;
int something;
void *aPointer;
const char *filename;
void (*entry_point)(void);
void *anotherPointer;
const char *module_name;
void *yetAnotherPointer;
void *andAnotherPointer;
};
// 0.10
extern "C" {
__attribute__((visibility("default")))
struct old_module hello_module = {
.version = 11,
.aPointer = __null,
.filename = "../hello.cc",
.entry_point = Init,
.module_name = "hello"
};
}
// 0.12
extern "C" {
static struct new_module _module = {
.version = 14 /* 46 for 4.0.0 */,
.something = 0,
.aPointer = __null,
.filename = "../hello.cc",
.entry_point = Init,
.anotherPointer = __null,
.module_name = "hello",
.yetAnotherPointer = __null,
.andAnotherPointer = __null
};
void node_module_register(void *module) __attribute__((weak));
unsigned int uv_version(void) __attribute((weak));
static void _register_hello(void) __attribute__((constructor));
static void _register_hello(void) {
if (node_module_register) {
if ((uv_version ? uv_version() : 0) >= 0x10703) {
_module.version = 46;
}
node_module_register(&_module);
}
}
}
I'd be interested in porting nbind to use NAPI. Basically it's a replacement for, among others, NAN (Node.js parts of nbind are built on top of it). The idea is that you write ordinary C++ classes and functions using standard library types, and finally do:
#include "nbind/nbind.h"
NBIND_CLASS(YourClassName) {
method(yourMethod);
// etc.
}
You don't need to list the method argument or return types, they're all autodetected using template magic and type conversion code is generated by the C++ compiler as needed. It uses NAN when targeting Node.js addons, but the exact same C++ code can also be compiled using Emscripten to target asm.js and run it inside browsers.
You can have any number of NBIND_CLASS
blocks in any source files and it Just Works™ with the API accessible from JavaScript.
If ported to NAPI, again only the code inside nbind would need to change, while any addons using it will remain unchanged. Currently the only public NPM package that I know is using it, is libui-node
which provides cross-platform native UI widgets.
However, if nbind supported NAPI, it would be way less work to write future native addons targeting NAPI (through nbind) than using any other previously existing API.
Current NAPI implementation works with Node-ChakraCore via the ChakraShim. Ideally NAPI support should be implemented using the JSRT APIs for all direct dependencies.
Updated npm-pkg-top
to use the modern skimdb URLs. The results are below sorted by npm stars, github stars, and number of dependent public modules:
# npm git dep pkg
1 153 0 973 node-sass
2 95 1928 565 sqlite3
3 214 0 524 bcrypt
4 52 0 483 serialport
5 131 0 441 phantomjs
6 28 239 435 hiredis
7 75 3617 354 canvas
8 51 1525 326 websocket
9 40 0 272 phantomjs-prebuilt
10 15 261 256 deasync
11 14 358 217 leveldown
12 18 0 199 nodegit
13 40 2484 198 fibers
14 44 1473 189 zmq
15 21 520 169 iconv
16 48 0 167 electron-prebuilt
17 21 0 161 libxmljs
18 6 0 154 radium
19 16 1740 128 ffi
20 9 0 117 microtime
You can run this yourself by:
npm i -g npm-pkg-top
npm-pkg-top --save-query 'npm.json' --username='VALID_GITHUB_USERNAME' --password='VALID_GITHUB_PASS'
We've been making some progress on the ABI stable API (will provide an update in WG meeting nodejs/api#22)
This is to capture the current view of some of the major milestones for PoC:
webworker-threads
We are working on a number of ports of modules to the ABI Stable PoC. This issues is to provide links to to those ports:
Module | Status | Link | Branch |
---|---|---|---|
node-sqlite3 | Work in progress | https://github.com/mhdawson/node-sqlite3/tree/v3.1.4-abi | v3.1.4-abi |
node-nanomsg | Done | https://github.com/sampsongao/node-nanomsg/tree/api-opaque-prototype | api-opaque-prototype |
leveldown | Done | https://github.com/boingoing/leveldown/tree/api-opaque-prototype | api-opaque-prototype |
C:\git\demo>..\NAPI-Node-Chakra\node.exe app.js
Error: Module version mismatch. Expected 48, got -1.
at bindings (C:\git\demo\node_modules\bindings\bindings.js:83:9)
at Anonymous function (C:\git\demo\node_modules\leveldown\leveldown.js:4:7)
at Module.prototype._compile (module.js:541:3)
at Module._extensions[.js] (module.js:550:3)
at Module.prototype.load (module.js:458:3)
at tryModuleLoad (module.js:417:5)
at Module._load (module.js:409:3)
at Module.prototype.require (module.js:468:3)
at require (internal/module.js:20:7)
at Anonymous function (C:\git\demo\app.js:1:63)
NAPI should expose APIs for JS typed arrays.
Of the top native modules that I scanned, canvas
was the only one that used typed arrays. But there must also be some less popular native modules that need them.
As I start to update nanomsg code to use error handling api, I found that it is quite hard to use.
For example:
NAPI_METHOD(Shutdown) {
napi_value args[2];
napi_get_cb_args(env, info, args, 2);
int s = napi_get_value_int64(env, args[0], &s);
int how = TRY_CATCH(napi_get_value_int64(env, args[1], &how);
napi_value ret = napi_create_number(env, nn_shutdown(s, how), &ret);
napi_set_return_value(env, info, ret);
}
will be converted to the following:
#define CHECK_STATUS \
if (status != napi_ok) { \
return; \
}
NAPI_METHOD(Shutdown) {
napi_status status;
napi_value args[2];
status = napi_get_cb_args(env, info, args, 2);
CHECK_STATUS;
int s;
status = napi_get_value_int64(env, args[0], &s);
CHECK_STATUS;
int how;
status = napi_get_value_int64(env, args[1], &how);
CHECK_STATUS;
napi_value ret;
status = napi_create_number(env, nn_shutdown(s, how), &ret);
CHECK_STATUS;
status = napi_set_return_value(env, info, ret);
CHECK_STATUS;
}
So I think alternatively we should let developer to pass in napi_status to get the status, e.g.
napi_value napi_create_number(napi_env env, int num, napi_status* status);
This way, the above code can be converted as:
#define CHECK_STATUS \
if (status != napi_ok) { \
return; \
}
NAPI_METHOD(Shutdown) {
napi_status status;
napi_value args[2];
napi_get_cb_args(env, info, args, 2);
int s = napi_get_value_int64(env, args[0], &status);
CHECK_STATUS;
int how = napi_get_value_int64(env, args[1], &status)
CHECK_STATUS;
napi_value ret = napi_create_number(env, nn_shutdown(s, how), &status);
CHECK_STATUS;
napi_set_return_value(env, info, ret, &status);
CHECK_STATUS;
}
This allows declaration on same line and nested calls where possible. I think this can be more flexible.
@aruneshchandra, @boingoing, @jasongin, @gabrielschulhof Please comment about what you think.
Publish and review design document for NAPI error handling
Comment from Ben in nodejs/node-eps#20 (comment), we'll need to see if:
NODE_EXTERN void napi_set_function_name(napi_env e, napi_value func,
+ napi_propertyname napi_value);
will work with other engines like spidermonkey were the name it declared up front.
After commit 8413f2a we are seeing a Linker error in LevelDown
I realized from #33 (comment) that we may want to have such an API because AFAIK all engines provide this functionality.
Hi,
could you do me a favor and add a local PR to your development branch (api-prototype ?). I'd like to follow the development and having a PR makes browsing the change set nice and easy.
node_jsvmapi.h
currently says:
// Consider: Would it be useful to include an optional void* for external data
// to be included on the function object? A finalizer function would also need
// to be included. Same for napi_create_object, and perhaps add a
// napi_set_external_data() and get api? Consider JsCreateExternalObject and
// v8::ObjectTemplate::SetInternalFieldCount()
NODE_EXTERN napi_value napi_create_function(napi_env e, napi_callback cbinfo);
nbind creates one C++ wrapper function for each signature (combination of argument and return types), so it then needs to pass a number telling which C++ function among those with identical signatures is actually referred to by that napi_value
. NAN allows doing:
Local<FunctionTemplate> functionTemplate = Nan::New<FunctionTemplate>(
functionPointer,
Nan::New<v8::External>(data)
);
It stores the data pointer in an internal field:
v8::Local<v8::Value> val = v8::Local<v8::Value>::New(isolate, data);
if (!val.IsEmpty()) {
obj->SetInternalField(imp::kDataIndex, val);
}
It will then read that field when calling the C++ function, passing it inside the equivalent of napi_func_cb_info
:
FunctionCallbackInfo<v8::Value>
cbinfo(info, obj->GetInternalField(kDataIndex));
callback(cbinfo);
So the NAPI equivalent could be a function like:
napi_value napi_get_cb_data(napi_env e, napi_func_cb_info cbinfo);
I don't think there's a need for a finalizer function here? Instead if needed, the programmer could pass the data value to napi_create_weakref
which should also allow passing it a finalizer. Currently it doesn't seem to take one and just sets up this empty finalizer instead:
static void WeakRefCallback(const v8::WeakCallbackInfo<int>& data) {
}
Suggestions from Ben on nodejs/node-eps#20 (comment)
+
+### Types
+```C
+typedef struct napi_env__ *napi_env;
@bnoordhuis
bnoordhuis 5 hours ago Node.js Foundation member
Using typedef'd pointers has the issue that they will silently decay to void* when used in a void* context. Can be avoided by wrapping the pointer in a struct, like so:
typedef struct napi_value { struct napi_value *v; } napi_value;
+
+struct napi_method_descriptor {
+ napi_callback callback;
+ char* utf8name;
@bnoordhuis
bnoordhuis 5 hours ago Node.js Foundation member
const char*?
+NODE_EXTERN int napi_get_string_length(napi_env e, napi_value v);
+NODE_EXTERN int napi_get_string_utf8_length(napi_env e, napi_value v);
+NODE_EXTERN int napi_get_string_utf8(napi_env e, napi_value v,
+ char* buf, int bufsize);
@bnoordhuis
bnoordhuis 5 hours ago Node.js Foundation member
Why int instead of size_t?
+// Methods to support error handling
+NODE_EXTERN void napi_throw(napi_env e, napi_value error);
+NODE_EXTERN void napi_throw_error(napi_env e, char* msg);
+NODE_EXTERN void napi_throw_type_error(napi_env e, char* msg);
@bnoordhuis
bnoordhuis 5 hours ago Node.js Foundation member
const char*
Code that is converted from V8's C++ classes to NAPI's C function calls is often a little more verbose, e.g. napi_get_number_from_value(env, v)
vs v->NumberValue()
. This could be cleaned up somewhat by adding low/zero-overhead C++ helper classes in NAPI, allowing for more V8 style interaction with objects, and therefore easier porting.
$ ./configure --prefix=${HOME}/sw
...
$ make
...
make[1]: *** No rule to make target '/home/nix/node/abi-stable-node/out/Release
/obj.target/v8_libbase/deps/v8/src/base/debug/stack_trace.o', needed by '/home/
nix/node/abi-stable-node/out/Release/obj.target/deps/v8/src/libv8_libbase.a'.
Stop.
Is there a way around this?
At VM Summit 2 - some concern was raised if the current NAPI design can accommodate porting canvas
.
Comment from Ben in : nodejs/node-eps#20 (comment)
I think most JS engines have an 'is exception pending?' function; V8 is the outlier with its TryCatch construct. API strawman:
// Wrapper around a v8::TryCatch. Dummy with other engines.
typedef struct napi_trycatch { struct napi_trycatch *v; } napi_trycatch;
NODE_EXTERN void napi_trycatch_new(napi_env e, napi_trycatch *trycatch);
// Returns exception object or undefined.
NODE_EXTERN napi_value napi_trycatch_exception(napi_env e, napi_trycatch trycatch);
NODE_EXTERN void napi_trycatch_delete(napi_env e, napi_trycatch trycatch);
nbind currently detects Node.js using #ifdef BUILDING_NODE_EXTENSION
and Emscripten using #ifdef EMSCRIPTEN
. How would I distinguish whether the Node.js version supports NAPI, without using other build tools in the module besides Gyp?
Could there be a macro like NODE_USE_NAPI
or similar defined in common.gypi
?
ChakraCore (and possibly other engines) do not support changing the function name after creating a function. Let's add an optional name parameter to napi_create_function instead. If it's possible, can we also remove napi_set_function_name?
Based on recent discussion its probably time to add CI jobs as a safety net. We should use the core node.js infrastructure so we can easily integrate the jobs into the regular regression tests once we merge into master.
I'm starting the process following :
https://github.com/nodejs/build/blob/master/doc/process/jenkins_job_configuation_access.md
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.