From 492735c7bdef6dd9f2fd2fcc144625a797f0b9ed Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Mon, 12 Dec 2016 16:22:25 -0500 Subject: [PATCH] address more comments in preparation to land as draft --- 005-ABI-Stable-Module-API.md | 127 ++++++++++++ 00X-ABI-Stable-Module-API.md | 377 ----------------------------------- 2 files changed, 127 insertions(+), 377 deletions(-) create mode 100644 005-ABI-Stable-Module-API.md delete mode 100644 00X-ABI-Stable-Module-API.md diff --git a/005-ABI-Stable-Module-API.md b/005-ABI-Stable-Module-API.md new file mode 100644 index 0000000..c9e3b00 --- /dev/null +++ b/005-ABI-Stable-Module-API.md @@ -0,0 +1,127 @@ +| Title | ABI Stable Module API | +|--------|---------------------------------------| +| Author | @mhdawson, @stefanbu, @Ianwjhalliday | +| Status | DRAFT | +| Date | 2016-12-12 | + +# Introduction + +The module ecosystem is one of the features making Node.js the fastest +growing runtime. An important subset of modules are those which have a +native component. In specific cases it is important to be able to leverage native +code in order to be able to: + +* use existing third party components written in C/C++ +* access physical devices, for example a serial port +* expose functionality from the operating system not otherwise available +* achieve higher speed than possible in native JavaScript + +There is an existing interface (addons) that allows you to build these modules +as a dynamically-linked shared module that can be loaded into Node.js and +used as an ordinary Node.js module (for more details see +https://nodejs.org/api/addons.html) + +Unfortunately in the existing implementation the V8 +(the JavaScript runtime for Node.js) APIs are exposed to modules. In +addition Google changes these APIs on a regular basis. This results in +the following issues: + +* Native modules must be recompiled for each version of Node.js +* The code within modules may need to be modified for a new version of + Node.js +* It's not clear which parts of the V8 API the Node.js community believes + are safe/unsafe to use in terms of long term support for that use. +* Modules written against the V8 APIs may or may not be able to work + with alternate JavaScript engines when/if Node.js supports them. + +The Native Abstractions for Node (NAN) +project (https://github.com/nodejs/nan) can be used to limit +the cases when modules must be updated in order to work with new versions +of Node.js but does not address the other issues as it does not +encapsulate the V8 argument types such that modules still need +to be recompiled for new Node.js versions. + +# Proposed Action + +The goal of this eps is to define an API that: + +* Provides a stable API which is not tied to the current + or further APIs provided by V8 +* Allows native modules built as shared libraries to be used + with different versions of Node.js +* Makes it clear which APIs can use with the expectation + of not being affected by changes in the JavaScript engine + or Node.js release +* The API provided by the Node.js runtime will be in C. In + addition a C++ wrapper which only contains inline functions + and uses the C APIs so that the changes in the C++ wrapper + would not require recompilation. + +# Background + +There have been ongoing discussions on a new module API in both the +community api working group (https://github.com/nodejs/api), other +issues in the Node.js issue tracker, and the recent community Node.js +face-to-face VM summit (https://github.com/jasnell/vm-summit). + +In that summit there was agreement that we wanted to define this +module API. The general approach was to be: + +* Start with NAN, trim down to minimal subset based on module usage + and inspection. +* Add in VM agnostic methods for object lifecycle management. +* Add in VM agnostic exception management +* Define C/C++ types for the API which are independent from V8 + (NAN currently uses the V8 types). +* Use the NAN examples to work through the API and build up the + definition of the key methods. +* Validate on most used modules in the ecosystem +* Complete performance analysis to confirm accepted level of overhead. + +Notes/details on other related work and the current experimental progress +on implementing this eps can be found in: +https://docs.google.com/document/d/1bX9SZKufbfhr7ncXL9fFev2LrljymtFZdWpwmv7mB9g/edit#heading=h.ln9aj9qjo969 + +# API definition + +**WORK IN PROGRESS, DRIVEN BY MODULES PORTED SO FAR** + +This API will be built up from a minimal set. Based on this set +we have been able to port the NaN example and level down. Additions +will be made as we port additional modules. + +There has been enough progress that instead of listing the API methods +here we will point to the header files in the working repos so it +remains up to date: + +Core Methods: +[node_jsvmapi_types.h](https://github.com/nodejs/abi-stable-node/blob/api-prototype-6.2.0/src/node_jsvmapi_types.h) +[node_jsvmapi.h](https://github.com/nodejs/abi-stable-node/blob/api-prototype-6.2.0/src/node_jsvmapi.h) + +AsyncWorker Support Methods: + +[node_asyncapi_types.h](https://github.com/nodejs/abi-stable-node/blob/api-prototype-6.2.0/src/node_asyncapi_types.h) +[node_asyncapi.h](https://github.com/nodejs/abi-stable-node/blob/api-prototype-6.2.0/src/node_asyncapi.h) + + +# C++ Wrapper API + +The current view is that the API exported by the Node.js binary +must be in C in order to achieve ABI stability. This [document] +(http://www.oracle.com/technetwork/articles/servers-storage-dev/stablecplusplusabi-333927.html) +outlines some of the challenges with respect to C++ and +ABI stability. + +However, we also believe that a C++ wrapper is possible and +would provide ease of use. The key element is that the C++ part must all +be inlined and only use the external functions/types exported +by the C API. + +The current set of helpers to provide ease of use are defined in: + +[node_api_helpers.h](https://github.com/nodejs/abi-stable-node/blob/api-prototype-6.2.0/src/node_api_helpers.h) + +Some of the more complete parts like AsyncWorker will quite likely end up outside +the scope of the ABI stable API itself, and be in a separate component. +For example they may end up part of a version of NaN that supports the new API. + diff --git a/00X-ABI-Stable-Module-API.md b/00X-ABI-Stable-Module-API.md deleted file mode 100644 index 2db82f5..0000000 --- a/00X-ABI-Stable-Module-API.md +++ /dev/null @@ -1,377 +0,0 @@ -| Title | ABI Stable Module API | -|--------|---------------------------------------| -| Author | @mhdawson, @stefanbu, @Ianwjhalliday | -| Status | DRAFT | -| Date | 2016-08-11 | - -# Introduction - -The module ecosystem is one of the features making Node.js the fastest -growing runtime. An important subset of modules are those which have a -native component. In specific cases it is important to be able to leverage native -code in order to be able to: - -* use existing third party components written in C/C++ -* access physical devices, for example a serial port -* expose functionality from the operating system not otherwise available -* achieve higher speed than possible in native JavaScript - -There is an existing interface (addons) that allows you to build these modules -as a dynamically-linked shared module that can be loaded into Node.js and -used as an ordinary Node.js module (for more details see -https://nodejs.org/api/addons.html) - -Unfortunately in the existing implementation the V8 -(the JavaScript runtime for Node.js) APIs are exposed to modules. In -addition Google changes these APIs on a regular basis. This results in -the following issues: - -* Native modules must be recompiled for each version of Node.js -* The code within modules may need to be modified for a new version of - Node.js -* It's not clear which parts of the V8 API the Node.js community believes - are safe/unsafe to use in terms of long term support for that use. -* Modules written against the V8 APIs may or may not be able to work - with alternate JavaScript engines when/if Node.js supports them. - -The Native Abstractions for Node (NAN) -project (https://github.com/nodejs/nan) can be used to limit -the cases when modules must be updated in order to work with new versions -of Node.js but does not address the other issues as it does not -encapsulate the V8 argument types such that modules still need -to be recompiled for new Node.js versions. - -# Proposed Action - -The goal of this eps is to define an API that: - -* Provides a stable API which is not tied to the current - or further APIs provided by V8 -* Allows native modules built as shared libraries to be used - with different versions of Node.js -* Makes it clear which APIs can use with the expectation - of not being affected by changes in the JavaScript engine - or Node.js release -* The API provided by the Node.js runtime will be in C. In - addition a C++ wrapper which only contains inline functions - and uses the C APIs so that the changes in the C++ wrapper - would not require recompilation. - -# Background - -There have been ongoing discussions on a new module API in both the -community api working group (https://github.com/nodejs/api), other -issues in the Node.js issue tracker, and the recent community Node.js -face-to-face VM summit (https://github.com/jasnell/vm-summit). - -In that summit there was agreement that we wanted to define this -module API. The general approach was to be: - -* Start with NAN, trim down to minimal subset based on module usage - and inspection. -* Add in VM agnostic methods for object lifecycle management. -* Add in VM agnostic exception management -* Define C/C++ types for the API which are independent from V8 - (NAN currently uses the V8 types). -* Use the NAN examples to work through the API and build up the - definition of the key methods. -* Validate on most used modules in the ecosystem -* Complete performance analysis to confirm accepted level of overhead. - -Notes/details on other related work and the current experimental progress -on implementing this eps can be found in: -https://docs.google.com/document/d/1bX9SZKufbfhr7ncXL9fFev2LrljymtFZdWpwmv7mB9g/edit#heading=h.ln9aj9qjo969 - -# API definition - -**WORK IN PROGRESS, DRIVEN BY MODULES PORTED SO FAR** - -This API will be built up from a minimal set. Based on this set -we have been able to port the NaN example and level down. Additions -will be made as we port additional modules. - -### Types -```C -typedef struct napi_env__ *napi_env; -typedef struct napi_value__ *napi_value; -typedef struct napi_persistent__ *napi_persistent; -typedef struct napi_handle_scope__ *napi_handle_scope; -typedef struct napi_escapable_handle_scope__ *napi_escapable_handle_scope; -typedef struct napi_propertyname__ *napi_propertyname; -typedef const struct napi_func_cb_info__ *napi_func_cb_info; -typedef void (*napi_callback)(napi_env, napi_func_cb_info); -typedef void napi_destruct(void* v); - - -struct napi_method_descriptor { - napi_callback callback; - char* utf8name; -}; - -extern "C" { -enum napi_valuetype { - // ES6 types (corresponds to typeof) - napi_undefined, - napi_null, - napi_boolean, - napi_number, - napi_string, - napi_symbol, - napi_object, - napi_function, -}; -``` -### Functions - -```C -// Getters for defined singletons -NODE_EXTERN napi_value napi_get_undefined(napi_env e); -NODE_EXTERN napi_value napi_get_null(napi_env e); -NODE_EXTERN napi_value napi_get_false(napi_env e); -NODE_EXTERN napi_value napi_get_true(napi_env e); -NODE_EXTERN napi_value napi_get_global_scope(napi_env e); - - -// Methods to create Primitive types/Objects -NODE_EXTERN napi_value napi_create_object(napi_env e); -NODE_EXTERN napi_value napi_create_array(napi_env e); -NODE_EXTERN napi_value napi_create_array_with_length(napi_env e, int length); -NODE_EXTERN napi_value napi_create_number(napi_env e, double val); -NODE_EXTERN napi_value napi_create_string(napi_env e, const char*); -NODE_EXTERN napi_value napi_create_string_with_length(napi_env e, const char*, - size_t length); -NODE_EXTERN napi_value napi_create_boolean(napi_env e, bool b); -NODE_EXTERN napi_value napi_create_symbol(napi_env e, const char* s = NULL); -NODE_EXTERN napi_value napi_create_function(napi_env e, napi_callback cbinfo); -NODE_EXTERN napi_value napi_create_error(napi_env e, napi_value msg); -NODE_EXTERN napi_value napi_create_type_error(napi_env e, napi_value msg); - - -// Methods to get the the native napi_value from Primitive type -NODE_EXTERN napi_valuetype napi_get_type_of_value(napi_env e, napi_value v); -NODE_EXTERN double napi_get_number_from_value(napi_env e, napi_value v); -NODE_EXTERN int napi_get_string_from_value(napi_env e, napi_value v, - char* buf, const int buf_size); -NODE_EXTERN int32_t napi_get_value_int32(napi_env e, napi_value v); -NODE_EXTERN uint32_t napi_get_value_uint32(napi_env e, napi_value v); -NODE_EXTERN int64_t napi_get_value_int64(napi_env e, napi_value v); -NODE_EXTERN bool napi_get_value_bool(napi_env e, napi_value v); - -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); - - -// Methods to coerce values -// These APIs may execute user script -NODE_EXTERN napi_value napi_coerce_to_object(napi_env e, napi_value v); -NODE_EXTERN napi_value napi_coerce_to_string(napi_env e, napi_value v); - - -// Methods to work with Objects -NODE_EXTERN napi_value napi_get_prototype(napi_env e, napi_value object); -NODE_EXTERN napi_propertyname napi_property_name(napi_env e, - const char* utf8name); -NODE_EXTERN napi_value napi_get_propertynames(napi_env e, napi_value object); -NODE_EXTERN void napi_set_property(napi_env e, napi_value object, - napi_propertyname name, napi_value v); -NODE_EXTERN bool napi_has_property(napi_env e, napi_value object, - napi_propertyname name); -NODE_EXTERN napi_value napi_get_property(napi_env e, napi_value object, - napi_propertyname name); -NODE_EXTERN void napi_set_element(napi_env e, napi_value object, - uint32_t i, napi_value v); -NODE_EXTERN bool napi_has_element(napi_env e, napi_value object, uint32_t i); -NODE_EXTERN napi_value napi_get_element(napi_env e, - napi_value object, uint32_t i); - - -// Methods to work with Arrays -NODE_EXTERN bool napi_is_array(napi_env e, napi_value v); -NODE_EXTERN uint32_t napi_get_array_length(napi_env e, napi_value v); - -// Methods to compare values -NODE_EXTERN bool napi_strict_equals(napi_env e, napi_value lhs, napi_value rhs); - - -// Methods to work with Functions -NODE_EXTERN void napi_set_function_name(napi_env e, napi_value func, - napi_propertyname napi_value); -NODE_EXTERN napi_value napi_call_function(napi_env e, napi_value scope, - napi_value func, - int argc, napi_value* argv); -NODE_EXTERN napi_value napi_new_instance(napi_env e, napi_value cons, - int argc, napi_value* argv); - -// Napi version of node::MakeCallback(...) -NODE_EXTERN napi_value napi_make_callback(napi_env e, napi_value recv, - napi_value func, - int argc, napi_value* argv); - - -// Methods to work with napi_callbacks -NODE_EXTERN int napi_get_cb_args_length(napi_env e, napi_func_cb_info cbinfo); -NODE_EXTERN void napi_get_cb_args(napi_env e, napi_func_cb_info cbinfo, - napi_value* buffer, size_t bufferlength); -NODE_EXTERN napi_value napi_get_cb_this(napi_env e, napi_func_cb_info cbinfo); -// V8 concept; see note in .cc file -NODE_EXTERN napi_value napi_get_cb_holder(napi_env e, napi_func_cb_info cbinfo); -NODE_EXTERN bool napi_is_construct_call(napi_env e, napi_func_cb_info cbinfo); -NODE_EXTERN void napi_set_return_value(napi_env e, napi_func_cb_info cbinfo, - napi_value v); - - -// Methods to support ObjectWrap -NODE_EXTERN napi_value napi_create_constructor_for_wrap(napi_env e, - napi_callback cb); -NODE_EXTERN void napi_wrap(napi_env e, napi_value jsObject, void* nativeObj, - napi_destruct* napi_destructor, - napi_persistent* handle); -NODE_EXTERN void* napi_unwrap(napi_env e, napi_value jsObject); - -NODE_EXTERN napi_value napi_create_constructor_for_wrap_with_methods( - napi_env e, - napi_callback cb, - char* utf8name, - int methodcount, - napi_method_descriptor* methods); - -// Methods to control object lifespan -NODE_EXTERN napi_persistent napi_create_persistent(napi_env e, napi_value v); -NODE_EXTERN void napi_release_persistent(napi_env e, napi_persistent p); -NODE_EXTERN napi_value napi_get_persistent_value(napi_env e, napi_persistent p); -NODE_EXTERN napi_handle_scope napi_open_handle_scope(napi_env e); -NODE_EXTERN void napi_close_handle_scope(napi_env e, napi_handle_scope s); -NODE_EXTERN napi_escapable_handle_scope - napi_open_escapable_handle_scope(napi_env e); -NODE_EXTERN void napi_close_escapable_handle_scope( - napi_env e, - napi_escapable_handle_scope s); -NODE_EXTERN napi_value napi_escape_handle(napi_env e, - napi_escapable_handle_scope s, - napi_value v); - - -// 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); - -// These are particularly rough, needs more analysis -typedef void (*napi_try_callback)(napi_env e, void* data); -typedef void (*napi_catch_callback)(napi_env e, void* data); -NODE_EXTERN bool napi_try_catch(napi_env e, napi_try_callback t, - napi_catch_callback c, void* data); - - -// Methods to provide node::Buffer functionality with napi types -NODE_EXTERN napi_value napi_buffer_copy(napi_env e, - const char* data, uint32_t size); -NODE_EXTERN bool napi_buffer_has_instance(napi_env e, napi_value v); -NODE_EXTERN char* napi_buffer_data(napi_env e, napi_value v); -NODE_EXTERN size_t napi_buffer_length(napi_env e, napi_value v); -``` - -# C++ Wrapper API - -The current view is that the API exported by the Node.js binary -must be in C in order to achieve ABI stability. This [document] -(http://www.oracle.com/technetwork/articles/servers-storage-dev/stablecplusplusabi-333927.html) -outlines some of the challenges with respect to C++ and -ABI stability. - -However, we also believe that a C++ wrapper is possible and -would provide ease of use. The key element is that the C++ part must all -be inlined and only use the external functions/types exported -by the C API. - -What follows are some examples (incomplete/immature) mirroring NaN when -possible on what some of the C++ wrapper might include. Its not as far along -as the C API as we've only added things that were needed in porting the -modules we've worked on so far. In the end we expect a more complete set. - -Some of the more complete parts like AsyncWorker will quite likely end up outside -the scope of the ABI stable API itself, and be in a separate component. -For example they may end up part of a version of NaN that supports the new API. - -```C++ - class HandleScope { - public: - HandleScope() { - explicit HandleScope(napi_env e) - ~HandleScope() - }; - - class EscapableHandleScope { - public: - EscapableHandleScope() - explicit EscapableHandleScope(napi_env e) - ~EscapableHandleScope() - - napi_value Escape - }; - - class Utf8String { - public: - inline explicit Utf8String(napi_value from) - inline int length() - inline char* operator*() { return str_; } - inline const char* operator*() const { return str_; } - - inline ~Utf8String() - }; - - class Callback { - public: - Callback() - explicit Callback(napi_value fn) - ~Callback() - - bool operator==(const Callback &other) - bool operator!=(const Callback &other) - inline napi_value operator*() - inline napi_value operator() - inline void SetFunction(napi_value fn) - inline napi_value GetFunction() - inline bool IsEmpty() - inline napi_value - Call(napi_value target, - int argc, - napi_value argv[]) - inline napi_value - Call(int argc, napi_value argv[]) - }; - - /* abstract */ class AsyncWorker { - public: - explicit AsyncWorker(Callback *callback_) - virtual ~AsyncWorker() - virtual void WorkComplete() - inline void SaveToPersistent( - const char *key, napi_value value) - inline void SaveToPersistent( - napi_propertyname key, napi_value value) - inline void SaveToPersistent( - uint32_t index, napi_value value) - inline napi_value GetFromPersistent(const char *key) - - inline napi_value - GetFromPersistent(napi_propertyname key) - - inline napi_value GetFromPersistent(uint32_t index) - virtual void Execute() = 0; - virtual void Destroy() - - protected: - virtual void HandleOKCallback() - virtual void HandleErrorCallback() - void SetErrorMessage(const char *msg) - - const char* ErrorMessage() - }; - - inline void AsyncExecute(uv_work_t* req) - inline void AsyncExecuteComplete(uv_work_t* req) - inline void AsyncQueueWorker(AsyncWorker* worker) -```