GithubHelp home page GithubHelp logo

nodejs / abi-stable-node Goto Github PK

View Code? Open in Web Editor NEW
239.0 239.0 47.0 233.92 MB

Repository used by the Node-API team to manage work related to Node-API and node-addon-api

JavaScript 100.00%
node nodejs

abi-stable-node's People

Contributors

anisha-rohra avatar aruneshchandra avatar ddlan avatar digitalinfinity avatar jasongin avatar jschlight avatar kevineady avatar kfarnung avatar legendecas avatar mhdawson avatar nicknaso avatar scholtzm avatar toyobayashi avatar vmoroz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

abi-stable-node's Issues

LevelDown built against NAPI Node 6.x fails with NAPI Node-ChakraCore

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)

Bugs in napi_get_string_from_value

I found multiple problems with the current implementations of napi_get_string_from_value:

  • The function is currently defined as returning the number of bytes remaining in
    the buffer. This is different from both V8 and JSRT string APIs, which always return the number of bytes
    written. I think this behavior will definitely confuse some people and cause bugs, so it should be fixed.
  • In the part of the V8 implementation that copies a stringified number value, the null-terminator byte is
    not copied: it just does a memcpy with the length of the string, where the length does not include the
    terminator.
  • The JSRT implementation does not handle numbers like the V8 implementation does.
  • If it's possible to get a string from a number value, why not other primitive types (symbol, boolean,
    null/undefined)? We should check the V8 behavior for all of those and try to be consistent.
  • The JSRT implementation does not copy the null terminator. It assumes JsCopyStringUtf8 does copy
    the terminator (it does not), and that incorrect assumption also leads to an incorrect
    remaining return value.

That last item in the list above causes the test_string and test_object test cases to fail in the NAPI
chakracore branch.

Property accessors

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.

napi_call_function scope parameter

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.

Making the compiling of native module independent of vm environment

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).

AdjustExternalMemory

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.

Roadmap

Roadmap

Milestone 1 - Initial API Implementation (DONE except landing EP)

Milestone 2 - Ports across versions and runtimes

  • Ports to ChakraCore (7.x)
  • Ports across versions (0.10.x, 0.12.x, 6.2.x, 7.x)
  • Performance Eval of initial implementation

Milestone 3 - Demo for Node Interactive

Milestone 4 - Full error handling

  • Land EP as a draft after CTC Review
  • Addition of full error handling
  • Broad analysis of V8 API usage in top 30 most downloaded native modules
  • Investigate canvas
  • Evaluate Performance

Milestone 5 - API feature complete (VM summit Readiness)

  • Full API checked in to abi-stable-node and up to date with current Node.js master
  • Update ports to head of v6, v8, ChakraCore-master
  • Enable build flag for experimental support for NAPI
  • Complete porting IoTivity
  • Convert most depended on native module node-sass
  • Convert native module from CITGM List canvas
  • Community review checkpoint and update EPS
  • Demo

Milestone 6 - Land in master as Experimental - Targeting Node version 8.0

  • Submit PR to Node master
  • Land N-API in Node 8.0

Milestone 7 - Community Outreach

  • Documentation for migrating existing Native modules to NAPI
  • Automation for Migration assistance
  • Blog: call to action for community / early adopters

WIP: Common module registration for all versions of node

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);
        }
    }
}

Porting nbind to use NAPI

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.

[Discussion] List of most popular binary modules

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'

ABI Stable API - milestones

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:

  • First example running
  • All NaN examples running
  • Module lifecycle methods
  • Robust Object life-cycle handling
  • Create/GetData methods for all primitive javascript types
  • Complete conversion functions for primitive types
  • Exception Handling methods complete
  • Error handling methods complete
  • Buffer methods complete
  • Version management methods complete
  • Level down running
  • nanomessage running
  • module 3 running
  • leveldown performance analysis
  • module 3 performance analysis
  • nanomessage performance analysis
  • understand impact of multi-isolate modules, e.g. webworker-threads
  • Test suite complete
  • Safety/Usability Analysis complete
  • Validate API copes with other implementations:
    • Port back to 0.10.X and/or
    • Port back to 0.12.X and/or
    • Port to NodeChakra and/or
    • Port to SpiderNode
    • DukTape?
  • Fill out rest of C API
  • NaN/C++ wrapper feasibility analysis complete
  • Make AsyncWorkers ABI stable.

Module version mismatch when using NAPI enabled Node-ChakraCore 7.x

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)

Typed arrays

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.

Alternative format for api with error handling

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.

Add local PR to improve visibility

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.

Adding a data pointer for calling a C function

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) {
}

See here and here.

Tweaks to API suggested on EPS

  • Version: All
  • Platform: ALL
  • Subsystem: API

Suggestions from Ben on nodejs/node-eps#20 (comment)

  1. 00X-ABI-Stable-Module-API.md
+
+### 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;

  1. 00X-ABI-Stable-Module-API.md
+
+struct napi_method_descriptor {
+  napi_callback callback;
+  char* utf8name;
@bnoordhuis
bnoordhuis 5 hours ago Node.js Foundation member

const char*?
  1. 00X-ABI-Stable-Module-API.md
+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?

  1. 00X-ABI-Stable-Module-API.md
+// 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*

C++ helper classes

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.

Building the api-prototype-chakracore-7.x branch on Linux

$ ./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?

Correct abstraction for try/catch

  • Version: ALL
  • Platform: ALL
  • Subsystem: API

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);

Detecting NAPI

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?

Revisit need for napi_set_function_name

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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.