Skip to content

Commit

Permalink
readme
Browse files Browse the repository at this point in the history
  • Loading branch information
toyobayashi committed Jun 16, 2021
1 parent 50f24c8 commit 214e2d6
Show file tree
Hide file tree
Showing 4 changed files with 537 additions and 46 deletions.
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ dist/library_napi_*
.eslint*
.gitignore
cgen.config.js
README_*
220 changes: 175 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,37 @@
# emnapi

适用于 [Emscripten](https://emscripten.org/index.html)[Node-API](https://nodejs.org/dist/latest-v14.x/docs/api/n-api.html) (v14.16.0)。
[Node-API (v14.16.0)](https://nodejs.org/docs/v14.16.0/api/n-api.html) implementation for [Emscripten](https://emscripten.org/index.html)

仅包含 `js_native_api.h` 中的 API。
## 构建
Only APIs in `js_native_api.h` are implemented.

设置 `$EMSDK` 环境变量为 emsdk 根目录,并确保 Emscripten 工具链二进制目录(`$EMSDK/upstream/emscripten`)和 CMake 在 `$PATH`
[中文 README](https://github.com/toyobayashi/emnapi/tree/main/README_CN.md).

未安装 `make` 的 Windows 用户请使用 Visual Studio Developer Command Prompt 跑命令(需要用到 `nmake`
## Quick Start

```bash
npm install
npm run build:lib
You will need to install:

# test
npm run rebuild
npm test
```
* Node.js latest LTS
* Emscripten tool chain v2.0.2+
* CMake v3.9+
* make / nmake (Windows only)

## 使用
Set `$EMSDK` environment variable to the emsdk root path.

仅支持运行在近期版本的现代浏览器和 Node.js LTS 版本,不支持 IE
Make sure `emcc` / `em++` / `cmake` / `make` can be found in `$PATH`.

Emscripten 需要 v2.0.2 以上的版本,链接上一步构建出来的 js 库,需要添加 `-sEXPORTED_FUNCTIONS=['_malloc','_free']`
If you have not installed `make` on Windows, you can also execute build commands in `Visual Studio Developer Command Prompt`.

```sh
emcc -O3 -I./include --js-library=./dist/library_napi.js \
-sALLOW_MEMORY_GROWTH=1 \
-sEXPORTED_FUNCTIONS=['_malloc','_free'] \
-o hello.js \
hello.c
### NPM Install

```bash
npm install -D @tybys/emnapi
```

### Using C

Create `hello.c`.

```c
// hello.c
#include <node_api.h>
#include <string.h>

Expand Down Expand Up @@ -80,39 +78,169 @@ NAPI_MODULE_INIT() {
}
```

导出对象默认是 `Module.emnapiExports`,可通过预定义 `NODE_GYP_MODULE_NAME` 宏来设置导出的 key 值。
Compile `hello.c` using `emcc`, set include directory, link napi js implementation with `--js-library`, and `_malloc` and `_free` should be exported.

```bash
emcc -O3 \
-I./node_modules/@tybys/emnapi/include \
--js-library=./node_modules/@tybys/emnapi/dist/library_napi.js \
-sALLOW_MEMORY_GROWTH=1 \
-sEXPORTED_FUNCTIONS=['_malloc','_free'] \
-o hello.js \
hello.c
```

如果在 `NAPI_MODULE_INITIALIZER` 中报错,Emscripten 生成的 JS 代码在 Node.js 环境会触发 `uncaughtException` 事件终止进程,可以添加 `-sNODEJS_CATCH_EXIT=0` 解决。
Use the output js in html. The default export key is `emnapiExports` on [`Module`](https://emscripten.org/docs/api_reference/module.html) object. You can change the key by predefining `NODE_GYP_MODULE_NAME`.

```html
<script src="hello.js"></script>
<script>
var Module = {
onRuntimeInitialized: function () {
var binding = Module.emnapiExports;
console.log(binding.hello()); // output 'world'
}
// Emscripten js glue code will create a global `Module` object
Module.onRuntimeInitialized = function () {
var binding = Module.emnapiExports;
var msg = 'hello ' + binding.hello();
window.alert(msg);
};
</script>
```

<script src="hello.js"></script>
Or in Node.js.

```js
const Module = require('./hello.js')

Module.onRuntimeInitialized = function () {
const binding = Module.emnapiExports
const msg = `hello ${binding.hello()}`
console.log(msg)
}
```

### Using C++

Alternatively, you can also use [`node-addon-api`](https://github.com/nodejs/node-addon-api) which is official Node-API C++ wrapper, already shipped in this package but without Node.js specific API such as `ThreadSafeFunction`, `AsyncWorker`, etc.

Create `hello.cpp`.

```cpp
#include <napi.h>

Napi::String Method(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
return Napi::String::New(env, "world");
}

Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "hello"),
Napi::Function::New(env, Method));
return exports;
}

NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
```
## API 列表
Compile `hello.cpp` using `em++`. Exception is disabled by Emscripten default, so predefine `NAPI_DISABLE_CPP_EXCEPTIONS` here.
### 不支持的 API
```bash
em++ -O3 \
-DNAPI_DISABLE_CPP_EXCEPTIONS \
-I./node_modules/@tybys/emnapi/include \
--js-library=./node_modules/@tybys/emnapi/dist/library_napi.js \
-sALLOW_MEMORY_GROWTH=1 \
-sEXPORTED_FUNCTIONS=['_malloc','_free'] \
-o hello.js \
hello.cpp
```

Then use the output js.

### Using CMake

Create `CMakeLists.txt`.

```cmake
cmake_minimum_required(VERSION 3.9)
project(emnapiexample)
add_executable(hello hello.c)
# or add_executable(hello hello.cpp)
execute_process(COMMAND node -p "require('@tybys/emnapi').include"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE EMNAPI_INCLUDE_DIR
)
string(REGEX REPLACE "[\r\n\"]" "" EMNAPI_INCLUDE_DIR ${EMNAPI_INCLUDE_DIR})
execute_process(COMMAND node -p "require('@tybys/emnapi').js_library"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE EMNAPI_JS_LIBRARY
)
string(REGEX REPLACE "[\r\n\"]" "" EMNAPI_JS_LIBRARY ${EMNAPI_JS_LIBRARY})
message(${EMNAPI_INCLUDE_DIR})
message(${EMNAPI_JS_LIBRARY})
target_include_directories(hello PRIVATE ${EMNAPI_INCLUDE_DIR})
target_link_options(hello PRIVATE
"-sALLOW_MEMORY_GROWTH=1"
"-sNODEJS_CATCH_EXIT=0"
"-sEXPORTED_FUNCTIONS=['_malloc','_free']"
"--js-library=${EMNAPI_JS_LIBRARY}"
)
```

Building with `emcmake`, output `build/hello.js` and `build/hello.wasm`.

```bash
mkdir build
cd build
emcmake cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
cd ..
```

If you have not installed `make` on Windows, execute commands below in `Visual Studio Developer Command Prompt`.

```bat
mkdir build
cd build
emcmake cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_MAKE_PROGRAM=nmake -G "NMake Makefiles" ..
cmake --build .
cd ..
```

Full example codes can be found in [here](https://github.com/toyobayashi/emnapi/tree/main/example).

以下 API 不可实现,调用后将永远返回 `napi_generic_failure` 状态。
Output code can run in recent version modern browsers and Node.js latest LTS. IE is not supported.

## Building

```bash
git clone https://github.com/toyobayashi/emnapi.git
cd ./emnapi
npm install
npm run build:lib # output ./dist/library_napi.js

# test
npm run rebuild
npm test
```

## API List

### Unavailable

These APIs always return `napi_generic_failure`.

- [x] ~~napi_create_external_arraybuffer~~
- [x] ~~napi_adjust_external_memory~~
- [x] ~~napi_detach_arraybuffer~~
- [x] ~~napi_is_detached_arraybuffer~~

### 能力受限的 API
### Limited

以下 API 受限于 JavaScript 运行时能力,可能与原生行为不一致,或是其残废的简易实现,请**谨慎使用**

* 需要 [FinalizationRegistry](https://www.caniuse.com/?search=FinalizationRegistry)[WeakRef](https://www.caniuse.com/?search=WeakRef) 的 API:
* These APIs require [FinalizationRegistry](https://www.caniuse.com/?search=FinalizationRegistry)[WeakRef](https://www.caniuse.com/?search=WeakRef)

- [x] ***napi_wrap***
- [x] ***napi_unwrap***
Expand All @@ -126,14 +254,22 @@ var Module = {
- [x] ***napi_get_reference_value***
- [x] ***napi_add_finalizer***

* `data` 指针返回值永远为 `NULL` 的 API:
* These APIs always return `NULL` data pointer

- [x] ***napi_create_arraybuffer***
- [x] ***napi_get_arraybuffer_info***
- [x] ***napi_get_typedarray_info***
- [x] ***napi_get_dataview_info***

### 稳定的 API
* These APIs require [BigInt](https://www.caniuse.com/?search=BigInt)

- [x] ***napi_create_bigint_int64***
- [x] ***napi_create_bigint_uint64***
- [x] ***napi_create_bigint_words***
- [x] ***napi_get_value_bigint_int64***
- [x] ***napi_get_value_bigint_uint64***
- [x] ***napi_get_value_bigint_words***
### Stable

- [x] napi_get_last_error_info
- [x] napi_get_undefined
Expand Down Expand Up @@ -225,9 +361,3 @@ var Module = {
- [x] napi_object_seal
- [x] napi_type_tag_object
- [x] napi_check_object_type_tag
- [x] ***napi_create_bigint_int64*** (需要 `BigInt`)
- [x] ***napi_create_bigint_uint64*** (需要 `BigInt`)
- [x] ***napi_create_bigint_words*** (需要 `BigInt`)
- [x] ***napi_get_value_bigint_int64*** (需要 `BigInt`)
- [x] ***napi_get_value_bigint_uint64*** (需要 `BigInt`)
- [x] ***napi_get_value_bigint_words*** (需要 `BigInt`)
Loading

0 comments on commit 214e2d6

Please sign in to comment.