├── .clang-format ├── .github ├── actions │ └── build-from-source │ │ └── action.yml └── workflows │ ├── external-test.yml │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── Tutorial_Wasm32_Wasi.md ├── Tutorial_Wasm_Bindgen.md ├── binding.gyp ├── deprecated └── Tutorial_General_Wasi_AOT.md ├── index.js ├── package.json ├── scripts ├── preinstall.sh └── release.sh ├── src ├── addon.cc ├── bytecode.cc ├── bytecode.h ├── cache.h ├── errors.h ├── options.cc ├── options.h ├── utils.cc ├── utils.h ├── wasmedgeaddon.cc └── wasmedgeaddon.h └── test ├── .gitignore ├── Cargo.toml ├── README.md ├── data └── integers_lib.aot.so ├── js ├── test-aot.js └── test-integers.js ├── rs └── integers_lib.rs └── test.sh /.clang-format: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 2 | BasedOnStyle: LLVM 3 | -------------------------------------------------------------------------------- /.github/actions/build-from-source/action.yml: -------------------------------------------------------------------------------- 1 | name: Build wasmedge-core 2 | 3 | inputs: 4 | cc: 5 | default: 'gcc' 6 | cxx: 7 | default: 'g++' 8 | path: 9 | default: '.' 10 | 11 | runs: 12 | using: 'composite' 13 | steps: 14 | - shell: bash 15 | env: 16 | CC: ${{ inputs.cc }} 17 | CXX: ${{ inputs.cxx }} 18 | run: | 19 | JOBS=max npm install --build-from-source --unsafe-perm ${{ inputs.path }} 20 | -------------------------------------------------------------------------------- /.github/workflows/external-test.yml: -------------------------------------------------------------------------------- 1 | name: External Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**/test**' 7 | tags: 8 | - '[0-9]+.[0-9]+.[0-9]+' 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | 14 | jobs: 15 | test_wasm_learning: 16 | runs-on: ubuntu-latest 17 | container: 18 | image: wasmedge/wasmedge:ubuntu2004_x86_64 19 | strategy: 20 | matrix: 21 | node-version: [10.x, 12.x, 14.x] 22 | include: 23 | - node-version: '14.x' 24 | ssvm-options: '--enable-aot' 25 | 26 | steps: 27 | # MAGIC: Checkout wasm-learning at $GITHUB_WORKSPACE so that node 28 | # module search will find 'ssvm' from inside the testdirs. 29 | - name: Checkout wasm-learning 30 | uses: actions/checkout@v2 31 | with: 32 | repository: second-state/wasm-learning 33 | 34 | - name: Use Node.js ${{ matrix.node-version }} 35 | uses: actions/setup-node@v1 36 | with: 37 | node-version: ${{ matrix.node-version }} 38 | 39 | - name: Checkout wasmedge-core 40 | uses: actions/checkout@v2 41 | with: 42 | path: wasmedge-core 43 | 44 | - name: Set Rust to required version 45 | uses: actions-rs/toolchain@v1 46 | with: 47 | toolchain: 1.50.0 48 | override: true 49 | 50 | - name: Install dependencies 51 | run: | 52 | npm install --unsafe-perm -g rustwasmc 53 | 54 | - name: Build and install wasmedge-core 55 | uses: './wasmedge-core/.github/actions/build-from-source' 56 | with: 57 | path: './wasmedge-core' 58 | 59 | - name: Test functions ${{ matrix.ssvm-options }} 60 | run: | 61 | rustwasmc build ${{ matrix.ssvm-options }} 62 | sed -i "s/require('ssvm')/require('wasmedge-core')/" pkg/*.js 63 | node node/app.js 64 | rustwasmc clean 65 | working-directory: nodejs/functions 66 | 67 | - name: Test JSON IO ${{ matrix.ssvm-options }} 68 | run: | 69 | rustwasmc build ${{ matrix.ssvm-options }} 70 | sed -i "s/require('ssvm')/require('wasmedge-core')/" pkg/*.js 71 | node node/app.js 72 | rustwasmc clean 73 | working-directory: nodejs/json_io 74 | 75 | - name: Test WASI ${{ matrix.ssvm-options }} 76 | run: | 77 | rustwasmc build ${{ matrix.ssvm-options }} 78 | sed -i "s/require('ssvm')/require('wasmedge-core')/" pkg/*.js 79 | node node/app.js 80 | rustwasmc clean 81 | working-directory: nodejs/wasi 82 | 83 | - name: Test KMeans ${{ matrix.ssvm-options }} 84 | if: ${{ contains(matrix.ssvm-options, 'aot') }} 85 | run: | 86 | rustwasmc build ${{ matrix.ssvm-options }} 87 | sed -i "s/require('ssvm')/require('wasmedge-core')/" pkg/*.js 88 | cd node 89 | node app.js 90 | cd - 91 | rustwasmc clean 92 | working-directory: nodejs/kmeans 93 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main Workflow 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - '**/test**' 8 | pull_request: 9 | branches: 10 | - master 11 | 12 | jobs: 13 | build_and_test: 14 | strategy: 15 | matrix: 16 | include: 17 | - cc: 'clang' 18 | cxx: 'clang++' 19 | - cc: 'gcc' 20 | cxx: 'g++' 21 | name: Build & test wasmedge-core 22 | runs-on: ubuntu-latest 23 | container: 24 | image: wasmedge/wasmedge:ubuntu2004_x86_64 25 | 26 | steps: 27 | - uses: actions/checkout@v2 28 | 29 | - name: Install clang 30 | if: ${{ contains(matrix.cc, 'clang') }} 31 | run: | 32 | apt update && apt install -y clang 33 | 34 | - name: Setup Node.js 35 | uses: actions/setup-node@v2 36 | with: 37 | node-version: '14' 38 | 39 | - name: Set Rust to required version 40 | uses: actions-rs/toolchain@v1 41 | with: 42 | toolchain: 1.50.0 43 | override: true 44 | 45 | - name: Build SSVM-napi with ${{ matrix.cc }} 46 | uses: './.github/actions/build-from-source' 47 | with: 48 | cc: ${{ matrix.cc }} 49 | cxx: ${{ matrix.cxx }} 50 | 51 | - name: Test SSVM-napi built with ${{ matrix.cc }} 52 | run: | 53 | JOBS=max npm test 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | lib/ 3 | node_modules/ 4 | package-lock.json 5 | ssvm/ 6 | *.tar.gz 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Second State WebAssembly VM for Node.js Addon 2 | 3 | The [Second State VM (SSVM)](https://github.com/second-state/ssvm) is a high-performance WebAssembly runtime optimized for server-side applications. This project provides support for accessing SSVM as a Node.js addon. It allows Node.js applications to call WebAssembly functions written in Rust or other high-performance languages. [Why do you want to run WebAssembly on the server-side?](https://cloud.secondstate.io/server-side-webassembly/why) The SSVM addon could interact with the wasm files generated by the [ssvmup](https://github.com/second-state/ssvmup) compiler tool. 4 | 5 | ## NOTICE 6 | 7 | SSVM Node.js Addon is in active development. 8 | 9 | In the current stage, our prebuilt version **only supports** x86\_64 and aarch64 Linux. 10 | Or you could use `--build-from-source` flag to build from source during addon installation. 11 | 12 | ## Requirements 13 | 14 | After SSVM Napi 0.4.0 release, we upgrade the base image from `Ubuntu 18.04` to `Ubuntu 20.04`. 15 | 16 | Users should install the dependencies by the following requirments: 17 | 18 | * boost >= 1.65.0 19 | * llvm >= 10 20 | * liblld-10-dev >= 10 21 | * libstdc++6 >= 6.0.28 (GLIBCXX >= 3.4.28) 22 | * g++ version >= 9.0 (Optional, if you have to build from source) 23 | 24 | ## Prepare environment 25 | 26 | ### Use our docker image (recommended) 27 | 28 | ```bash 29 | $ docker pull secondstate/ssvm 30 | ``` 31 | 32 | ### For ubuntu 20.04 33 | 34 | ```bash 35 | # Tools and libraries 36 | $ sudo apt install -y \ 37 | software-properties-common \ 38 | cmake \ 39 | libboost-all-dev 40 | 41 | # And you will need to install llvm for ssvm-aot tools 42 | $ sudo apt install -y \ 43 | llvm-dev \ 44 | liblld-10-dev 45 | 46 | # SSVM supports both clang++ and g++ compilers 47 | # You can choose one of them for building this project 48 | $ sudo apt install -y gcc g++ 49 | $ sudo apt install -y clang 50 | ``` 51 | 52 | ### Verify the version of llvm 53 | 54 | ```bash 55 | $ sudo apt list | grep llvm 56 | ...omitted... 57 | llvm-dev/focal,now 1:10.0-50~exp1 amd64 [installed] 58 | llvm-runtime/focal,now 1:10.0-50~exp1 amd64 [installed,automatic] 59 | llvm/focal,now 1:10.0-50~exp1 amd64 [installed,automatic] 60 | ...omitted... 61 | 62 | # If the version is 1:10.x, then your llvm version is correct. 63 | ``` 64 | 65 | ### Verify the version of libstdc++6 66 | 67 | ```bash 68 | $ strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep GLIBCXX 69 | ...omitted... 70 | GLIBCXX_3.4.24 71 | GLIBCXX_3.4.25 72 | GLIBCXX_3.4.26 73 | GLIBCXX_3.4.27 74 | GLIBCXX_3.4.28 75 | GLIBCXX_DEBUG_MESSAGE_LENGTH 76 | 77 | # If you can find GLIBCXX_3.4.28 in the output, then your libstdc++6 version is correct. 78 | ``` 79 | 80 | ### Works with Rust library using Wasm-Bindgen 81 | 82 | Please refer to [Tutorial: A Wasm-Bindgen application](./Tutorial_Wasm_Bindgen.md). 83 | 84 | ### Works with Rust application using standalone wasm32-wasi backend 85 | 86 | Please refer to [Tutorial: A standalone wasm32-wasi application](./Tutorial_Wasm32_Wasi.md). 87 | 88 | ## APIs 89 | 90 | ### Constructor: `ssvm.VM(wasm, ssvm_options) -> vm_instance` 91 | * Create a ssvm instance by given wasm file and options. 92 | * Arguments: 93 | * `wasm`: Input wasm file, can be the following three formats: 94 | 1. Wasm file path (String, e.g. `/tmp/hello.wasm`) 95 | 2. Wasm bytecode format which is the content of a wasm binary file (Uint8Array) 96 | * `options`: An options object for setup the SSVM execution environment. 97 | * `options` 98 | * `args` : An array of strings that Wasm application will get as function arguments. Default: `[]`. 99 | * `env` : An object like `process.env` that Wasm application will get as its environment variables. Default: `{}`. 100 | * `preopens` : An object which maps ':'. E.g. `{'/sandbox': '/some/real/path/that/wasm/can/access'}` Default: `{}`. 101 | * `EnableWasiStartFunction` : This option will disable wasm-bindgen mode and prepare the working environment for standalone wasm program. If you want to run an appliation with `main()`, you should set this to `true`. Default: `false`. 102 | * `EnableAOT` : This option will enable ssvm aot mode. Default: `false`. 103 | * `EnableMeasurement` : This option will enable measurement but decrease its performance. Default: `false`. 104 | * `AllowCommands` : An array of strings that indicate what commands are allowed to execute in the SSVM Process Module. Default `[]`. 105 | * `AllowAllCommands` : Allow users to call any command in the SSVM Process Module. This option will overwrite the `AllowCommands`. Default: `false`. 106 | * Return value: 107 | * `vm_instance`: A ssvm instance. 108 | 109 | ### Methods 110 | 111 | #### `Start() -> Integer` 112 | * Emit `_start()` and expect the return value type is `Integer` which represents the error code from `main()`. 113 | * Arguments: 114 | * If you want to append arguments for the standalone wasm program, please set the `args` in `wasi options`. 115 | * Example: 116 | ```javascript 117 | let error_code = Start(); 118 | ``` 119 | 120 | #### `Run(function_name, args...) -> void` 121 | * Emit `function_name` with `args` and expect the return value type is `void`. 122 | * Arguments: 123 | * `function_name` : The function name which users want to emit. 124 | * `args` \*: The function arguments. The delimiter is `,` 125 | * Example: 126 | ```javascript 127 | Run("Print", 1234); 128 | ``` 129 | 130 | #### `RunInt(function_name, args...) -> Integer` 131 | * Emit `function_name` with `args` and expect the return value type is `Integer` (Int32). 132 | * Arguments: 133 | * `function_name` : The function name which users want to emit. 134 | * `args` \*: The function arguments. The delimiter is `,` 135 | * Example: 136 | ```javascript 137 | let result = RunInt("Add", 1, 2); 138 | // result should be 3 139 | ``` 140 | 141 | #### `RunUInt(function_name, args...) -> Integer` 142 | * Emit `function_name` with `args` and expect the return value type is `Integer` (UInt32). 143 | * Arguments: 144 | * `function_name` : The function name which users want to emit. 145 | * `args` \*: The function arguments. The delimiter is `,` 146 | * Example: 147 | ```javascript 148 | let result = RunInt("Add", 1, 2); 149 | // result should be 3 150 | ``` 151 | 152 | #### `RunInt64(function_name, args...) -> BigInt` 153 | * Emit `function_name` with `args` and expect the return value type is `BigInt` (Int64). 154 | * Arguments: 155 | * `function_name` : The function name which users want to emit. 156 | * `args` \*: The function arguments. The delimiter is `,` 157 | * Example: 158 | ```javascript 159 | let result = RunInt("Add", 1, 2); 160 | // result should be 3 161 | ``` 162 | 163 | #### `RunUInt64(function_name, args...) -> BigInt` 164 | * Emit `function_name` with `args` and expect the return value type is `BigInt` (UInt64). 165 | * Arguments: 166 | * `function_name` : The function name which users want to emit. 167 | * `args` \*: The function arguments. The delimiter is `,` 168 | * Example: 169 | ```javascript 170 | let result = RunInt("Add", 1, 2); 171 | // result should be 3 172 | ``` 173 | 174 | #### `RunString(function_name, args...) -> String` 175 | * Emit `function_name` with `args` and expect the return value type is `String`. 176 | * Arguments: 177 | * `function_name` : The function name which users want to emit. 178 | * `args` \*: The function arguments. The delimiter is `,` 179 | * Example: 180 | ```javascript 181 | let result = RunString("PrintMathScore", "Amy", 98); 182 | // result: "Amy’s math score is 98". 183 | ``` 184 | 185 | #### `RunUint8Array(function_name, args...) -> Uint8Array` 186 | * Emit `function_name` with `args` and expect the return value type is `Uint8Array`. 187 | * Arguments: 188 | * `function_name` : The function name which users want to emit. 189 | * `args` \*: The function arguments. The delimiter is `,` 190 | * Example: 191 | ```javascript 192 | let result = RunUint8Array("Hash", "Hello, world!"); 193 | // result: "[12, 22, 33, 42, 51]". 194 | ``` 195 | 196 | #### `Compile(output_filename) -> boolean` 197 | * Compile a given wasm file (can be a file path or a byte array) into a native binary whose name is the given `output_filename`. 198 | * This function uses SSVM AOT compiler. 199 | * Return `false` when the compilation failed. 200 | ```javascript 201 | // Compile only 202 | let vm = ssvm.VM("/path/to/wasm/file", options); 203 | vm.Compile("/path/to/aot/file"); 204 | 205 | // When you want to run the compiled file 206 | let vm = ssvm.VM("/path/to/aot/file", options); 207 | vm.RunXXX("Func", args); 208 | ``` 209 | 210 | #### `GetStatistics() -> Object` 211 | * If you want to enable measurement, set the option `EnableMeasurement` to `true`. But please notice that enabling measurement will significantly affect performance. 212 | * Get the statistics of execution runtime. 213 | * Return Value `Statistics` 214 | * `Measure` -> : To show if the measurement is enabled or not. 215 | * `TotalExecutionTime` -> : Total execution time (Wasm exeuction time + Host function execution time) in `` unit. 216 | * `WasmExecutionTime` -> : Wasm instructions execution time in `ns` unit. 217 | * `HostFunctionExecutionTime` -> : Host functions (e.g. eei or wasi functions) execution time in `ns` unit. 218 | * `InstructionCount` -> : The number of executed instructions in this execution. 219 | * `TotalGasCost` -> : The cost of this execution. 220 | * `InstructionPerSecond` -> : The instructions per second of this execution. 221 | 222 | ```javascript 223 | let result = RunInt("Add", 1, 2); 224 | // result should be 3 225 | let stat = GetStatistics(); 226 | /* 227 | If the `EnableMeasurement: true`: 228 | 229 | stat = Statistics: { 230 | Measure: true, 231 | TotalExecutionTime: 1512, 232 | WasmExecutionTime: 1481, 233 | HostFunctionExecutionTime: 31, 234 | InstructionCount: 27972, 235 | TotalGasCost: 27972, 236 | InstructionPerSecond: 18887238.35246455 237 | } 238 | 239 | Else: 240 | 241 | stat = Statistics: { 242 | Measure: false 243 | } 244 | */ 245 | ``` 246 | -------------------------------------------------------------------------------- /Tutorial_Wasm32_Wasi.md: -------------------------------------------------------------------------------- 1 | # How to run wasm applications with ssvm-napi (General Wasm32-wasi with interpreter mode) 2 | 3 | ## Environment Setup for Rust, Nodejs, and ssvmup 4 | 5 | ```bash 6 | $ sudo apt-get update 7 | $ sudo apt-get -y upgrade 8 | $ sudo apt install build-essential curl wget git vim libboost-all-dev 9 | 10 | $ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 11 | $ source $HOME/.cargo/env 12 | 13 | $ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash 14 | 15 | $ export NVM_DIR="$HOME/.nvm" 16 | $ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 17 | $ [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" 18 | 19 | $ nvm install v14.2.0 20 | $ nvm use v14.2.0 21 | 22 | $ npm i -g ssvmup 23 | ``` 24 | 25 | ## Example 1. Print environment variables, arguments, and test filesystem 26 | 27 | ### Create a new rust project 28 | 29 | ```bash 30 | cargo new file-example 31 | cd file-example 32 | ``` 33 | 34 | ### Write Rust code 35 | 36 | Below is the entire content of the `src/main.rs` file. 37 | 38 | ```rust 39 | use std::env; 40 | use std::fs; 41 | use std::fs::File; 42 | use std::io::{Write, Read}; 43 | 44 | fn main() { 45 | println!("This is a demo application to show how to run a standalone wasi program with ssvm-napi!"); 46 | println!("============================================"); 47 | println!("Print environment variables"); 48 | println!("--------------------------------------------"); 49 | println!("The env vars are as follows."); 50 | for (key, value) in env::vars() { 51 | println!("{}: {}", key, value); 52 | } 53 | println!("============================================\n"); 54 | println!("Print arguments"); 55 | println!("--------------------------------------------"); 56 | println!("The args are as follows."); 57 | for argument in env::args() { 58 | println!("{}", argument); 59 | } 60 | println!("============================================\n"); 61 | println!("Test filesystem, create a /hello.txt, read and write to it, and then delete it"); 62 | println!("--------------------------------------------"); 63 | let path = "/hello.txt".to_string(); 64 | let content = "Hello from SSVM\nThis file is located at wasm binary folder".to_string(); 65 | let mut output = File::create(&path).unwrap(); 66 | output.write_all(&content.as_bytes()).unwrap(); 67 | let mut f = File::open(&path).unwrap(); 68 | let mut s = String::new(); 69 | let ret = match f.read_to_string(&mut s) { 70 | Ok(_) => s, 71 | Err(e) => e.to_string(), 72 | }; 73 | println!("Output: {}", ret); 74 | fs::remove_file(&path).expect("Unable to delete"); 75 | println!("============================================\n"); 76 | } 77 | ``` 78 | 79 | ### Build the WASM bytecode with cargo wasm32-wasi backend 80 | 81 | ```bash 82 | cargo build --release --target wasm32-wasi 83 | ``` 84 | 85 | After building, our target wasm file is located at `target/wasm32-wasi/release/file-example.wasm`. 86 | 87 | ### Install SSVM addon for your application 88 | 89 | ```bash 90 | npm install ssvm 91 | ``` 92 | 93 | or if you want to build from source: 94 | 95 | ```bash 96 | export CXX=g++-9 97 | npm install --build-from-source https://github.com/second-state/ssvm-napi 98 | ``` 99 | 100 | ### Use SSVM addon 101 | After installing the SSVM addon, we could now interact with `file_example.wasm` generated by wasm32-wasi backend in Node.js. 102 | 103 | - Create js file `app.js` and `lib.js` in the root folder. 104 | 105 | #### Folder structure 106 | 107 | ``` 108 | ├── Cargo.lock 109 | ├── Cargo.toml 110 | ├── README.md 111 | ├── app.js 112 | ├── lib.js 113 | ├── node_modules 114 | │   └── ssvm 115 | ├── package-lock.json 116 | ├── src 117 | │   └── main.rs 118 | └── target 119 | ├── release 120 | │   ├── ...omitted... 121 | │   └── incremental 122 | └── wasm32-wasi 123 | └── release 124 | ├── ...omitted... 125 | ├── file-example.d 126 | ├── file-example.wasm 127 | └── incremental 128 | ``` 129 | 130 | #### The entire content of `app.js`: 131 | 132 | ```javascript 133 | const { file_demo } = require('./lib.js'); 134 | 135 | file_demo(); 136 | ``` 137 | 138 | #### The entire content of `lib.js`: 139 | 140 | ```javascript 141 | let vm; 142 | module.exports.file_demo = function() { 143 | return vm.Start(); 144 | }; 145 | 146 | const ssvm = require('ssvm'); 147 | const path = require('path').join(__dirname, 'target/wasm32-wasi/release/file-example.wasm'); 148 | 149 | vm = new ssvm.VM(path, {"EnableWasiStartFunction": true, env: process.env, args: process.argv, preopens:{'/': __dirname}}); 150 | ``` 151 | 152 | ### Execute and check results 153 | 154 | ```bash 155 | $ node app.js arg1 arg2 156 | 157 | This is a demo application to show how to run a standalone wasi program with ssvm-napi! 158 | ============================================ 159 | Print environment variables 160 | -------------------------------------------- 161 | The env vars are as follows. 162 | LANG: C.UTF-8 163 | (...omitted...) 164 | PATH: /bin:/usr/local/sbin:/usr/local/bin:/usr/local/sbin 165 | PWD: /home/hydai/workspace/wasm-learning/ssvm/file-example 166 | _: /home/hydai/.nvm/versions/node/v14.5.0/bin/node 167 | ============================================ 168 | 169 | Print arguments 170 | -------------------------------------------- 171 | The args are as follows. 172 | _start 173 | arg1 174 | arg2 175 | ============================================ 176 | 177 | Test filesystem, create a /hello.txt, read and write to it, and then delete it 178 | -------------------------------------------- 179 | Output: Hello from SSVM 180 | This file is located at wasm binary folder 181 | ============================================ 182 | ``` 183 | 184 | -------------------------------------------------------------------------------- /Tutorial_Wasm_Bindgen.md: -------------------------------------------------------------------------------- 1 | # How to run wasm applications with ssvm-napi (Wasm-Bindgen and interpreter mode) 2 | 3 | ## Environment Setup for Rust, Nodejs, and ssvmup 4 | 5 | ```bash 6 | $ sudo apt-get update 7 | $ sudo apt-get -y upgrade 8 | $ sudo apt install build-essential curl wget git vim libboost-all-dev 9 | 10 | $ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 11 | $ source $HOME/.cargo/env 12 | $ cargo install cargo-wasi 13 | 14 | $ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash 15 | 16 | $ export NVM_DIR="$HOME/.nvm" 17 | $ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 18 | $ [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" 19 | 20 | $ nvm install v14.2.0 21 | $ nvm use v14.2.0 22 | 23 | $ npm i -g ssvmup 24 | ``` 25 | 26 | ## Example 1. Hello Application 27 | 28 | ### Create a new rust library project 29 | 30 | ```bash 31 | cargo new --lib hello 32 | cd hello 33 | ``` 34 | 35 | ### Modify the cargo config file 36 | 37 | Add the following to the `Cargo.toml` file. 38 | 39 | ```toml 40 | [lib] 41 | name = "hello" 42 | path = "src/lib.rs" 43 | crate-type =["cdylib"] 44 | 45 | [dependencies] 46 | wasm-bindgen = "=0.2.61" 47 | ``` 48 | 49 | ### Write Rust code 50 | 51 | Below is the entire content of the `src/lib.rs` file. 52 | 53 | ```rust 54 | use wasm_bindgen::prelude::*; 55 | 56 | #[wasm_bindgen] 57 | pub fn say(s: String) -> String { 58 | let r = String::from("hello "); 59 | return r + &s; 60 | } 61 | 62 | #[wasm_bindgen] 63 | pub fn add(a: i32, b: i32) -> i32 { 64 | return a + b; 65 | } 66 | 67 | #[wasm_bindgen] 68 | pub fn reverse(v: Vec) -> Vec { 69 | let mut r = v.clone(); 70 | r.reverse(); 71 | return r; 72 | } 73 | ``` 74 | 75 | In addition to the above code, functions like the example below can deliberately parse incoming JSON string data. This is an additional say example that uses valid JSON instead of just plain string. 76 | Please note, in addition to the dependencies above, you will now also need to add `serde_json = "1.0.53"` to your Cargo.toml file for the following `say_with_json` demonstration to work. 77 | 78 | ```rust 79 | use serde_json::json; 80 | use wasm_bindgen::prelude::*; 81 | use serde_json::{Result, Value}; 82 | 83 | #[wasm_bindgen] 84 | pub fn say_with_json(s: String) -> String { 85 | let json_as_value: Value = serde_json::from_str(&s).unwrap(); 86 | let first_word = String::from("Hello "); 87 | let concatenation = first_word + &serde_json::to_string(&json_as_value["name"]).unwrap(); 88 | let response_object = json!({ "result": concatenation }); 89 | return serde_json::to_string(&response_object).unwrap(); 90 | } 91 | ``` 92 | 93 | When given `{"name": "Tim"}` this `say_with_json` function returns `Hello Tim` wrapped in a response object (as valid JSON) like this `{"ssvm_response": ["{\"result\": \"Hello Tim\"}"]}` 94 | 95 | ### Build the WASM bytecode with ssvmup 96 | 97 | ```bash 98 | ssvmup build 99 | ``` 100 | 101 | After building, our target wasm file is located at `pkg/hello_bg.wasm`. 102 | 103 | ### Install SSVM addon for your application 104 | 105 | ```bash 106 | npm install ssvm 107 | ``` 108 | 109 | or if you want to build from source: 110 | 111 | ```bash 112 | export CXX=g++-9 113 | npm install --build-from-source https://github.com/second-state/ssvm-napi 114 | ``` 115 | 116 | ### Use SSVM addon 117 | 118 | After installing SSVM addon, we could now interact with `hello_bg.wasm` generated by wasm-pack in Node.js. 119 | Make sure you use the corresponding VM method to the rust return type. 120 | 121 | - Create a new folder at any path you want. (e.g. `mkdir application`) 122 | - Copy `hello_bg.wasm` into your application directory. (e.g. `cp hello_gb.wasm `) 123 | - Create js file `main.js` (or whatever you like) with the following content: 124 | 125 | ```javascript 126 | var ssvm = require('ssvm'); 127 | var vm = new ssvm.VM("hello_bg.wasm"); 128 | var ret = vm.RunString("say", "world"); 129 | console.log(ret); 130 | 131 | ret = vm.RunInt("add", 3, 4); 132 | console.log(ret); 133 | 134 | ret = vm.RunUint8Array("reverse", Uint8Array.from([1, 2, 3, 4, 5, 6])); 135 | console.log(ret); 136 | 137 | ret = vm.RunInt("add", 999, -111); 138 | console.log(ret); 139 | 140 | ret = vm.RunUint8Array("reverse", Uint8Array.from([60, 50, 40, 30, 20, 10])); 141 | console.log(ret); 142 | ``` 143 | 144 | ### Execute and check results 145 | 146 | ```bash 147 | $ node main.js 148 | 149 | hello world 150 | 7 151 | Uint8Array [ 6, 5, 4, 3, 2, 1 ] 152 | 888 153 | Uint8Array [ 10, 20, 30, 40, 50, 60 ] 154 | ``` 155 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "<(module_name)", 5 | "cflags_cc": [ "-std=c++17" ], 6 | "cflags!": [ "-fno-exceptions", "-fno-rtti" ], 7 | "cflags_cc!": [ "-fno-exceptions", "-fno-rtti" ], 8 | "link_settings": { 9 | "libraries": [ 10 | "/usr/local/lib/libwasmedge_c.so", 11 | ], 12 | }, 13 | "sources": [ 14 | "src/addon.cc", 15 | "src/bytecode.cc", 16 | "src/options.cc", 17 | "src/wasmedgeaddon.cc", 18 | "src/utils.cc", 19 | ], 20 | "include_dirs": [ 21 | " String { 69 | let mut rng = OsRng; 70 | let private_key = RSAPrivateKey::new(&mut rng, bits as usize).expect("failed to generate a key"); 71 | let public_key = private_key.to_public_key(); 72 | let key_pair = RSAKeyPair {rsa_private_key: private_key, rsa_public_key: public_key}; 73 | return serde_json::to_string(&key_pair).unwrap(); 74 | } 75 | pub fn decrypt (pk: &str, data: &[u8]) -> Vec { 76 | let private_key: RSAPrivateKey = serde_json::from_str(pk).unwrap(); 77 | return private_key.decrypt(PaddingScheme::PKCS1v15, data).expect("failed to decrypt"); 78 | } 79 | pub fn encrypt (pk: &str, data: &[u8]) -> Vec { 80 | let mut rng = OsRng; 81 | let public_key: RSAPublicKey = serde_json::from_str(pk).unwrap(); 82 | return public_key.encrypt(&mut rng, PaddingScheme::PKCS1v15, data).expect("failed to encrypt"); 83 | } 84 | fn main() { 85 | let matches = App::new("RSA Example") 86 | .subcommand(SubCommand::with_name("generate_key_pair") 87 | .arg(Arg::with_name("bits") 88 | .short("b") 89 | .long("bits") 90 | .value_name("BITS") 91 | .index(1) 92 | )) 93 | .subcommand(SubCommand::with_name("decrypt") 94 | .arg(Arg::with_name("private_key") 95 | .short("k") 96 | .long("key") 97 | .value_name("PRIVATE_KEY_JSON") 98 | .required(true) 99 | .index(1) 100 | ) 101 | .arg(Arg::with_name("data") 102 | .short("d") 103 | .long("data") 104 | .value_name("DATA_JSON") 105 | .required(true) 106 | .index(2) 107 | )) 108 | .subcommand(SubCommand::with_name("encrypt") 109 | .arg(Arg::with_name("public_key") 110 | .short("k") 111 | .long("key") 112 | .value_name("PUBLIC_KEY_JSON") 113 | .required(true) 114 | .index(1) 115 | ) 116 | .arg(Arg::with_name("data") 117 | .short("d") 118 | .long("data") 119 | .value_name("DATA_JSON") 120 | .required(true) 121 | .index(2) 122 | )) 123 | .get_matches(); 124 | if let Some(matches) = matches.subcommand_matches("generate_key_pair") { 125 | let bits = matches 126 | .value_of("bits") 127 | .and_then(|x| x.parse::().ok()) 128 | .unwrap_or(2048); 129 | println!("{}", generate_key_pair(bits)); 130 | } else if let Some(matches) = matches.subcommand_matches("encrypt") { 131 | let pk = matches 132 | .value_of("public_key") 133 | .unwrap(); 134 | let data = matches 135 | .value_of("data") 136 | .unwrap(); 137 | println!("data:'{}'", data); 138 | let data_bytes = data.as_bytes(); 139 | let result = encrypt(pk, &data_bytes); 140 | let result_json = serde_json::to_string(&result).unwrap(); 141 | println!("encrypt:'{}'", result_json); 142 | } else if let Some(matches) = matches.subcommand_matches("decrypt") { 143 | let pk = matches 144 | .value_of("private_key") 145 | .unwrap(); 146 | let data = matches 147 | .value_of("data") 148 | .and_then(|x| serde_json::from_str::>(x).ok()) 149 | .expect("failed to decode json string"); 150 | println!("data:'{}'", serde_json::to_string(&data).unwrap()); 151 | let result = decrypt(pk, &data); 152 | let result_utf8 = String::from_utf8(result).expect("failed to decode utf8 bytes"); 153 | println!("decrypt:'{}'", result_utf8); 154 | } 155 | } 156 | ``` 157 | 158 | ### Build the WASM bytecode with cargo wasm32-wasi backend 159 | 160 | ```bash 161 | cargo wasi build --release 162 | ``` 163 | 164 | After building, our target wasm file is located at `target/wasm32-wasi/release/rsa-example.wasm`. 165 | 166 | ### Install SSVM addon for your application 167 | 168 | ```bash 169 | npm install ssvm 170 | ``` 171 | 172 | or if you want to build from source: 173 | 174 | ```bash 175 | export CXX=g++-9 176 | npm install --build-from-source https://github.com/second-state/ssvm-napi 177 | ``` 178 | 179 | ### Use SSVM addon 180 | After installing the SSVM addon, we could now interact with `rsa_example.wasm` generated by wasm32-wasi backend in Node.js. 181 | 182 | - Create a new folder at any path you want. (e.g. `mkdir application`) 183 | - Copy `rsa_example.wasm` into your application directory. (e.g. `cp rsa_example.wasm `) 184 | - Create js file `main.js` and `lib.js` with the following content: 185 | 186 | #### The entire content of `main.js`: 187 | 188 | ```javascript 189 | const { generate_key_pair } = require('./lib.js'); 190 | 191 | console.log( "Generate Key Pair:" ); 192 | console.time('Generate_Key_Pair_Compile_And_Run'); 193 | generate_key_pair(2048) 194 | console.timeEnd('Generate_Key_Pair_Compile_And_Run'); 195 | 196 | console.log( "Test codecache" ); 197 | console.log( "Generate Key Pair:" ); 198 | console.time('Generate_Key_Pair_CACHE'); 199 | generate_key_pair(2048) 200 | console.timeEnd('Generate_Key_Pair_CACHE'); 201 | 202 | console.log( "Test codecache2" ); 203 | console.log( "Generate Key Pair:" ); 204 | console.time('Generate_Key_Pair_CACHE'); 205 | generate_key_pair(2048) 206 | console.timeEnd('Generate_Key_Pair_CACHE'); 207 | ``` 208 | 209 | #### The entire content of `lib.js`: 210 | 211 | ```javascript 212 | let vm; 213 | /** 214 | * @param {number} bits 215 | * @returns {string} 216 | */ 217 | module.exports.generate_key_pair = function(bits) { 218 | return vm.RunAot({ 219 | "args": ["generate_key_pair", bits] 220 | }); 221 | }; 222 | 223 | const ssvm = require('ssvm'); 224 | const path = require('path').join(__dirname, 'target/wasm32-wasi/release/rsa_example_aot.wasm'); 225 | 226 | vm = new ssvm.VM(path, {"DisableWasmBindgen": true}); 227 | ``` 228 | 229 | ### Execute and check results 230 | 231 | Please notice that ssvm.js is running in aot mode. 232 | It will take a few minutes to compile the wasm bytecode into native binary. 233 | SSVM Napi supports code cache, so you can find that it took 1 minute and 44 seconds to compile and run the first time. 234 | And the remaining two execution took 905 ms and 856 ms only. 235 | 236 | ```bash 237 | $ node main.js 238 | 239 | Generate Key Pair: 240 | 2020-07-10 16:37:54,476 INFO [default] compile start 241 | 2020-07-10 16:37:55,511 INFO [default] verify start 242 | 2020-07-10 16:37:55,703 INFO [default] optimize start 243 | 2020-07-10 16:39:08,531 INFO [default] codegen start 244 | 2020-07-10 16:39:38,820 INFO [default] compile done 245 | {"rsa_private_key":{"n":[1983117373,2247399852,3418733532,4289161310,927612907,1101523362,2827939280,1413757326,2495300994,2358531003,4181075018,50883607,3324275287,2334324690,2685083620,2348780898,2767351415,327053028,1154932497,3327809547,4261663267,1744195352,1824322712,745917304,602213007,65438281,3543166904,570213504,340858058,3574357884,1631976464,3219245193,216547475,3333845260,169550770,1447060135,4256419845,3130861173,4235807035,3059174324,3721369962,581339731,2849030407,3693804299,1865913092,3117006541,1171233254,1948144833,515873677,1692255690,1431944722,182446396,2309052522,1686786413,1792202215,3398549959,2501709357,2939165553,2594483209,3034035264,1941147101,2184106274,789517948,3202069254],"e":[65537],"d":[3963203713,2907246659,3366985422,576748352,1161370977,2097013433,1354819067,2421943565,3888419313,2139594718,1412195722,4050407029,1381602200,1100638271,534047261,3574181614,2522624904,3874177184,262070841,1062086928,4013167632,2869200350,3165854798,1700554383,3732476279,1104502897,1881890674,2993540464,2014071387,253737994,2656500765,1614889361,2746729114,3140232998,3480065062,166963359,278356859,656398101,2063611529,2609540709,2728221049,1070307602,71071743,3805639283,457792205,3536688013,638543397,2718371060,1299890561,3746359370,1055402468,3836576900,3184520317,3521812408,3482527924,17814868,1098242002,3469366472,233014848,3994648154,1083334490,2669134302,3571269573,781450106],"primes":[[3260553329,3626617802,3778306423,2676159408,4017970995,2629845941,172526436,3157327837,1098938159,1741158604,333988906,3529905269,1247870457,555586149,518022149,2406510384,1815346384,1618114181,3752386498,354100011,2614124617,164563692,1135916519,3830626434,1954641310,4219196309,2298100810,1235601987,3703419860,449830155,1236359119,3527520607],[2737283725,4201033096,2907195163,748955735,901596992,2468200643,1019433158,887683740,873395265,737106587,4053395608,1836047485,27769815,3614005007,3863931014,1094152197,274007989,1257574031,928709123,2711508767,3110788743,4186797926,4193649553,3147532630,1718836571,191792376,3413814851,2956224537,2236750218,1158092872,2575968924,3898710810]]},"rsa_public_key":{"n":[1983117373,2247399852,3418733532,4289161310,927612907,1101523362,2827939280,1413757326,2495300994,2358531003,4181075018,50883607,3324275287,2334324690,2685083620,2348780898,2767351415,327053028,1154932497,3327809547,4261663267,1744195352,1824322712,745917304,602213007,65438281,3543166904,570213504,340858058,3574357884,1631976464,3219245193,216547475,3333845260,169550770,1447060135,4256419845,3130861173,4235807035,3059174324,3721369962,581339731,2849030407,3693804299,1865913092,3117006541,1171233254,1948144833,515873677,1692255690,1431944722,182446396,2309052522,1686786413,1792202215,3398549959,2501709357,2939165553,2594483209,3034035264,1941147101,2184106274,789517948,3202069254],"e":[65537]}} 246 | Generate_Key_Pair_Compile_And_Run: 1:44.926 (m:ss.mmm) 247 | Test codecache 248 | Generate Key Pair: 249 | {"rsa_private_key":{"n":[3096848235,1797968459,1804302772,1704926852,2069976515,3087669824,4157421854,2150556660,3691897224,3610398013,2841789364,3403741232,2890327785,1333787269,90811988,3921854237,2633931107,907659922,3765941586,3750218241,3590267261,1372768177,653924617,3651415365,1998046315,3272284832,1778261880,1658693700,974877212,3600651526,468587548,3203576471,3153644958,1499405536,751840724,1365860369,727452873,1570941737,670745584,1377125532,137328187,1391716148,3667723573,3290547470,1099305624,491603592,4269240615,476878823,2678948031,2333283605,1326807013,1203424255,3059150094,758553098,1013703344,2482785364,1439334649,1019660776,2315603403,1490965813,232932239,2545945906,1220165920,3038230755],"e":[65537],"d":[667859729,2845641205,771129649,570206897,1112700604,4036243977,1755432443,1909592619,3831339351,3883786410,3296775394,1394806769,1961400318,1742085518,797467422,1701332594,4271397992,2170835268,3731546692,1179415431,1059003537,194004393,1309691433,2565543508,2092851719,3377909655,2603558755,3911385738,1170137073,1765925030,2740742901,1308408932,3082275592,2496439867,2617244069,1945703773,2035229530,2742193775,2543866800,3273234180,3902044181,2416151980,3334250320,318409839,2812436608,3614849706,726214354,1620636788,3511253535,1605499309,2948727509,1287801236,731214009,185423821,2560454048,476958055,3799655598,2939619199,2796516634,1395174851,224628046,92508791,3844218431,2930770558],"primes":[[2340115171,264422593,2633237044,1610022469,1686718305,1847999691,1669187428,2219220498,3968417980,352671343,4227953747,210146546,3377597624,3571400343,3229458893,2170796540,2985295838,2341774529,1693399060,3226217781,543606750,2983234993,308243472,27464910,2078085090,733428876,2083306974,2380935351,3429506921,440362201,2683931237,3836425558],[1736677337,3979059478,559262679,11422300,3534577406,669933558,1324445504,1293184657,1531749471,4279731603,185608725,2700203224,628063552,2554692326,261539079,2918457288,542742069,3861182639,1118575070,3333659657,4293976369,4165933184,3552649147,2111198244,2039904865,1164974774,1608442694,552686728,4060946402,1776849867,3924745747,3401369720]]},"rsa_public_key":{"n":[3096848235,1797968459,1804302772,1704926852,2069976515,3087669824,4157421854,2150556660,3691897224,3610398013,2841789364,3403741232,2890327785,1333787269,90811988,3921854237,2633931107,907659922,3765941586,3750218241,3590267261,1372768177,653924617,3651415365,1998046315,3272284832,1778261880,1658693700,974877212,3600651526,468587548,3203576471,3153644958,1499405536,751840724,1365860369,727452873,1570941737,670745584,1377125532,137328187,1391716148,3667723573,3290547470,1099305624,491603592,4269240615,476878823,2678948031,2333283605,1326807013,1203424255,3059150094,758553098,1013703344,2482785364,1439334649,1019660776,2315603403,1490965813,232932239,2545945906,1220165920,3038230755],"e":[65537]}} 250 | Generate_Key_Pair_CACHE: 905.033ms 251 | Test codecache2 252 | Generate Key Pair: 253 | {"rsa_private_key":{"n":[836640533,2811245416,716324048,2993722125,2443208307,1207933224,1763966872,768230429,2698832834,1722770511,3481056265,2483454227,1581944854,1854441585,1267604383,225507178,2248629754,222796813,436665417,2767570513,1751460175,3567334893,158551180,1936216845,4143458509,3490367465,4084860363,1721850123,1379384883,3483780499,4242230917,2422006539,1537799438,3474259944,2996099166,2549725424,2874957611,1494382397,462245471,1722720530,1143308317,1819787583,3197383042,4024279149,1536879499,298310184,1814074952,136661287,782643024,1746597339,900351955,2634931530,4259949916,2983184200,2755062806,1675383366,4274792210,2929612017,3247824938,3718374389,1609377025,274078680,2522020422,3380278664],"e":[65537],"d":[3718516409,403739405,3841152456,2261798446,4084337024,752106852,2623138215,4078595412,3537418867,1870168139,2028553975,1682790126,340635940,3487094012,445770472,3180552619,827365636,3899776205,2678516068,2928766469,2523089326,1544583927,2480445868,25059490,628011049,534030143,4170423773,875656408,3091393555,2037542700,1532045649,1141911976,2780439328,1055640734,2256501609,1587646446,3961881818,1922084283,3815870426,1462890383,2510425470,2412374745,2133312881,2481311933,3176828778,406793081,1459898943,1581505357,446908790,1862155794,391389634,1337983029,2167916805,894031543,2972205793,3981874314,3970873540,904702946,4281281793,2011341563,1730040502,449447927,1392479146,1079943767],"primes":[[4194895487,2019100567,288166068,3420769453,578087955,144710467,2455684951,2290410576,769352734,1683603247,3742735841,2112729998,3689821774,2841117347,1945557644,1743471659,1789336052,3409611568,2427879267,3494586315,3683981184,2933731708,3487316595,1772371330,1823785759,3813045124,1304563176,2625727395,4034751701,389170009,2539914550,3417039931],[3745749099,117137048,2535594633,4009287526,3011582450,3686928199,2246015868,2190556989,128754455,251282487,1322369199,336317848,783652423,2196981797,1304603190,305597228,4230817681,893020485,2847642738,3082596049,2148113249,674456457,1003703807,3602735589,2874372484,3869771314,1591665775,4272594151,4176695394,4205601753,1503030929,4248761093]]},"rsa_public_key":{"n":[836640533,2811245416,716324048,2993722125,2443208307,1207933224,1763966872,768230429,2698832834,1722770511,3481056265,2483454227,1581944854,1854441585,1267604383,225507178,2248629754,222796813,436665417,2767570513,1751460175,3567334893,158551180,1936216845,4143458509,3490367465,4084860363,1721850123,1379384883,3483780499,4242230917,2422006539,1537799438,3474259944,2996099166,2549725424,2874957611,1494382397,462245471,1722720530,1143308317,1819787583,3197383042,4024279149,1536879499,298310184,1814074952,136661287,782643024,1746597339,900351955,2634931530,4259949916,2983184200,2755062806,1675383366,4274792210,2929612017,3247824938,3718374389,1609377025,274078680,2522020422,3380278664],"e":[65537]}} 254 | Generate_Key_Pair_CACHE: 856.716ms 255 | ``` 256 | 257 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var binary = require('@mapbox/node-pre-gyp'); 2 | var path = require('path') 3 | var binding_path = 4 | binary.find(path.resolve(path.join(__dirname, './package.json'))); 5 | 6 | const os = require('os'); 7 | process.dlopen(module, binding_path, 8 | os.constants.dlopen.RTLD_LAZY | os.constants.dlopen.RTLD_GLOBAL); 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wasmedge-core", 3 | "version": "0.8.1", 4 | "description": "Second State WebAssembly VM for Node.js Addon", 5 | "keywords": [ 6 | "wasm", 7 | "rust", 8 | "webassembly", 9 | "wasi", 10 | "runtime", 11 | "serverless", 12 | "fucntion-as-a-service" 13 | ], 14 | "repository": "https://github.com/second-state/wasmedge-core.git", 15 | "license": "Apache-2.0", 16 | "main": "index.js", 17 | "binary": { 18 | "module_name": "wasmedge", 19 | "module_path": "./lib/binding/{platform}-{arch}/", 20 | "host": "https://github.com/second-state/wasmedge-core/releases/download/", 21 | "remote_path": "{version}", 22 | "package_name": "{module_name}-{platform}-{arch}.tar.gz" 23 | }, 24 | "dependencies": { 25 | "@mapbox/node-pre-gyp": "^1.0.0", 26 | "node-addon-api": "^3.0.0", 27 | "npm": "^7.11.1" 28 | }, 29 | "devDependencies": { 30 | "mocha": "^8.1.3", 31 | "node-gyp-cache": "^0.2.1", 32 | "rustwasmc": "^0.1.29" 33 | }, 34 | "scripts": { 35 | "test": "cd test && ./test.sh", 36 | "preinstall": "./scripts/preinstall.sh", 37 | "install": "node-pre-gyp install --fallback-to-build", 38 | "release": "node-pre-gyp install --fallback-to-build --update-binary" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /scripts/preinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | INSTALL_SCRIPT=/tmp/install_wasmedge.sh 4 | wget -O "$INSTALL_SCRIPT" https://github.com/second-state/WasmEdge-go/releases/download/v0.8.1/install_wasmedge.sh 5 | sh "$INSTALL_SCRIPT" /usr/local 6 | rm -f "$INSTALL_SCRIPT" 7 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ARCH=$1 3 | npm run release 4 | rm -rf wasmedge 5 | mkdir wasmedge 6 | cp build/Release/wasmedge.node wasmedge 7 | strip wasmedge/wasmedge.node 8 | tar zcvf wasmedge-linux-${ARCH}.tar.gz wasmedge 9 | -------------------------------------------------------------------------------- /src/addon.cc: -------------------------------------------------------------------------------- 1 | #include "wasmedgeaddon.h" 2 | 3 | #include 4 | 5 | Napi::Object InitAll(Napi::Env env, Napi::Object exports) { 6 | return WasmEdgeAddon::Init(env, exports); 7 | } 8 | 9 | NODE_API_MODULE(addon, InitAll) 10 | -------------------------------------------------------------------------------- /src/bytecode.cc: -------------------------------------------------------------------------------- 1 | #include "bytecode.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace WASMEDGE { 9 | namespace NAPI { 10 | 11 | void Bytecode::setPath(const std::string &IPath) noexcept { 12 | Path = IPath; 13 | Mode = InputMode::FilePath; 14 | } 15 | 16 | void Bytecode::setData(const std::vector &IData) noexcept { 17 | Data = IData; 18 | if (isWasm()) { 19 | Mode = InputMode::WasmBytecode; 20 | } else if (isELF()) { 21 | Mode = InputMode::ELFBytecode; 22 | } else if (isMachO()) { 23 | Mode = InputMode::MachOBytecode; 24 | } else { 25 | Mode = InputMode::Invalid; 26 | } 27 | } 28 | 29 | const std::vector &Bytecode::getData() noexcept { 30 | if (!isFile()) { 31 | return Data; 32 | } 33 | /// Read bytecode from file is in FilePath mode 34 | std::ifstream File(Path.c_str(), std::ios::binary); 35 | Data = std::vector((std::istreambuf_iterator(File)), 36 | std::istreambuf_iterator()); 37 | File.close(); 38 | return Data; 39 | } 40 | 41 | void Bytecode::setFileMode() noexcept { 42 | if (isFile()) { 43 | return; 44 | } 45 | size_t CodeHash = boost::hash_range(Data.begin(), Data.end()); 46 | Path = std::string("/tmp/wasmedge.tmp.") + std::to_string(CodeHash) + 47 | std::string(".wasm"); 48 | std::ofstream File(Path.c_str()); 49 | std::ostream_iterator OutIter(File); 50 | std::copy(Data.begin(), Data.end(), OutIter); 51 | File.close(); 52 | Mode = InputMode::FilePath; 53 | } 54 | 55 | bool Bytecode::isFile() const noexcept { 56 | if (Mode == InputMode::FilePath) { 57 | return true; 58 | } 59 | return false; 60 | } 61 | 62 | bool Bytecode::isWasm() const noexcept { 63 | if (Data[0] == 0x00 && Data[1] == 0x61 && Data[2] == 0x73 && 64 | Data[3] == 0x6d) { 65 | return true; 66 | } 67 | return false; 68 | } 69 | 70 | bool Bytecode::isELF() const noexcept { 71 | if (Data[0] == 0x7f && Data[1] == 0x45 && Data[2] == 0x4c && 72 | Data[3] == 0x46) { 73 | return true; 74 | } 75 | return false; 76 | } 77 | 78 | bool Bytecode::isMachO() const noexcept { 79 | if ((Data[0] == 0xfe && // Mach-O 32 bit 80 | Data[1] == 0xed && Data[2] == 0xfa && Data[3] == 0xce) || 81 | (Data[0] == 0xfe && // Mach-O 64 bit 82 | Data[1] == 0xed && Data[2] == 0xfa && Data[3] == 0xcf) || 83 | (Data[0] == 0xca && // Mach-O Universal 84 | Data[1] == 0xfe && Data[2] == 0xba && Data[3] == 0xbe)) { 85 | return true; 86 | } 87 | return false; 88 | } 89 | 90 | bool Bytecode::isCompiled() const noexcept { 91 | if (Mode == InputMode::MachOBytecode || Mode == InputMode::ELFBytecode) { 92 | return true; 93 | } 94 | return false; 95 | } 96 | 97 | bool Bytecode::isValidData() const noexcept { 98 | if (isFile()) { 99 | return false; 100 | } 101 | if (isWasm() || isELF() || isMachO()) { 102 | return true; 103 | } 104 | return false; 105 | } 106 | 107 | } // namespace NAPI 108 | } // namespace WASMEDGE 109 | -------------------------------------------------------------------------------- /src/bytecode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace WASMEDGE { 8 | namespace NAPI { 9 | 10 | class Bytecode { 11 | public: 12 | enum class InputMode { 13 | Invalid, 14 | FilePath, 15 | WasmBytecode, 16 | ELFBytecode, 17 | MachOBytecode 18 | }; 19 | 20 | private: 21 | std::vector Data; 22 | std::string Path; 23 | InputMode Mode; 24 | 25 | public: 26 | void setPath(const std::string &IPath) noexcept; 27 | const std::string &getPath() const noexcept { return Path; } 28 | void setData(const std::vector &IData) noexcept; 29 | const std::vector &getData() noexcept; 30 | void setFileMode() noexcept; 31 | bool isFile() const noexcept; 32 | bool isWasm() const noexcept; 33 | bool isELF() const noexcept; 34 | bool isMachO() const noexcept; 35 | bool isCompiled() const noexcept; 36 | bool isValidData() const noexcept; 37 | }; 38 | 39 | } // namespace NAPI 40 | } // namespace WASMEDGE 41 | -------------------------------------------------------------------------------- /src/cache.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::ifstream, std::ofstream 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace WASMEDGE { 11 | namespace NAPI { 12 | 13 | class Cache { 14 | private: 15 | std::string Path; 16 | size_t CodeHash; 17 | 18 | public: 19 | inline void init(const std::vector &Data) { 20 | Path = std::string("/tmp/wasmedge.tmp.") + std::to_string(hash(Data)) + 21 | std::string(".so"); 22 | } 23 | 24 | inline size_t hash(const std::vector &Data) { 25 | CodeHash = boost::hash_range(Data.begin(), Data.end()); 26 | return CodeHash; 27 | } 28 | 29 | inline bool isCached() const { 30 | std::ifstream File(Path.c_str()); 31 | bool Cached = File.good(); 32 | File.close(); 33 | return Cached; 34 | } 35 | 36 | inline const std::string &getPath() const noexcept { return Path; } 37 | 38 | inline void dumpToFile(const std::vector &Data) { 39 | init(Data); 40 | std::ofstream File(Path.c_str()); 41 | std::ostream_iterator OutIter(File); 42 | std::copy(Data.begin(), Data.end(), OutIter); 43 | File.close(); 44 | } 45 | }; 46 | 47 | } // namespace NAPI 48 | } // namespace WASMEDGE 49 | -------------------------------------------------------------------------------- /src/errors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace WASMEDGE { 8 | namespace NAPI { 9 | 10 | enum class ErrorType { 11 | ExpectWasmFileOrBytecode, 12 | ParseOptionsFailed, 13 | UnknownBytecodeFormat, 14 | UnsupportedArgumentType, 15 | InvalidInputFormat, 16 | LoadWasmFailed, 17 | ValidateWasmFailed, 18 | InstantiateWasmFailed, 19 | ExecutionFailed, 20 | BadMemoryAccess, 21 | InitReactorFailed, 22 | WasmBindgenMallocFailed, 23 | WasmBindgenFreeFailed, 24 | NAPIUnkownIntType 25 | }; 26 | 27 | const std::map ErrorMsgs = { 28 | {ErrorType::ExpectWasmFileOrBytecode, 29 | "Expected a Wasm file or a Wasm binary sequence."}, 30 | {ErrorType::ParseOptionsFailed, "Parse options failed."}, 31 | {ErrorType::UnknownBytecodeFormat, "Unknown bytecode format."}, 32 | {ErrorType::InvalidInputFormat, 33 | "Input Wasm is not a valid Uint8Array or not a valid file path."}, 34 | {ErrorType::LoadWasmFailed, 35 | "Wasm bytecode/file cannot be loaded correctly."}, 36 | {ErrorType::ValidateWasmFailed, 37 | "Wasm bytecode/file failed at validation stage."}, 38 | {ErrorType::InstantiateWasmFailed, 39 | "Wasm bytecode/file cannot be instantiated."}, 40 | {ErrorType::ExecutionFailed, "WASMEDGE execution failed"}, 41 | {ErrorType::BadMemoryAccess, 42 | "Access to forbidden memory address when retrieving address and " 43 | "length of result data"}, 44 | {ErrorType::InitReactorFailed, "Initialize wasi reactor mode failed"}, 45 | {ErrorType::WasmBindgenMallocFailed, 46 | "Failed to call wasm-bindgen helper function __wbindgen_malloc"}, 47 | {ErrorType::WasmBindgenFreeFailed, 48 | "Failed to call wasm-bindgen helper function __wbindgen_free"}, 49 | {ErrorType::NAPIUnkownIntType, 50 | "WASMEDGE-Napi implementation error: unknown integer type"}, 51 | {ErrorType::UnsupportedArgumentType, "Unsupported argument type"}}; 52 | 53 | } // namespace NAPI 54 | } // namespace WASMEDGE 55 | -------------------------------------------------------------------------------- /src/options.cc: -------------------------------------------------------------------------------- 1 | #include "options.h" 2 | 3 | namespace WASMEDGE { 4 | namespace NAPI { 5 | 6 | namespace { 7 | 8 | bool parseWasiStartFlag(const Napi::Object &Options) { 9 | if (Options.Has("EnableWasiStartFunction") && 10 | Options.Get("EnableWasiStartFunction").IsBoolean()) { 11 | return Options.Get("EnableWasiStartFunction").As().Value(); 12 | } 13 | return false; 14 | } 15 | 16 | bool parseAllowedCmds(std::vector &AllowedCmds, const Napi::Object &Options) { 17 | AllowedCmds.clear(); 18 | if (Options.Has(kAllowedCommandsString) && Options.Get(kAllowedCommandsString).IsArray()) { 19 | Napi::Array Cmds = Options.Get(kAllowedCommandsString).As(); 20 | for (uint32_t I = 0; I < Cmds.Length(); I++) { 21 | Napi::Value Cmd = Cmds[I]; 22 | if (Cmd.IsString()) { 23 | AllowedCmds.push_back(Cmd.As().Utf8Value()); 24 | } else { 25 | // Invalid inputs 26 | return false; 27 | } 28 | } 29 | } 30 | return true; 31 | } 32 | 33 | bool parseAllowedCmdsAll(const Napi::Object &Options) { 34 | if (Options.Has(kAllowedCommandsAllString) && Options.Get(kAllowedCommandsAllString).IsBoolean()) { 35 | return Options.Get(kAllowedCommandsAllString).As().Value(); 36 | } 37 | return false; 38 | } 39 | 40 | 41 | bool parseCmdArgs(std::vector &CmdArgs, 42 | const Napi::Object &Options) { 43 | CmdArgs.clear(); 44 | if (Options.Has(kCmdArgsString) && Options.Get(kCmdArgsString).IsArray()) { 45 | Napi::Array Args = Options.Get(kCmdArgsString).As(); 46 | for (uint32_t i = 0; i < Args.Length(); i++) { 47 | Napi::Value Arg = Args[i]; 48 | if (Arg.IsNumber()) { 49 | CmdArgs.push_back(std::to_string(Arg.As().Uint32Value())); 50 | } else if (Arg.IsString()) { 51 | CmdArgs.push_back(Arg.As().Utf8Value()); 52 | } else if (Arg.IsTypedArray() && 53 | Arg.As().TypedArrayType() == 54 | napi_uint8_array) { 55 | Napi::ArrayBuffer DataBuffer = Arg.As().ArrayBuffer(); 56 | std::string ArrayArg = std::string( 57 | static_cast(DataBuffer.Data()), 58 | static_cast(DataBuffer.Data()) + DataBuffer.ByteLength()); 59 | CmdArgs.push_back(ArrayArg); 60 | } else { 61 | // TODO: support other types 62 | return false; 63 | } 64 | } 65 | } 66 | return true; 67 | } 68 | 69 | bool parseDirs(std::vector &Dirs, const Napi::Object &Options) { 70 | Dirs.clear(); 71 | if (Options.Has(kPreOpensString) && Options.Get(kPreOpensString).IsObject()) { 72 | Napi::Object Preopens = Options.Get(kPreOpensString).As(); 73 | Napi::Array Keys = Preopens.GetPropertyNames(); 74 | for (uint32_t i = 0; i < Keys.Length(); i++) { 75 | // Dir format: : 76 | Napi::Value Key = Keys[i]; 77 | if (!Key.IsString()) { 78 | // host path must be a string 79 | return false; 80 | } 81 | std::string Dir = Key.As().Utf8Value(); 82 | Dir.append(":"); 83 | Napi::Value Value = Preopens.Get(Key); 84 | if (!Value.IsString()) { 85 | // guest path must be a string 86 | return false; 87 | } 88 | Dir.append(Value.As().Utf8Value()); 89 | Dirs.push_back(Dir); 90 | } 91 | } 92 | return true; 93 | } 94 | 95 | bool parseEnvs(std::vector &Envs, const Napi::Object &Options) { 96 | Envs.clear(); 97 | if (Options.Has(kEnvString) && Options.Get(kEnvString).IsObject()) { 98 | Napi::Object Environs = Options.Get(kEnvString).As(); 99 | Napi::Array Keys = Environs.GetPropertyNames(); 100 | for (uint32_t i = 0; i < Keys.Length(); i++) { 101 | // Environ format: = 102 | Napi::Value Key = Keys[i]; 103 | if (!Key.IsString()) { 104 | // key must be a string 105 | return false; 106 | } 107 | std::string Env = Key.As().Utf8Value(); 108 | Env.append("="); 109 | Napi::Value Value = Environs.Get(Key); 110 | if (!Value.IsString()) { 111 | // value must be a string 112 | return false; 113 | } 114 | Env.append(Value.As().Utf8Value()); 115 | Envs.push_back(Env); 116 | } 117 | } 118 | return true; 119 | } 120 | 121 | bool parseAOTConfig(const Napi::Object &Options) { 122 | if (Options.Has(kEnableAOTString) && Options.Get(kEnableAOTString).IsBoolean()) { 123 | return Options.Get(kEnableAOTString).As().Value(); 124 | } 125 | return false; 126 | } 127 | 128 | bool parseMeasure(const Napi::Object &Options) { 129 | if (Options.Has(kEnableMeasurementString) && 130 | Options.Get(kEnableMeasurementString).IsBoolean()) { 131 | return Options.Get(kEnableMeasurementString).As().Value(); 132 | } 133 | return false; 134 | } 135 | 136 | } // namespace 137 | 138 | bool Options::parse(const Napi::Object &Options) { 139 | if (!parseCmdArgs(getWasiCmdArgs(), Options) || 140 | !parseDirs(getWasiDirs(), Options) || 141 | !parseEnvs(getWasiEnvs(), Options) || 142 | !parseAllowedCmds(getAllowedCmds(), Options)) { 143 | return false; 144 | } 145 | setReactorMode(!parseWasiStartFlag(Options)); 146 | setAOTMode(parseAOTConfig(Options)); 147 | setMeasure(parseMeasure(Options)); 148 | setAllowedCmdsAll(parseAllowedCmdsAll(Options)); 149 | return true; 150 | } 151 | 152 | } // namespace NAPI 153 | } // namespace WASMEDGE 154 | -------------------------------------------------------------------------------- /src/options.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace WASMEDGE { 8 | namespace NAPI { 9 | 10 | static inline std::string kAllowedCommandsString [[maybe_unused]] = "AllowCommands"; 11 | static inline std::string kAllowedCommandsAllString [[maybe_unused]] = "AllowAllCommands"; 12 | static inline std::string kCmdArgsString [[maybe_unused]] = "args"; 13 | static inline std::string kPreOpensString [[maybe_unused]] = "preopens"; 14 | static inline std::string kEnvString [[maybe_unused]] = "env"; 15 | static inline std::string kEnableAOTString [[maybe_unused]] = "EnableAOT"; 16 | static inline std::string kEnableMeasurementString [[maybe_unused]] = "EnableMeasurement"; 17 | 18 | class Options { 19 | private: 20 | bool ReactorMode; 21 | bool AOTMode; 22 | bool Measure; 23 | bool AllowedCmdsAll; 24 | std::vector WasiCmdArgs, WasiDirs, WasiEnvs, AllowedCmds; 25 | 26 | public: 27 | void setReactorMode(bool Value = true) { ReactorMode = Value; } 28 | void setAOTMode(bool Value = true) { AOTMode = Value; } 29 | void setMeasure(bool Value = true) { Measure = Value; } 30 | void setAllowedCmdsAll(bool Value = true) { AllowedCmdsAll = Value; } 31 | void setWasiCmdArgs(const std::vector &WCA) { 32 | WasiCmdArgs = WCA; 33 | } 34 | void setAllowedCmds(const std::vector &AC) { 35 | AllowedCmds = AC; 36 | } 37 | void setWasiDirs(const std::vector &WD) { WasiDirs = WD; } 38 | void setWasiEnvs(const std::vector &WE) { WasiEnvs = WE; } 39 | bool isReactorMode() const noexcept { return ReactorMode; } 40 | bool isAOTMode() const noexcept { return AOTMode; } 41 | bool isMeasuring() const noexcept { return Measure; } 42 | bool isAllowedCmdsAll() const noexcept { return AllowedCmdsAll; } 43 | const std::vector &getWasiCmdArgs() const { return WasiCmdArgs; } 44 | std::vector &getWasiCmdArgs() { return WasiCmdArgs; } 45 | const std::vector &getAllowedCmds() const { return AllowedCmds; } 46 | std::vector &getAllowedCmds() { return AllowedCmds; } 47 | const std::vector &getWasiDirs() const { return WasiDirs; } 48 | std::vector &getWasiDirs() { return WasiDirs; } 49 | const std::vector &getWasiEnvs() const { return WasiEnvs; } 50 | std::vector &getWasiEnvs() { return WasiEnvs; } 51 | bool parse(const Napi::Object &Options); 52 | }; 53 | 54 | } // namespace NAPI 55 | } // namespace WASMEDGE 56 | -------------------------------------------------------------------------------- /src/utils.cc: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace WASMEDGE { 7 | namespace NAPI { 8 | 9 | bool checkLibCXXVersion() { 10 | #ifdef __GLIBCXX__ 11 | #ifdef __aarch64__ 12 | std::string GLibPath = "/usr/lib/aarch64-linux-gnu"; 13 | #endif 14 | #ifdef __x86_64__ 15 | std::string GLibPath = "/usr/lib/x86_64-linux-gnu"; 16 | #endif 17 | std::string GLibCXXName = "libstdc++.so.6.0."; 18 | std::string CurrentGLibVer = "unknown"; 19 | bool IsVersionCompatible = false; 20 | for (const auto &Entry : std::filesystem::directory_iterator(GLibPath)) { 21 | std::string LibName = Entry.path().filename().string(); 22 | size_t Pos = LibName.find(GLibCXXName); 23 | if (Pos != std::string::npos) { 24 | CurrentGLibVer = LibName; 25 | std::string GV = LibName.substr(GLibCXXName.length(), LibName.length()); 26 | if (std::stoi(GV) >= 28) { 27 | IsVersionCompatible = true; 28 | } 29 | } 30 | } 31 | if (!IsVersionCompatible) { 32 | std::cerr << "=============================================================" 33 | "=======\n" 34 | << "Error: libstdc++ version mismatched!\n" 35 | << "Your current version is " << CurrentGLibVer 36 | << " which is less than libstdc++6.0.28\n" 37 | << "WASMEDGE relies on >=libstdc++6.0.28 (GLIBCXX >= 3.4.28)\n" 38 | << "Please upgrade the libstdc++6 library.\n\n" 39 | << "For more details, refer to our environment set up document: " 40 | "https://www.secondstate.io/articles/setup-rust-nodejs/\n" 41 | << "=============================================================" 42 | "=======\n"; 43 | return false; 44 | } else { 45 | return true; 46 | } 47 | #endif 48 | return false; 49 | } 50 | 51 | } // namespace NAPI 52 | } // namespace WASMEDGE 53 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #if defined(__cpp_lib_filesystem) 5 | #define EXPERIMENTAL 0 6 | #elif defined(__cpp_lib_experimental_filesystem) 7 | #define EXPERIMENTAL 1 8 | #elif !defined(__has_include) 9 | #define EXPERIMENTAL 1 10 | #elif __has_include() 11 | #ifdef _MSC_VER 12 | #if __has_include() 13 | #include 14 | #if defined(_HAS_CXX17) && _HAS_CXX17 15 | #define EXPERIMENTAL 0 16 | #else 17 | #define EXPERIMENTAL 1 18 | #endif 19 | #else 20 | #define EXPERIMENTAL 1 21 | #endif 22 | #else 23 | #define EXPERIMENTAL 0 24 | #endif 25 | #elif __has_include() 26 | #define EXPERIMENTAL 1 27 | #else 28 | #error Could not find system header "" or "" 29 | #endif 30 | 31 | #if EXPERIMENTAL 32 | #include 33 | namespace std { 34 | namespace filesystem = experimental::filesystem; 35 | } 36 | #else 37 | #include 38 | #endif 39 | 40 | #undef EXPERIMENTAL 41 | 42 | namespace WASMEDGE { 43 | namespace NAPI { 44 | 45 | // Check glibcxx version 46 | // If the version is incompatible, reporting an error message to users 47 | // Requires: libstdc++6 >= 6.0.28 (GLIBCXX >= 3.4.28) 48 | bool checkLibCXXVersion(); 49 | 50 | } // namespace NAPI 51 | } // namespace WASMEDGE 52 | -------------------------------------------------------------------------------- /src/wasmedgeaddon.cc: -------------------------------------------------------------------------------- 1 | #include "wasmedgeaddon.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | Napi::FunctionReference WasmEdgeAddon::Constructor; 10 | 11 | Napi::Object WasmEdgeAddon::Init(Napi::Env Env, Napi::Object Exports) { 12 | Napi::HandleScope Scope(Env); 13 | 14 | WASMEDGE::NAPI::checkLibCXXVersion(); 15 | 16 | Napi::Function Func = DefineClass( 17 | Env, "VM", 18 | {InstanceMethod("GetStatistics", &WasmEdgeAddon::GetStatistics), 19 | InstanceMethod("Start", &WasmEdgeAddon::RunStart), 20 | InstanceMethod("Compile", &WasmEdgeAddon::RunCompile), 21 | InstanceMethod("Run", &WasmEdgeAddon::Run), 22 | InstanceMethod("RunInt", &WasmEdgeAddon::RunInt), 23 | InstanceMethod("RunUInt", &WasmEdgeAddon::RunUInt), 24 | InstanceMethod("RunInt64", &WasmEdgeAddon::RunInt64), 25 | InstanceMethod("RunUInt64", &WasmEdgeAddon::RunUInt64), 26 | InstanceMethod("RunString", &WasmEdgeAddon::RunString), 27 | InstanceMethod("RunUint8Array", &WasmEdgeAddon::RunUint8Array)}); 28 | 29 | Constructor = Napi::Persistent(Func); 30 | Constructor.SuppressDestruct(); 31 | 32 | Exports.Set("VM", Func); 33 | return Exports; 34 | } 35 | 36 | namespace { 37 | inline bool checkInputWasmFormat(const Napi::CallbackInfo &Info) { 38 | return Info.Length() <= 0 || (!Info[0].IsString() && !Info[0].IsTypedArray()); 39 | } 40 | 41 | inline bool isWasiOptionsProvided(const Napi::CallbackInfo &Info) { 42 | return Info.Length() == 2 && Info[1].IsObject(); 43 | } 44 | 45 | inline uint32_t castFromU64ToU32(uint64_t V) { 46 | return static_cast( 47 | V & static_cast(std::numeric_limits::max())); 48 | } 49 | 50 | inline uint32_t castFromBytesToU32(const uint8_t *bytes, int Idx) { 51 | return bytes[Idx] | (bytes[Idx + 1] << 8) | (bytes[Idx + 2] << 16) | 52 | (bytes[Idx + 3] << 24); 53 | } 54 | 55 | inline uint64_t castFromU32ToU64(uint32_t L, uint32_t H) { 56 | return static_cast(L) | (static_cast(H) << 32); 57 | } 58 | 59 | inline bool endsWith(const std::string &S, const std::string &Suffix) { 60 | return S.length() >= Suffix.length() && 61 | S.compare(S.length() - Suffix.length(), std::string::npos, Suffix) == 62 | 0; 63 | } 64 | 65 | } // namespace 66 | 67 | WasmEdgeAddon::WasmEdgeAddon(const Napi::CallbackInfo &Info) 68 | : Napi::ObjectWrap(Info), Configure(nullptr), VM(nullptr), 69 | MemInst(nullptr), WasiMod(nullptr), Inited(false) { 70 | Napi::Env Env = Info.Env(); 71 | Napi::HandleScope Scope(Env); 72 | 73 | if (checkInputWasmFormat(Info)) { 74 | napi_throw_error( 75 | Info.Env(), "Error", 76 | WASMEDGE::NAPI::ErrorMsgs.at(ErrorType::ExpectWasmFileOrBytecode) 77 | .c_str()); 78 | return; 79 | } 80 | 81 | // Assume the WasmBindgen is enabled by default. 82 | // Assume the WASI options object is {} 83 | 84 | // Check if a Wasi options object is given or not 85 | if (isWasiOptionsProvided(Info)) { 86 | // Get a WASI options object 87 | Napi::Object WasiOptions = Info[1].As(); 88 | if (!Options.parse(WasiOptions)) { 89 | napi_throw_error( 90 | Info.Env(), "Error", 91 | WASMEDGE::NAPI::ErrorMsgs.at(ErrorType::ParseOptionsFailed).c_str()); 92 | return; 93 | } 94 | } 95 | 96 | // Handle input wasm 97 | if (Info[0].IsString()) { 98 | // Wasm file path 99 | BC.setPath(std::move(Info[0].As().Utf8Value())); 100 | } else if (Info[0].IsTypedArray() && 101 | Info[0].As().TypedArrayType() == 102 | napi_uint8_array) { 103 | size_t Length = Info[0].As().ElementLength(); 104 | size_t Offset = Info[0].As().ByteOffset(); 105 | // Wasm binary format 106 | Napi::ArrayBuffer DataBuffer = Info[0].As().ArrayBuffer(); 107 | BC.setData(std::move(std::vector( 108 | static_cast(DataBuffer.Data()) + Offset, 109 | static_cast(DataBuffer.Data()) + Offset + Length))); 110 | 111 | if (!BC.isValidData()) { 112 | napi_throw_error( 113 | Info.Env(), "Error", 114 | WASMEDGE::NAPI::ErrorMsgs.at(ErrorType::UnknownBytecodeFormat) 115 | .c_str()); 116 | return; 117 | } 118 | } else { 119 | napi_throw_error( 120 | Info.Env(), "Error", 121 | WASMEDGE::NAPI::ErrorMsgs.at(ErrorType::InvalidInputFormat).c_str()); 122 | return; 123 | } 124 | } 125 | 126 | void WasmEdgeAddon::InitVM(const Napi::CallbackInfo &Info) { 127 | if (Inited) { 128 | return; 129 | } 130 | 131 | Store = WasmEdge_StoreCreate(); 132 | Configure = WasmEdge_ConfigureCreate(); 133 | Stat = WasmEdge_StatisticsCreate(); 134 | WasmEdge_ConfigureAddProposal(Configure, 135 | WasmEdge_Proposal_BulkMemoryOperations); 136 | WasmEdge_ConfigureAddProposal(Configure, WasmEdge_Proposal_ReferenceTypes); 137 | WasmEdge_ConfigureAddProposal(Configure, WasmEdge_Proposal_SIMD); 138 | WasmEdge_ConfigureAddHostRegistration(Configure, 139 | WasmEdge_HostRegistration_Wasi); 140 | WasmEdge_ConfigureAddHostRegistration( 141 | Configure, WasmEdge_HostRegistration_WasmEdge_Process); 142 | VM = WasmEdge_VMCreate(Configure, Store); 143 | 144 | WasmEdge_LogSetErrorLevel(); 145 | 146 | WasmEdge_ImportObjectContext *ProcObject = WasmEdge_VMGetImportModuleContext( 147 | VM, WasmEdge_HostRegistration_WasmEdge_Process); 148 | std::vector AllowCmds; 149 | AllowCmds.reserve(Options.getAllowedCmds().size()); 150 | for (auto &cmd : Options.getAllowedCmds()) { 151 | AllowCmds.push_back(cmd.c_str()); 152 | } 153 | WasmEdge_ImportObjectInitWasmEdgeProcess(ProcObject, AllowCmds.data(), 154 | AllowCmds.size(), 155 | Options.isAllowedCmdsAll()); 156 | 157 | Inited = true; 158 | } 159 | 160 | void WasmEdgeAddon::FiniVM() { 161 | if (!Inited) { 162 | return; 163 | } 164 | 165 | Stat = WasmEdge_VMGetStatisticsContext(VM); 166 | WasmEdge_VMDelete(VM); 167 | VM = nullptr; 168 | WasmEdge_StoreDelete(Store); 169 | Store = nullptr; 170 | WasmEdge_ConfigureDelete(Configure); 171 | Configure = nullptr; 172 | MemInst = nullptr; 173 | WasiMod = nullptr; 174 | 175 | Inited = false; 176 | } 177 | 178 | void WasmEdgeAddon::InitWasi(const Napi::CallbackInfo &Info, 179 | const std::string &FuncName) { 180 | WasiMod = 181 | WasmEdge_VMGetImportModuleContext(VM, WasmEdge_HostRegistration_Wasi); 182 | 183 | /// Origin input can be Bytecode or FilePath 184 | if (Options.isAOTMode()) { 185 | if (BC.isFile() && endsWith(BC.getPath(), ".so")) { 186 | // BC is already the compiled filename, do nothing 187 | } else if (!BC.isCompiled()) { 188 | Compile(); 189 | } 190 | /// After Compile(), {Bytecode, FilePath} -> {FilePath} 191 | } 192 | 193 | if (Options.isReactorMode()) { 194 | LoadWasm(Info); 195 | } 196 | 197 | std::vector WasiCmdArgs; 198 | WasiCmdArgs.reserve(Options.getWasiCmdArgs().size()); 199 | for (auto &cmd : Options.getWasiCmdArgs()) { 200 | WasiCmdArgs.push_back(cmd.c_str()); 201 | } 202 | std::vector WasiEnvs; 203 | WasiEnvs.reserve(Options.getWasiEnvs().size()); 204 | for (auto &env : Options.getWasiEnvs()) { 205 | WasiEnvs.push_back(env.c_str()); 206 | } 207 | std::vector WasiDirs; 208 | WasiDirs.reserve(Options.getWasiDirs().size()); 209 | for (auto &dir : Options.getWasiDirs()) { 210 | WasiDirs.push_back(dir.c_str()); 211 | } 212 | WasmEdge_ImportObjectInitWASI(WasiMod, WasiCmdArgs.data(), WasiCmdArgs.size(), 213 | WasiEnvs.data(), WasiEnvs.size(), 214 | WasiDirs.data(), WasiDirs.size(), nullptr, 0); 215 | 216 | if (Options.isAOTMode()) { 217 | InitReactor(Info); 218 | } 219 | } 220 | 221 | void WasmEdgeAddon::ThrowNapiError(const Napi::CallbackInfo &Info, 222 | ErrorType Type) { 223 | FiniVM(); 224 | napi_throw_error(Info.Env(), "Error", 225 | WASMEDGE::NAPI::ErrorMsgs.at(Type).c_str()); 226 | } 227 | 228 | bool WasmEdgeAddon::Compile() { 229 | /// Calculate hash and path. 230 | Cache.init(BC.getData()); 231 | 232 | /// If the compiled bytecode existed, return directly. 233 | if (!Cache.isCached()) { 234 | /// Cache not found. Compile wasm bytecode 235 | if (auto Res = CompileBytecodeTo(Cache.getPath()); !Res) { 236 | return false; 237 | } 238 | } 239 | 240 | /// After compiled Bytecode, the output will be written to a FilePath. 241 | BC.setPath(Cache.getPath()); 242 | return true; 243 | } 244 | 245 | bool WasmEdgeAddon::CompileBytecodeTo(const std::string &Path) { 246 | /// Make sure BC is in FilePath mode 247 | BC.setFileMode(); 248 | 249 | if (Options.isMeasuring()) { 250 | WasmEdge_ConfigureCompilerSetCostMeasuring(Configure, true); 251 | WasmEdge_ConfigureCompilerSetInstructionCounting(Configure, true); 252 | } 253 | WasmEdge_CompilerContext *CompilerCxt = WasmEdge_CompilerCreate(Configure); 254 | WasmEdge_Result Res = 255 | WasmEdge_CompilerCompile(CompilerCxt, BC.getPath().c_str(), Path.c_str()); 256 | if (!WasmEdge_ResultOK(Res)) { 257 | std::cerr << "WasmEdge Compile failed. Error: " 258 | << WasmEdge_ResultGetMessage(Res); 259 | return false; 260 | } 261 | return true; 262 | } 263 | 264 | void WasmEdgeAddon::PrepareResource(const Napi::CallbackInfo &Info, 265 | std::vector &Args, 266 | IntKind IntT) { 267 | for (std::size_t I = 1; I < Info.Length(); I++) { 268 | Napi::Value Arg = Info[I]; 269 | uint32_t MallocSize = 0, MallocAddr = 0; 270 | if (Arg.IsNumber()) { 271 | switch (IntT) { 272 | case IntKind::SInt32: 273 | case IntKind::UInt32: 274 | case IntKind::Default: 275 | Args.emplace_back( 276 | WasmEdge_ValueGenI32(Arg.As().Int32Value())); 277 | break; 278 | case IntKind::SInt64: 279 | case IntKind::UInt64: { 280 | if (Args.size() == 0) { 281 | // Set memory offset for return value 282 | Args.emplace_back(WasmEdge_ValueGenI32(0)); 283 | } 284 | uint64_t V = static_cast(Arg.As().Int64Value()); 285 | Args.emplace_back(WasmEdge_ValueGenI32(castFromU64ToU32(V))); 286 | Args.emplace_back(WasmEdge_ValueGenI32(castFromU64ToU32(V >> 32))); 287 | break; 288 | } 289 | default: 290 | napi_throw_error( 291 | Info.Env(), "Error", 292 | WASMEDGE::NAPI::ErrorMsgs.at(ErrorType::NAPIUnkownIntType).c_str()); 293 | return; 294 | } 295 | continue; 296 | } else if (Arg.IsString()) { 297 | std::string StrArg = Arg.As().Utf8Value(); 298 | MallocSize = StrArg.length(); 299 | } else if (Arg.IsTypedArray() && 300 | Arg.As().TypedArrayType() == 301 | napi_uint8_array) { 302 | Napi::ArrayBuffer DataBuffer = Arg.As().ArrayBuffer(); 303 | MallocSize = DataBuffer.ByteLength(); 304 | } else { 305 | // TODO: support other types 306 | napi_throw_error( 307 | Info.Env(), "Error", 308 | WASMEDGE::NAPI::ErrorMsgs.at(ErrorType::UnsupportedArgumentType) 309 | .c_str()); 310 | return; 311 | } 312 | 313 | // Malloc 314 | WasmEdge_Value Params = WasmEdge_ValueGenI32(MallocSize); 315 | WasmEdge_Value Rets; 316 | WasmEdge_String FuncName = 317 | WasmEdge_StringCreateByCString("__wbindgen_malloc"); 318 | WasmEdge_Result Res = 319 | WasmEdge_VMExecute(VM, FuncName, &Params, 1, &Rets, 1); 320 | WasmEdge_StringDelete(FuncName); 321 | if (!WasmEdge_ResultOK(Res)) { 322 | napi_throw_error(Info.Env(), "Error", WasmEdge_ResultGetMessage(Res)); 323 | return; 324 | } 325 | MallocAddr = (uint32_t)WasmEdge_ValueGetI32(Rets); 326 | 327 | // Prepare arguments and memory data 328 | Args.emplace_back(WasmEdge_ValueGenI32(MallocAddr)); 329 | Args.emplace_back(WasmEdge_ValueGenI32(MallocSize)); 330 | 331 | // Setup memory 332 | if (Arg.IsString()) { 333 | std::string StrArg = Arg.As().Utf8Value(); 334 | std::vector StrArgVec(StrArg.begin(), StrArg.end()); 335 | WasmEdge_MemoryInstanceSetData(MemInst, StrArgVec.data(), MallocAddr, 336 | StrArgVec.size()); 337 | } else if (Arg.IsTypedArray() && 338 | Arg.As().TypedArrayType() == 339 | napi_uint8_array) { 340 | Napi::ArrayBuffer DataBuffer = Arg.As().ArrayBuffer(); 341 | uint8_t *Data = (uint8_t *)DataBuffer.Data(); 342 | WasmEdge_MemoryInstanceSetData(MemInst, Data, MallocAddr, 343 | DataBuffer.ByteLength()); 344 | } 345 | } 346 | } 347 | 348 | void WasmEdgeAddon::PrepareResource(const Napi::CallbackInfo &Info, 349 | std::vector &Args) { 350 | PrepareResource(Info, Args, IntKind::Default); 351 | } 352 | 353 | void WasmEdgeAddon::ReleaseResource(const Napi::CallbackInfo &Info, 354 | const uint32_t Offset, 355 | const uint32_t Size) { 356 | WasmEdge_Value Params[2] = {WasmEdge_ValueGenI32(Offset), 357 | WasmEdge_ValueGenI32(Size)}; 358 | WasmEdge_String WasmFuncName = 359 | WasmEdge_StringCreateByCString("__wbindgen_free"); 360 | WasmEdge_Result Res = 361 | WasmEdge_VMExecute(VM, WasmFuncName, Params, 2, nullptr, 0); 362 | WasmEdge_StringDelete(WasmFuncName); 363 | 364 | if (!WasmEdge_ResultOK(Res)) { 365 | napi_throw_error( 366 | Info.Env(), "Error", 367 | WASMEDGE::NAPI::ErrorMsgs.at(ErrorType::WasmBindgenFreeFailed).c_str()); 368 | return; 369 | } 370 | } 371 | 372 | Napi::Value WasmEdgeAddon::RunStart(const Napi::CallbackInfo &Info) { 373 | InitVM(Info); 374 | 375 | std::string FuncName = "_start"; 376 | const std::vector &WasiCmdArgs = Options.getWasiCmdArgs(); 377 | Options.getWasiCmdArgs().erase(WasiCmdArgs.begin(), WasiCmdArgs.begin() + 2); 378 | 379 | InitWasi(Info, FuncName); 380 | 381 | // command mode 382 | WasmEdge_String WasmFuncName = 383 | WasmEdge_StringCreateByCString(FuncName.c_str()); 384 | WasmEdge_Value Ret; 385 | WasmEdge_Result Res = WasmEdge_VMRunWasmFromFile( 386 | VM, BC.getPath().c_str(), WasmFuncName, nullptr, 0, &Ret, 1); 387 | WasmEdge_StringDelete(WasmFuncName); 388 | 389 | if (!WasmEdge_ResultOK(Res)) { 390 | ThrowNapiError(Info, ErrorType::ExecutionFailed); 391 | return Napi::Value(); 392 | } 393 | auto ErrCode = WasmEdge_ResultGetCode(Res); 394 | FiniVM(); 395 | return Napi::Number::New(Info.Env(), ErrCode); 396 | } 397 | 398 | void WasmEdgeAddon::InitReactor(const Napi::CallbackInfo &Info) { 399 | using namespace std::literals::string_literals; 400 | WasmEdge_String InitFunc = WasmEdge_StringCreateByCString("_initialize"); 401 | 402 | bool HasInit = false; 403 | 404 | uint32_t FuncListLen = WasmEdge_VMGetFunctionListLength(VM); 405 | WasmEdge_String FuncList[FuncListLen]; 406 | WasmEdge_VMGetFunctionList(VM, FuncList, nullptr, FuncListLen); 407 | for (std::size_t I = 1; I < FuncListLen; I++) { 408 | if (WasmEdge_StringIsEqual(FuncList[I], InitFunc)) { 409 | HasInit = true; 410 | } 411 | } 412 | 413 | if (HasInit) { 414 | WasmEdge_Value Ret; 415 | WasmEdge_Result Res = WasmEdge_VMExecute(VM, InitFunc, nullptr, 0, &Ret, 1); 416 | if (!WasmEdge_ResultOK(Res)) { 417 | napi_throw_error( 418 | Info.Env(), "Error", 419 | WASMEDGE::NAPI::ErrorMsgs.at(ErrorType::InitReactorFailed).c_str()); 420 | } 421 | } 422 | WasmEdge_StringDelete(InitFunc); 423 | } 424 | 425 | void WasmEdgeAddon::Run(const Napi::CallbackInfo &Info) { 426 | InitVM(Info); 427 | 428 | std::string FuncName = ""; 429 | if (Info.Length() > 0) { 430 | FuncName = Info[0].As().Utf8Value(); 431 | } 432 | 433 | InitWasi(Info, FuncName); 434 | 435 | std::vector Args; 436 | PrepareResource(Info, Args); 437 | WasmEdge_String WasmFuncName = 438 | WasmEdge_StringCreateByCString(FuncName.c_str()); 439 | WasmEdge_Value Ret; 440 | WasmEdge_Result Res = 441 | WasmEdge_VMExecute(VM, WasmFuncName, Args.data(), Args.size(), &Ret, 1); 442 | WasmEdge_StringDelete(WasmFuncName); 443 | 444 | if (!WasmEdge_ResultOK(Res)) { 445 | ThrowNapiError(Info, ErrorType::ExecutionFailed); 446 | } 447 | 448 | FiniVM(); 449 | } 450 | 451 | Napi::Value WasmEdgeAddon::RunCompile(const Napi::CallbackInfo &Info) { 452 | std::string FileName; 453 | if (Info.Length() > 0) { 454 | FileName = Info[0].As().Utf8Value(); 455 | } 456 | 457 | return Napi::Value::From(Info.Env(), CompileBytecodeTo(FileName)); 458 | } 459 | 460 | Napi::Value WasmEdgeAddon::RunIntImpl(const Napi::CallbackInfo &Info, 461 | IntKind IntT) { 462 | InitVM(Info); 463 | std::string FuncName = ""; 464 | if (Info.Length() > 0) { 465 | FuncName = Info[0].As().Utf8Value(); 466 | } 467 | 468 | InitWasi(Info, FuncName); 469 | 470 | std::vector Args; 471 | PrepareResource(Info, Args, IntT); 472 | WasmEdge_String WasmFuncName = 473 | WasmEdge_StringCreateByCString(FuncName.c_str()); 474 | WasmEdge_Value Ret; 475 | WasmEdge_Result Res = 476 | WasmEdge_VMExecute(VM, WasmFuncName, Args.data(), Args.size(), &Ret, 1); 477 | WasmEdge_StringDelete(WasmFuncName); 478 | 479 | if (WasmEdge_ResultOK(Res)) { 480 | switch (IntT) { 481 | case IntKind::SInt32: 482 | case IntKind::UInt32: 483 | case IntKind::Default: 484 | FiniVM(); 485 | return Napi::Number::New(Info.Env(), (uint32_t)WasmEdge_ValueGetI32(Ret)); 486 | case IntKind::SInt64: 487 | case IntKind::UInt64: 488 | uint8_t ResultMem[8]; 489 | Res = WasmEdge_MemoryInstanceGetData(MemInst, ResultMem, 0, 8); 490 | if (WasmEdge_ResultOK(Res)) { 491 | uint32_t L = castFromBytesToU32(ResultMem, 0); 492 | uint32_t H = castFromBytesToU32(ResultMem, 4); 493 | FiniVM(); 494 | return Napi::Number::New(Info.Env(), castFromU32ToU64(L, H)); 495 | } 496 | [[fallthrough]]; 497 | default: 498 | ThrowNapiError(Info, ErrorType::NAPIUnkownIntType); 499 | return Napi::Value(); 500 | } 501 | } else { 502 | ThrowNapiError(Info, ErrorType::ExecutionFailed); 503 | return Napi::Value(); 504 | } 505 | } 506 | 507 | Napi::Value WasmEdgeAddon::RunInt(const Napi::CallbackInfo &Info) { 508 | return RunIntImpl(Info, IntKind::SInt32); 509 | } 510 | 511 | Napi::Value WasmEdgeAddon::RunUInt(const Napi::CallbackInfo &Info) { 512 | return RunIntImpl(Info, IntKind::UInt32); 513 | } 514 | 515 | Napi::Value WasmEdgeAddon::RunInt64(const Napi::CallbackInfo &Info) { 516 | return RunIntImpl(Info, IntKind::SInt64); 517 | } 518 | 519 | Napi::Value WasmEdgeAddon::RunUInt64(const Napi::CallbackInfo &Info) { 520 | return RunIntImpl(Info, IntKind::UInt64); 521 | } 522 | 523 | Napi::Value WasmEdgeAddon::RunString(const Napi::CallbackInfo &Info) { 524 | InitVM(Info); 525 | std::string FuncName = ""; 526 | if (Info.Length() > 0) { 527 | FuncName = Info[0].As().Utf8Value(); 528 | } 529 | 530 | InitWasi(Info, FuncName); 531 | 532 | WasmEdge_Result Res; 533 | std::vector Args; 534 | uint32_t ResultMemAddr = 8; 535 | Args.emplace_back(WasmEdge_ValueGenI32(ResultMemAddr)); 536 | PrepareResource(Info, Args); 537 | WasmEdge_String WasmFuncName = 538 | WasmEdge_StringCreateByCString(FuncName.c_str()); 539 | WasmEdge_Value Ret; 540 | Res = WasmEdge_VMExecute(VM, WasmFuncName, Args.data(), Args.size(), &Ret, 1); 541 | WasmEdge_StringDelete(WasmFuncName); 542 | 543 | if (!WasmEdge_ResultOK(Res)) { 544 | ThrowNapiError(Info, ErrorType::ExecutionFailed); 545 | return Napi::Value(); 546 | } 547 | 548 | uint8_t ResultMem[8]; 549 | Res = WasmEdge_MemoryInstanceGetData(MemInst, ResultMem, ResultMemAddr, 8); 550 | uint32_t ResultDataAddr = 0; 551 | uint32_t ResultDataLen = 0; 552 | if (WasmEdge_ResultOK(Res)) { 553 | ResultDataAddr = ResultMem[0] | (ResultMem[1] << 8) | (ResultMem[2] << 16) | 554 | (ResultMem[3] << 24); 555 | ResultDataLen = ResultMem[4] | (ResultMem[5] << 8) | (ResultMem[6] << 16) | 556 | (ResultMem[7] << 24); 557 | } else { 558 | ThrowNapiError(Info, ErrorType::BadMemoryAccess); 559 | return Napi::Value(); 560 | } 561 | 562 | std::vector ResultData(ResultDataLen); 563 | Res = WasmEdge_MemoryInstanceGetData(MemInst, ResultData.data(), 564 | ResultDataAddr, ResultDataLen); 565 | if (WasmEdge_ResultOK(Res)) { 566 | ReleaseResource(Info, ResultDataAddr, ResultDataLen); 567 | } else { 568 | ThrowNapiError(Info, ErrorType::BadMemoryAccess); 569 | return Napi::Value(); 570 | } 571 | 572 | std::string ResultString(ResultData.begin(), ResultData.end()); 573 | FiniVM(); 574 | return Napi::String::New(Info.Env(), ResultString); 575 | } 576 | 577 | Napi::Value WasmEdgeAddon::RunUint8Array(const Napi::CallbackInfo &Info) { 578 | InitVM(Info); 579 | std::string FuncName = ""; 580 | if (Info.Length() > 0) { 581 | FuncName = Info[0].As().Utf8Value(); 582 | } 583 | 584 | InitWasi(Info, FuncName); 585 | 586 | WasmEdge_Result Res; 587 | std::vector Args; 588 | uint32_t ResultMemAddr = 8; 589 | Args.emplace_back(WasmEdge_ValueGenI32(ResultMemAddr)); 590 | PrepareResource(Info, Args); 591 | WasmEdge_String WasmFuncName = 592 | WasmEdge_StringCreateByCString(FuncName.c_str()); 593 | WasmEdge_Value Ret; 594 | Res = WasmEdge_VMExecute(VM, WasmFuncName, Args.data(), Args.size(), &Ret, 1); 595 | WasmEdge_StringDelete(WasmFuncName); 596 | 597 | if (!WasmEdge_ResultOK(Res)) { 598 | ThrowNapiError(Info, ErrorType::ExecutionFailed); 599 | return Napi::Value(); 600 | } 601 | 602 | uint8_t ResultMem[8]; 603 | Res = WasmEdge_MemoryInstanceGetData(MemInst, ResultMem, ResultMemAddr, 8); 604 | uint32_t ResultDataAddr = 0; 605 | uint32_t ResultDataLen = 0; 606 | if (WasmEdge_ResultOK(Res)) { 607 | ResultDataAddr = ResultMem[0] | (ResultMem[1] << 8) | (ResultMem[2] << 16) | 608 | (ResultMem[3] << 24); 609 | ResultDataLen = ResultMem[4] | (ResultMem[5] << 8) | (ResultMem[6] << 16) | 610 | (ResultMem[7] << 24); 611 | } else { 612 | ThrowNapiError(Info, ErrorType::BadMemoryAccess); 613 | return Napi::Value(); 614 | } 615 | 616 | std::vector ResultData(ResultDataLen); 617 | Res = WasmEdge_MemoryInstanceGetData(MemInst, ResultData.data(), 618 | ResultDataAddr, ResultDataLen); 619 | if (WasmEdge_ResultOK(Res)) { 620 | ReleaseResource(Info, ResultDataAddr, ResultDataLen); 621 | } else { 622 | ThrowNapiError(Info, ErrorType::BadMemoryAccess); 623 | return Napi::Value(); 624 | } 625 | 626 | Napi::ArrayBuffer ResultArrayBuffer = 627 | Napi::ArrayBuffer::New(Info.Env(), &(ResultData[0]), ResultDataLen); 628 | Napi::Uint8Array ResultTypedArray = Napi::Uint8Array::New( 629 | Info.Env(), ResultDataLen, ResultArrayBuffer, 0, napi_uint8_array); 630 | FiniVM(); 631 | return ResultTypedArray; 632 | } 633 | 634 | void WasmEdgeAddon::LoadWasm(const Napi::CallbackInfo &Info) { 635 | Napi::Env Env = Info.Env(); 636 | Napi::HandleScope Scope(Env); 637 | 638 | if (BC.isCompiled()) { 639 | Cache.dumpToFile(BC.getData()); 640 | BC.setPath(Cache.getPath()); 641 | } 642 | 643 | WasmEdge_Result Res; 644 | if (BC.isFile()) { 645 | Res = WasmEdge_VMLoadWasmFromFile(VM, BC.getPath().c_str()); 646 | if (!WasmEdge_ResultOK(Res)) { 647 | ThrowNapiError(Info, ErrorType::LoadWasmFailed); 648 | return; 649 | } 650 | } else if (BC.isValidData()) { 651 | Res = WasmEdge_VMLoadWasmFromBuffer(VM, BC.getData().data(), 652 | BC.getData().size()); 653 | if (!WasmEdge_ResultOK(Res)) { 654 | ThrowNapiError(Info, ErrorType::LoadWasmFailed); 655 | return; 656 | } 657 | } 658 | 659 | Res = WasmEdge_VMValidate(VM); 660 | if (!WasmEdge_ResultOK(Res)) { 661 | ThrowNapiError(Info, ErrorType::ValidateWasmFailed); 662 | return; 663 | } 664 | 665 | Res = WasmEdge_VMInstantiate(VM); 666 | if (!WasmEdge_ResultOK(Res)) { 667 | ThrowNapiError(Info, ErrorType::InstantiateWasmFailed); 668 | return; 669 | } 670 | 671 | // Get memory instance 672 | uint32_t MemLen = WasmEdge_StoreListMemoryLength(Store); 673 | WasmEdge_String MemNames[MemLen]; 674 | WasmEdge_StoreListMemory(Store, MemNames, MemLen); 675 | MemInst = WasmEdge_StoreFindMemory(Store, MemNames[0]); 676 | } 677 | 678 | Napi::Value WasmEdgeAddon::GetStatistics(const Napi::CallbackInfo &Info) { 679 | Napi::Object RetStat = Napi::Object::New(Info.Env()); 680 | if (!Options.isMeasuring()) { 681 | RetStat.Set("Measure", Napi::Boolean::New(Info.Env(), false)); 682 | } else { 683 | RetStat.Set("Measure", Napi::Boolean::New(Info.Env(), true)); 684 | RetStat.Set( 685 | "InstructionCount", 686 | Napi::Number::New(Info.Env(), WasmEdge_StatisticsGetInstrCount(Stat))); 687 | RetStat.Set( 688 | "TotalGasCost", 689 | Napi::Number::New(Info.Env(), WasmEdge_StatisticsGetTotalCost(Stat))); 690 | RetStat.Set("InstructionPerSecond", 691 | Napi::Number::New(Info.Env(), 692 | WasmEdge_StatisticsGetInstrPerSecond(Stat))); 693 | } 694 | 695 | return RetStat; 696 | } 697 | -------------------------------------------------------------------------------- /src/wasmedgeaddon.h: -------------------------------------------------------------------------------- 1 | #ifndef WASMEDGEADDON_H 2 | #define WASMEDGEADDON_H 3 | 4 | #include "bytecode.h" 5 | #include "cache.h" 6 | #include "errors.h" 7 | #include "options.h" 8 | #include "utils.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | class WasmEdgeAddon : public Napi::ObjectWrap { 17 | public: 18 | static Napi::Object Init(Napi::Env Env, Napi::Object Exports); 19 | WasmEdgeAddon(const Napi::CallbackInfo &Info); 20 | ~WasmEdgeAddon() { 21 | if (Configure != nullptr) { 22 | WasmEdge_ConfigureDelete(Configure); 23 | Configure = nullptr; 24 | } 25 | if (VM != nullptr) { 26 | WasmEdge_VMDelete(VM); 27 | VM = nullptr; 28 | } 29 | }; 30 | 31 | enum class IntKind { Default, SInt32, UInt32, SInt64, UInt64 }; 32 | 33 | private: 34 | using ErrorType = WASMEDGE::NAPI::ErrorType; 35 | static Napi::FunctionReference Constructor; 36 | WasmEdge_ConfigureContext *Configure; 37 | WasmEdge_StoreContext *Store; 38 | WasmEdge_VMContext *VM; 39 | WasmEdge_MemoryInstanceContext *MemInst; 40 | WasmEdge_StatisticsContext *Stat; 41 | WasmEdge_ImportObjectContext *WasiMod; 42 | WASMEDGE::NAPI::Bytecode BC; 43 | WASMEDGE::NAPI::Options Options; 44 | WASMEDGE::NAPI::Cache Cache; 45 | bool Inited; 46 | 47 | /// Setup related functions 48 | void InitVM(const Napi::CallbackInfo &Info); 49 | void FiniVM(); 50 | void InitWasi(const Napi::CallbackInfo &Info, const std::string &FuncName); 51 | void LoadWasm(const Napi::CallbackInfo &Info); 52 | /// WasmBindgen related functions 53 | void PrepareResource(const Napi::CallbackInfo &Info, 54 | std::vector &Args, IntKind IntT); 55 | void PrepareResource(const Napi::CallbackInfo &Info, 56 | std::vector &Args); 57 | void ReleaseResource(const Napi::CallbackInfo &Info, const uint32_t Offset, 58 | const uint32_t Size); 59 | /// Run functions 60 | void Run(const Napi::CallbackInfo &Info); 61 | Napi::Value RunStart(const Napi::CallbackInfo &Info); 62 | Napi::Value RunCompile(const Napi::CallbackInfo &Info); 63 | Napi::Value RunIntImpl(const Napi::CallbackInfo &Info, IntKind IntT); 64 | Napi::Value RunInt(const Napi::CallbackInfo &Info); 65 | Napi::Value RunUInt(const Napi::CallbackInfo &Info); 66 | Napi::Value RunInt64(const Napi::CallbackInfo &Info); 67 | Napi::Value RunUInt64(const Napi::CallbackInfo &Info); 68 | Napi::Value RunString(const Napi::CallbackInfo &Info); 69 | Napi::Value RunUint8Array(const Napi::CallbackInfo &Info); 70 | /// Statistics 71 | Napi::Value GetStatistics(const Napi::CallbackInfo &Info); 72 | /// AoT functions 73 | bool Compile(); 74 | bool CompileBytecodeTo(const std::string &Path); 75 | void InitReactor(const Napi::CallbackInfo &Info); 76 | /// Error handling functions 77 | void ThrowNapiError(const Napi::CallbackInfo &Info, ErrorType Type); 78 | }; 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | build 5 | node_modules 6 | *.swp 7 | -------------------------------------------------------------------------------- /test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasmedge-napi-tests" 3 | version = "0.1.0" 4 | authors = ["ubuntu"] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "integers_lib" 9 | path = "rs/integers_lib.rs" 10 | crate-type =["cdylib"] 11 | 12 | [dependencies] 13 | num-integer = "0.1" 14 | wasm-bindgen = "=0.2.61" 15 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | ### Build and Run 2 | 3 | Add `../node_modules/.bin/` to your path. 4 | 5 | ``` 6 | ssvmup build 7 | cd pkg/ 8 | npm install ../.. 9 | cd - 10 | mocha js 11 | ssvmup clean 12 | ``` 13 | -------------------------------------------------------------------------------- /test/data/integers_lib.aot.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/second-state/ssvm-napi/e89e9fb02ad3d73573a9e39e1dba32782d9ab7e3/test/data/integers_lib.aot.so -------------------------------------------------------------------------------- /test/js/test-aot.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const fs = require('fs'); 3 | const ssvm = require('../..'); 4 | 5 | describe('aot', function() { 6 | let inputName = 'pkg/integers_lib_bg.wasm'; 7 | 8 | describe('compile', function() { 9 | this.timeout(0); 10 | 11 | // AOT filename MUST ends with '.so' 12 | let outputName = 'pkg/integers_lib_bg.compile.so'; 13 | 14 | beforeEach(function() { 15 | if (fs.existsSync(outputName)) { 16 | fs.unlinkSync( 17 | outputName); // Alt solution for fs.rmSync (added in v14.14) 18 | } 19 | }); 20 | 21 | let cases = new Map([ 22 | [ 'from filename', (s) => s ], 23 | [ 'from bytearray', (s) => fs.readFileSync(s) ], 24 | ]); 25 | 26 | cases.forEach(function(f, caseName) { 27 | it(caseName, function() { 28 | let vm = new ssvm.VM(f(inputName), { 29 | EnableAOT : true, 30 | args : process.argv, 31 | env : process.env, 32 | preopens : {'/' : __dirname}, 33 | }); 34 | 35 | assert.ok(vm.Compile(outputName)); 36 | }); 37 | }); 38 | 39 | afterEach(function() { 40 | if (fs.existsSync(outputName)) { 41 | fs.unlinkSync( 42 | outputName); // Alt solution for fs.rmSync (added in v14.14) 43 | } 44 | }); 45 | }); 46 | 47 | describe('run', function() { 48 | // AOT filename MUST ends with '.so' 49 | let aotName = 'pkg/integers_lib_bg.run.so'; 50 | 51 | before(function() { 52 | this.timeout(0); 53 | 54 | if (fs.existsSync(aotName)) { 55 | fs.unlinkSync(aotName); // Alt solution for fs.rmSync (added in v14.14) 56 | } 57 | 58 | let vm = new ssvm.VM(inputName, { 59 | EnableAOT : true, 60 | args : process.argv, 61 | env : process.env, 62 | preopens : {'/' : __dirname}, 63 | }); 64 | 65 | assert.ok(vm.Compile(aotName)); 66 | }); 67 | 68 | let cases = new Map([ 69 | [ 'from filename', (s) => s ], 70 | [ 'from bytearray', (s) => fs.readFileSync(s) ], 71 | ]); 72 | 73 | cases.forEach(function(f, caseName) { 74 | it(caseName, function() { 75 | let vm = new ssvm.VM(f(aotName), { 76 | EnableAOT : true, 77 | args : process.argv, 78 | env : process.env, 79 | preopens : {'/' : __dirname}, 80 | }); 81 | 82 | assert.equal(vm.RunInt('lcm_s32', 123, 1011), 41451); 83 | assert.equal(vm.RunUInt('lcm_u32', 2147483647, 2), 4294967294); 84 | assert.equal(vm.RunInt64('lcm_s64', 2147483647, 2), 4294967294); 85 | assert.equal(vm.RunUInt64('lcm_u64', 9223372036854775807, 2), 86 | 18446744073709551614); 87 | }); 88 | }); 89 | 90 | after(function() { 91 | if (fs.existsSync(aotName)) { 92 | fs.unlinkSync(aotName); // Alt solution for fs.rmSync (added in v14.14) 93 | } 94 | }); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /test/js/test-integers.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const {lcm_s32, lcm_u32, lcm_s64, lcm_u64} = require('../pkg/integers_lib.js'); 3 | 4 | describe('s32', function() { 5 | it('lcm', function() { assert.equal(lcm_s32(123, 1011), 41451); }); 6 | it('lcm s32 overflow', 7 | function() { assert.notEqual(lcm_s32(2147483647, 2), 4294967294); }); 8 | }); 9 | 10 | describe('u32', function() { 11 | it('lcm', function() { assert.equal(lcm_u32(123, 1011), 41451); }); 12 | it('lcm s32 overflow', 13 | function() { assert.equal(lcm_u32(2147483647, 2), 4294967294); }); 14 | it('lcm u32 overflow', 15 | function() { assert.notEqual(lcm_u32(4294967295, 2), 8589934590); }); 16 | }); 17 | 18 | describe('s64', function() { 19 | it('lcm', function() { assert.equal(lcm_s64(123, 1011), 41451); }); 20 | it('lcm s64 overflow', function() { 21 | assert.notEqual(lcm_s64(9223372036854775807, 2), 18446744073709551614); 22 | }); 23 | }); 24 | 25 | describe('u64', function() { 26 | it('lcm', function() { assert.equal(lcm_u64(123, 1011), 41451); }); 27 | it('lcm s64 overflow', function() { 28 | assert.equal(lcm_u64(9223372036854775807, 2), 18446744073709551614); 29 | }); 30 | it('lcm u64 overflow', function() { 31 | assert.notEqual(lcm_u64(18446744073709551615, 2), 36893488147419103230); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/rs/integers_lib.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | use num_integer::lcm; 3 | 4 | #[wasm_bindgen] 5 | pub fn lcm_s32(a: i32, b: i32) -> i32 { 6 | let r = lcm(a, b); 7 | return r; 8 | } 9 | 10 | #[wasm_bindgen] 11 | pub fn lcm_u32(a: u32, b: u32) -> u32 { 12 | let r = lcm(a, b); 13 | return r; 14 | } 15 | 16 | #[wasm_bindgen] 17 | pub fn lcm_s64(a: i64, b: i64) -> i64 { 18 | let r = lcm(a, b); 19 | return r; 20 | } 21 | 22 | #[wasm_bindgen] 23 | pub fn lcm_u64(a: u64, b: u64) -> u64 { 24 | let r = lcm(a, b); 25 | return r; 26 | } 27 | -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -xe 3 | rustwasmc build 4 | cd pkg 5 | npm install ../.. 6 | sed -i "s/require('ssvm')/require('wasmedge-core')/" integers_lib.js 7 | cd - 8 | mocha js 9 | rustwasmc clean 10 | --------------------------------------------------------------------------------