├── .nvmrc ├── .watchmanconfig ├── example ├── .node-version ├── .watchmanconfig ├── .ruby-version ├── src │ ├── utils │ │ ├── index.ts │ │ └── fetchWasm.ts │ ├── sources │ │ ├── Local.Callback.wasm │ │ ├── Local.Hello.wasm │ │ ├── Local.SimpleMemory.wasm │ │ ├── Local.Callback.wat │ │ └── Local.SimpleMemory.wat │ ├── hooks │ │ ├── index.ts │ │ ├── useWasmHelloWorld.ts │ │ ├── useWasmUri.ts │ │ └── useWasmCircomRuntime.ts │ └── App.tsx ├── .bundle │ └── config ├── app.json ├── index.d.ts ├── ios │ ├── File.swift │ ├── WebassemblyExample │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── AppDelegate.mm │ │ ├── Info.plist │ │ └── LaunchScreen.storyboard │ ├── WebassemblyExample-Bridging-Header.h │ ├── WebassemblyExample.xcworkspace │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── contents.xcworkspacedata │ ├── .xcode.env │ ├── WebassemblyExampleTests │ │ ├── Info.plist │ │ └── WebassemblyExampleTests.m │ ├── Podfile │ └── WebassemblyExample.xcodeproj │ │ └── xcshareddata │ │ └── xcschemes │ │ └── WebassemblyExample.xcscheme ├── android │ ├── app │ │ ├── debug.keystore │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── values │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ └── styles.xml │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ └── drawable │ │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── webassemblyexample │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ ├── debug │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── webassemblyexample │ │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── release │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── webassemblyexample │ │ │ │ └── ReactNativeFlipper.java │ │ ├── proguard-rules.pro │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── build.gradle │ ├── gradle.properties │ ├── gradlew.bat │ └── gradlew ├── index.js ├── README.md ├── Gemfile ├── react-native.config.js ├── babel.config.js ├── package.json └── metro.config.js ├── .npmignore ├── src ├── __tests__ │ └── index.test.tsx └── index.tsx ├── .gitattributes ├── tsconfig.build.json ├── babel.config.js ├── .yarnrc ├── android ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── webassembly │ │ ├── WebassemblyPackage.java │ │ └── WebassemblyModule.java ├── gradle.properties ├── cpp-adapter.cpp ├── CMakeLists.txt └── build.gradle ├── cpp ├── m3_exec.c ├── react-native-webassembly.h ├── m3_api_tracer.h ├── m3_api_libc.h ├── m3_bind.h ├── m3_api_wasi.h ├── m3_info.h ├── m3_exception.h ├── m3_exec_defs.h ├── m3_code.h ├── m3_function.h ├── m3_module.c ├── m3_config.h ├── m3_bind.c ├── m3_function.c ├── m3_config_platforms.h ├── m3_compile.h ├── m3_code.c ├── m3_api_tracer.c ├── m3_api_libc.c ├── m3_env.h └── wasm3_defs.h ├── ios ├── Webassembly.h └── Webassembly.mm ├── .editorconfig ├── lefthook.yml ├── app.plugin.js ├── .github ├── actions │ └── setup │ │ └── action.yml └── workflows │ └── ci.yml ├── tsconfig.json ├── scripts └── bootstrap.js ├── .gitignore ├── LICENSE ├── react-native-webassembly.podspec ├── package.json ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 16.18.1 2 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/.node-version: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.6 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.env 2 | .idea/ 3 | wasm3/ 4 | 5 | -------------------------------------------------------------------------------- /src/__tests__/index.test.tsx: -------------------------------------------------------------------------------- 1 | it.todo('write a test'); 2 | -------------------------------------------------------------------------------- /example/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './fetchWasm'; 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf -------------------------------------------------------------------------------- /example/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "extends": "./tsconfig", 4 | "exclude": ["example"] 5 | } 6 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WebassemblyExample", 3 | "displayName": "WebassemblyExample" 4 | } -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.wasm' { 2 | const value: any; 3 | export default value; 4 | } 5 | -------------------------------------------------------------------------------- /example/ios/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // WebassemblyExample 4 | // 5 | 6 | import Foundation 7 | -------------------------------------------------------------------------------- /example/src/sources/Local.Callback.wasm: -------------------------------------------------------------------------------- 1 | asm`runtimecallbackcallBackFunction 2 |   -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # Override Yarn command so we can automatically setup the repo on running `yarn` 2 | 3 | yarn-path "scripts/bootstrap.js" 4 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cawfree/react-native-webassembly/HEAD/example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/src/sources/Local.Hello.wasm: -------------------------------------------------------------------------------- 1 | asm 2 | ``pA memorytableadd A  3 |  j  -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | WebassemblyExample 3 | 4 | -------------------------------------------------------------------------------- /example/ios/WebassemblyExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useWasmCircomRuntime'; 2 | export * from './useWasmHelloWorld'; 3 | export * from './useWasmUri'; 4 | -------------------------------------------------------------------------------- /example/src/sources/Local.SimpleMemory.wasm: -------------------------------------------------------------------------------- 1 | asm ``9memorywrite_byte_to_memoryread_byte_from_memory 2 |  A : A- -------------------------------------------------------------------------------- /example/ios/WebassemblyExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cawfree/react-native-webassembly/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/ios/WebassemblyExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : RCTAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /cpp/m3_exec.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_exec.c 3 | // 4 | // Created by Steven Massey on 4/17/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | // EMPTY FOR NOW 9 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cawfree/react-native-webassembly/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cawfree/react-native-webassembly/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cawfree/react-native-webassembly/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cawfree/react-native-webassembly/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cawfree/react-native-webassembly/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cawfree/react-native-webassembly/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cawfree/react-native-webassembly/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cawfree/react-native-webassembly/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | Webassembly_kotlinVersion=1.7.0 2 | Webassembly_minSdkVersion=21 3 | Webassembly_targetSdkVersion=31 4 | Webassembly_compileSdkVersion=31 5 | Webassembly_ndkversion=21.4.7075529 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cawfree/react-native-webassembly/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cawfree/react-native-webassembly/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import App from './src/App'; 3 | import { name as appName } from './app.json'; 4 | 5 | AppRegistry.registerComponent(appName, () => App); 6 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | ### How to compile a `.wat` file: 2 | 3 | 1. Download and configure [__WebAssembly Binary Toolkit__](https://github.com/WebAssembly/wabt). 4 | 2. Then you can use something like: `wat2wasm test.wat -o test.wasm`. 5 | -------------------------------------------------------------------------------- /ios/Webassembly.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "react-native-webassembly.h" 4 | 5 | @interface Webassembly : NSObject 6 | @property (nonatomic, assign) BOOL setBridgeOnMainQueue; 7 | @end 8 | -------------------------------------------------------------------------------- /example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 4 | ruby File.read(File.join(__dir__, '.ruby-version')).strip 5 | 6 | gem 'cocoapods', '~> 1.11', '>= 1.11.3' 7 | -------------------------------------------------------------------------------- /example/react-native.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pak = require('../package.json'); 3 | 4 | module.exports = { 5 | dependencies: { 6 | [pak.name]: { 7 | root: path.join(__dirname, '..'), 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /example/ios/WebassemblyExample/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'WebassemblyExample' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | includeBuild('../node_modules/react-native-gradle-plugin') 5 | -------------------------------------------------------------------------------- /example/src/utils/fetchWasm.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export async function fetchWasm(uri: string) { 4 | const { data: bufferSource } = await axios({ 5 | url: uri, 6 | method: 'get', 7 | responseType: 'arraybuffer', 8 | }); 9 | 10 | return bufferSource; 11 | } 12 | -------------------------------------------------------------------------------- /cpp/react-native-webassembly.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBASSEMBLY_H 2 | #define WEBASSEMBLY_H 3 | 4 | namespace facebook { 5 | namespace jsi { 6 | class Runtime; 7 | } 8 | } 9 | 10 | namespace webassembly { 11 | void install(facebook::jsi::Runtime &jsiRuntime); 12 | } 13 | 14 | #endif /* WEBASSEMBLY_H */ 15 | -------------------------------------------------------------------------------- /example/ios/WebassemblyExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/src/hooks/useWasmHelloWorld.ts: -------------------------------------------------------------------------------- 1 | import { useWasmUri } from './useWasmUri'; 2 | 3 | export const useWasmHelloWorld = () => 4 | useWasmUri<{ 5 | readonly add: (a: number, b: number) => number; 6 | }>( 7 | 'https://github.com/torch2424/wasm-by-example/raw/master/examples/hello-world/demo/assemblyscript/hello-world.wasm' 8 | ); 9 | -------------------------------------------------------------------------------- /example/ios/WebassemblyExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/src/sources/Local.Callback.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type $callback_type (func (param i32) (result i32))) 3 | (import "runtime" "callback" (func $callback (type $callback_type))) 4 | 5 | (func $callBackFunction (param $param i32) (result i32) 6 | (call $callback (local.get $param)) 7 | ) 8 | 9 | (export "callBackFunction" (func $callBackFunction)) 10 | ) 11 | -------------------------------------------------------------------------------- /cpp/m3_api_tracer.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_api_tracer.h 3 | // 4 | // Created by Volodymyr Shymanskyy on 02/18/20. 5 | // Copyright © 2020 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_api_tracer_h 9 | #define m3_api_tracer_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | M3Result m3_LinkTracer (IM3Module io_module); 16 | 17 | d_m3EndExternC 18 | 19 | #endif // m3_api_tracer_h 20 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | parallel: true 3 | commands: 4 | lint: 5 | files: git diff --name-only @{push} 6 | glob: "*.{js,ts,jsx,tsx}" 7 | run: npx eslint {files} 8 | types: 9 | files: git diff --name-only @{push} 10 | glob: "*.{js,ts, jsx, tsx}" 11 | run: npx tsc --noEmit 12 | commit-msg: 13 | parallel: true 14 | commands: 15 | commitlint: 16 | run: npx commitlint --edit 17 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pak = require('../package.json'); 3 | 4 | module.exports = { 5 | presets: ['module:metro-react-native-babel-preset'], 6 | plugins: [ 7 | [ 8 | 'module-resolver', 9 | { 10 | extensions: ['.tsx', '.ts', '.js', '.json'], 11 | alias: { 12 | [pak.name]: path.join(__dirname, '..', pak.source), 13 | }, 14 | }, 15 | ], 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /cpp/m3_api_libc.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_api_libc.h 3 | // 4 | // Created by Volodymyr Shymanskyy on 11/20/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_api_libc_h 9 | #define m3_api_libc_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | M3Result m3_LinkLibC (IM3Module io_module); 16 | M3Result m3_LinkSpecTest (IM3Module io_module); 17 | 18 | d_m3EndExternC 19 | 20 | #endif // m3_api_libc_h 21 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /android/cpp-adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "react-native-webassembly.h" 6 | 7 | 8 | using namespace facebook::jsi; 9 | using namespace std; 10 | 11 | extern "C" 12 | JNIEXPORT void JNICALL 13 | Java_com_webassembly_WebassemblyModule_nativeInstall(JNIEnv *env, jobject thiz, jlong jsi) { 14 | 15 | auto runtime = reinterpret_cast(jsi); 16 | 17 | if (runtime) { 18 | webassembly::install(*runtime); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /cpp/m3_bind.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_bind.h 3 | // 4 | // Created by Steven Massey on 2/27/20. 5 | // Copyright © 2020 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_bind_h 9 | #define m3_bind_h 10 | 11 | #include "m3_env.h" 12 | 13 | d_m3BeginExternC 14 | 15 | u8 ConvertTypeCharToTypeId (char i_code); 16 | char ConvertTypeIdToTypeChar (u8 type); 17 | M3Result SignatureToFuncType (IM3FuncType * o_functionType, ccstr_t i_signature); 18 | 19 | d_m3EndExternC 20 | 21 | #endif /* m3_bind_h */ 22 | -------------------------------------------------------------------------------- /example/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /app.plugin.js: -------------------------------------------------------------------------------- 1 | const { 2 | withPlugins, 3 | AndroidConfig, 4 | createRunOncePlugin, 5 | } = require('@expo/config-plugins'); 6 | 7 | const { name, version } = require('./package.json'); 8 | 9 | const withWebAssembly = (config) => { 10 | if (!config.ios) config.ios = {}; 11 | if (!config.ios.infoPlist) config.ios.infoPlist = {}; 12 | 13 | const androidPermissions = []; 14 | 15 | return withPlugins(config, [ 16 | [AndroidConfig.Permissions.withPermissions, androidPermissions], 17 | ]); 18 | }; 19 | 20 | module.exports = createRunOncePlugin(withWebAssembly, name, version); 21 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/src/sources/Local.SimpleMemory.wat: -------------------------------------------------------------------------------- 1 | (module 2 | ;; Define a memory block with one page (64KB) of memory 3 | (memory $my_memory 1) 4 | 5 | ;; Export the memory block so that it can be accessed from JavaScript 6 | (export "memory" (memory $my_memory)) 7 | 8 | ;; Define a function that writes a byte to the first byte of memory 9 | (func (export "write_byte_to_memory") (param $value i32) 10 | (i32.store8 (i32.const 0) (local.get $value)) 11 | ) 12 | 13 | ;; Define a function that reads a byte from the first byte of memory 14 | (func (export "read_byte_from_memory") (result i32) 15 | (i32.load8_u (i32.const 0)) 16 | ) 17 | ) 18 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WebassemblyExample", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "pods": "pod-install --quiet" 10 | }, 11 | "dependencies": { 12 | "axios": "1.3.4", 13 | "react": "18.2.0", 14 | "react-native": "0.71.4" 15 | }, 16 | "devDependencies": { 17 | "@babel/core": "^7.20.0", 18 | "@babel/preset-env": "^7.20.0", 19 | "@babel/runtime": "^7.20.0", 20 | "babel-plugin-module-resolver": "^4.1.0", 21 | "metro-react-native-babel-preset": "0.73.8" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "33.0.0" 6 | minSdkVersion = 21 7 | compileSdkVersion = 33 8 | targetSdkVersion = 33 9 | 10 | // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. 11 | ndkVersion = "23.1.7779620" 12 | } 13 | repositories { 14 | google() 15 | mavenCentral() 16 | } 17 | dependencies { 18 | classpath("com.android.tools.build:gradle:7.3.1") 19 | classpath("com.facebook.react:react-native-gradle-plugin") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Setup Node.js and install dependencies 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Setup Node.js 8 | uses: actions/setup-node@v3 9 | with: 10 | node-version-file: .nvmrc 11 | 12 | - name: Cache dependencies 13 | id: yarn-cache 14 | uses: actions/cache@v3 15 | with: 16 | path: | 17 | **/node_modules 18 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 19 | restore-keys: | 20 | ${{ runner.os }}-yarn- 21 | 22 | - name: Install dependencies 23 | if: steps.yarn-cache.outputs.cache-hit != 'true' 24 | run: | 25 | yarn install --cwd example --frozen-lockfile 26 | yarn install --frozen-lockfile 27 | shell: bash 28 | -------------------------------------------------------------------------------- /example/android/app/src/release/java/com/webassemblyexample/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.webassemblyexample; 8 | 9 | import android.content.Context; 10 | import com.facebook.react.ReactInstanceManager; 11 | 12 | /** 13 | * Class responsible of loading Flipper inside your React Native application. This is the release 14 | * flavor of it so it's empty as we don't want to load Flipper. 15 | */ 16 | public class ReactNativeFlipper { 17 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 18 | // Do nothing as we don't want to initialize Flipper on Release. 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "react-native-webassembly": ["./src/index"] 6 | }, 7 | "allowUnreachableCode": false, 8 | "allowUnusedLabels": false, 9 | "esModuleInterop": true, 10 | "importsNotUsedAsValues": "error", 11 | "forceConsistentCasingInFileNames": true, 12 | "jsx": "react", 13 | "lib": ["esnext"], 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "noFallthroughCasesInSwitch": true, 17 | "noImplicitReturns": true, 18 | "noImplicitUseStrict": false, 19 | "noStrictGenericChecks": false, 20 | "noUncheckedIndexedAccess": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": false, 23 | "resolveJsonModule": true, 24 | "skipLibCheck": true, 25 | "strict": true, 26 | "target": "esnext" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example/ios/WebassemblyExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /android/src/main/java/com/webassembly/WebassemblyPackage.java: -------------------------------------------------------------------------------- 1 | package com.webassembly; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.uimanager.ViewManager; 9 | 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | public class WebassemblyPackage implements ReactPackage { 14 | @NonNull 15 | @Override 16 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 17 | return Collections.singletonList(new WebassemblyModule(reactContext)); 18 | } 19 | 20 | @NonNull 21 | @Override 22 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 23 | return Collections.emptyList(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cpp/m3_api_wasi.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_api_wasi.h 3 | // 4 | // Created by Volodymyr Shymanskyy on 11/20/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_api_wasi_h 9 | #define m3_api_wasi_h 10 | 11 | #include "m3_core.h" 12 | 13 | #if defined(d_m3HasUVWASI) 14 | #include "uvwasi.h" 15 | #endif 16 | 17 | d_m3BeginExternC 18 | 19 | typedef struct m3_wasi_context_t 20 | { 21 | i32 exit_code; 22 | u32 argc; 23 | ccstr_t * argv; 24 | } m3_wasi_context_t; 25 | 26 | M3Result m3_LinkWASI (IM3Module io_module); 27 | 28 | #if defined(d_m3HasUVWASI) 29 | 30 | M3Result m3_LinkWASIWithOptions (IM3Module io_module, uvwasi_options_t uvwasiOptions); 31 | 32 | #endif 33 | 34 | m3_wasi_context_t* m3_GetWasiContext(); 35 | 36 | d_m3EndExternC 37 | 38 | #endif // m3_api_wasi_h 39 | -------------------------------------------------------------------------------- /scripts/bootstrap.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | const path = require('path'); 3 | const child_process = require('child_process'); 4 | 5 | const root = path.resolve(__dirname, '..'); 6 | const args = process.argv.slice(2); 7 | const options = { 8 | cwd: process.cwd(), 9 | env: process.env, 10 | stdio: 'inherit', 11 | encoding: 'utf-8', 12 | }; 13 | 14 | if (os.type() === 'Windows_NT') { 15 | options.shell = true; 16 | } 17 | 18 | let result; 19 | 20 | if (process.cwd() !== root || args.length) { 21 | // We're not in the root of the project, or additional arguments were passed 22 | // In this case, forward the command to `yarn` 23 | result = child_process.spawnSync('yarn', args, options); 24 | } else { 25 | // If `yarn` is run without arguments, perform bootstrap 26 | result = child_process.spawnSync('yarn', ['bootstrap'], options); 27 | } 28 | 29 | process.exitCode = result.status; 30 | -------------------------------------------------------------------------------- /ios/Webassembly.mm: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | #import "Webassembly.h" 7 | #import "react-native-webassembly.h" 8 | 9 | using namespace facebook::jsi; 10 | using namespace std; 11 | 12 | @implementation Webassembly 13 | 14 | @synthesize bridge = _bridge; 15 | @synthesize methodQueue = _methodQueue; 16 | 17 | RCT_EXPORT_MODULE() 18 | 19 | + (BOOL)requiresMainQueueSetup { 20 | return YES; 21 | } 22 | 23 | RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install) 24 | { 25 | RCTBridge* bridge = [RCTBridge currentBridge]; 26 | RCTCxxBridge* cxxBridge = (RCTCxxBridge*)bridge; 27 | 28 | if (cxxBridge == nil) return @false; 29 | 30 | auto jsiRuntime = (facebook::jsi::Runtime*) cxxBridge.runtime; 31 | 32 | if (jsiRuntime == nil) return @false; 33 | 34 | webassembly::install(*(facebook::jsi::Runtime *)jsiRuntime); 35 | 36 | return @true; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | 32 | # Android/IJ 33 | # 34 | .classpath 35 | .cxx 36 | .gradle 37 | .idea 38 | .project 39 | .settings 40 | local.properties 41 | android.iml 42 | 43 | # Cocoapods 44 | # 45 | example/ios/Pods 46 | 47 | # Ruby 48 | example/vendor/ 49 | 50 | # node.js 51 | # 52 | node_modules/ 53 | npm-debug.log 54 | yarn-debug.log 55 | yarn-error.log 56 | 57 | # BUCK 58 | buck-out/ 59 | \.buckd/ 60 | android/app/libs 61 | android/keystores/debug.keystore 62 | 63 | # Expo 64 | .expo/ 65 | 66 | # Turborepo 67 | .turbo/ 68 | 69 | # generated by bob 70 | lib/ 71 | 72 | *.env 73 | .idea/ 74 | wasm3/ 75 | 76 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | 17 | - name: Setup 18 | uses: ./.github/actions/setup 19 | 20 | - name: Lint files 21 | run: yarn lint 22 | 23 | - name: Typecheck files 24 | run: yarn typecheck 25 | 26 | test: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v3 31 | 32 | - name: Setup 33 | uses: ./.github/actions/setup 34 | 35 | - name: Run unit tests 36 | run: yarn test --maxWorkers=2 --coverage 37 | 38 | build: 39 | runs-on: ubuntu-latest 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@v3 43 | 44 | - name: Setup 45 | uses: ./.github/actions/setup 46 | 47 | - name: Build package 48 | run: yarn prepack 49 | -------------------------------------------------------------------------------- /cpp/m3_info.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_info.h 3 | // 4 | // Created by Steven Massey on 12/6/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_info_h 9 | #define m3_info_h 10 | 11 | #include "m3_compile.h" 12 | 13 | d_m3BeginExternC 14 | 15 | void ProfileHit (cstr_t i_operationName); 16 | 17 | #ifdef DEBUG 18 | 19 | void dump_type_stack (IM3Compilation o); 20 | void log_opcode (IM3Compilation o, m3opcode_t i_opcode); 21 | const char * get_indention_string (IM3Compilation o); 22 | void log_emit (IM3Compilation o, IM3Operation i_operation); 23 | 24 | cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType); 25 | 26 | #else // DEBUG 27 | 28 | #define dump_type_stack(...) {} 29 | #define log_opcode(...) {} 30 | #define get_indention_string(...) "" 31 | #define emit_stack_dump(...) {} 32 | #define log_emit(...) {} 33 | 34 | #endif // DEBUG 35 | 36 | d_m3EndExternC 37 | 38 | #endif // m3_info_h 39 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 cawfree 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /cpp/m3_exception.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_exception.h 3 | // 4 | // Created by Steven Massey on 7/5/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | // some macros to emulate try/catch 8 | 9 | #ifndef m3_exception_h 10 | #define m3_exception_h 11 | 12 | #include "m3_config.h" 13 | 14 | # if d_m3EnableExceptionBreakpoint 15 | 16 | // declared in m3_info.c 17 | void ExceptionBreakpoint (cstr_t i_exception, cstr_t i_message); 18 | 19 | # define EXCEPTION_PRINT(ERROR) ExceptionBreakpoint (ERROR, (__FILE__ ":" M3_STR(__LINE__))) 20 | 21 | # else 22 | # define EXCEPTION_PRINT(...) 23 | # endif 24 | 25 | 26 | #define _try M3Result result = m3Err_none; 27 | #define _(TRY) { result = TRY; if (M3_UNLIKELY(result)) { EXCEPTION_PRINT (result); goto _catch; } } 28 | #define _throw(ERROR) { result = ERROR; EXCEPTION_PRINT (result); goto _catch; } 29 | #define _throwif(ERROR, COND) if (M3_UNLIKELY(COND)) { _throw(ERROR); } 30 | 31 | #define _throwifnull(PTR) _throwif (m3Err_mallocFailed, !(PTR)) 32 | 33 | #endif // m3_exception_h 34 | -------------------------------------------------------------------------------- /example/ios/WebassemblyExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /android/src/main/java/com/webassembly/WebassemblyModule.java: -------------------------------------------------------------------------------- 1 | package com.webassembly; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.bridge.ReactMethod; 8 | import com.facebook.react.module.annotations.ReactModule; 9 | 10 | @ReactModule(name = WebassemblyModule.NAME) 11 | public class WebassemblyModule extends ReactContextBaseJavaModule { 12 | public static final String NAME = "Webassembly"; 13 | 14 | private native void nativeInstall(long jsiPtr, String docDir); 15 | 16 | public WebassemblyModule(ReactApplicationContext reactContext) { 17 | super(reactContext); 18 | } 19 | 20 | @Override 21 | @NonNull 22 | public String getName() { 23 | return NAME; 24 | } 25 | 26 | @ReactMethod(isBlockingSynchronousMethod = true) 27 | public boolean install() { 28 | try { 29 | System.loadLibrary("cpp"); 30 | 31 | ReactApplicationContext context = getReactApplicationContext(); 32 | nativeInstall( 33 | context.getJavaScriptContextHolder().get(), 34 | context.getFilesDir().getAbsolutePath() 35 | ); 36 | return true; 37 | } catch (Exception exception) { 38 | return false; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /example/ios/WebassemblyExample/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | 5 | @implementation AppDelegate 6 | 7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 8 | { 9 | self.moduleName = @"WebassemblyExample"; 10 | // You can add your custom initial props in the dictionary below. 11 | // They will be passed down to the ViewController used by React Native. 12 | self.initialProps = @{}; 13 | 14 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 15 | } 16 | 17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 18 | { 19 | #if DEBUG 20 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 21 | #else 22 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 23 | #endif 24 | } 25 | 26 | /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. 27 | /// 28 | /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html 29 | /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). 30 | /// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`. 31 | - (BOOL)concurrentRootEnabled 32 | { 33 | return true; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const escape = require('escape-string-regexp'); 3 | const exclusionList = require('metro-config/src/defaults/exclusionList'); 4 | const metroDefault = require('metro-config/src/defaults/defaults'); 5 | const pak = require('../package.json'); 6 | 7 | const root = path.resolve(__dirname, '..'); 8 | 9 | const modules = Object.keys({ 10 | ...pak.peerDependencies, 11 | }); 12 | 13 | module.exports = { 14 | projectRoot: __dirname, 15 | watchFolders: [root], 16 | 17 | // We need to make sure that only one version is loaded for peerDependencies 18 | // So we block them at the root, and alias them to the versions in example's node_modules 19 | resolver: { 20 | blacklistRE: exclusionList( 21 | modules.map( 22 | (m) => 23 | new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) 24 | ) 25 | ), 26 | 27 | extraNodeModules: modules.reduce((acc, name) => { 28 | acc[name] = path.join(__dirname, 'node_modules', name); 29 | return acc; 30 | }, {}), 31 | 32 | assetExts: metroDefault.assetExts.concat(['wasm']), 33 | }, 34 | 35 | transformer: { 36 | getTransformOptions: async () => ({ 37 | transform: { 38 | experimentalImportSupport: false, 39 | inlineRequires: true, 40 | }, 41 | }), 42 | }, 43 | }; 44 | -------------------------------------------------------------------------------- /react-native-webassembly.podspec: -------------------------------------------------------------------------------- 1 | require "json" 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, "package.json"))) 4 | folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' 5 | 6 | Pod::Spec.new do |s| 7 | s.name = "react-native-webassembly" 8 | s.version = package["version"] 9 | s.summary = package["description"] 10 | s.homepage = package["homepage"] 11 | s.license = package["license"] 12 | s.authors = package["author"] 13 | 14 | s.platforms = { :ios => "11.0" } 15 | s.source = { :git => "https://github.com/cawfree/react-native-webassembly.git", :tag => "#{s.version}" } 16 | 17 | s.source_files = "ios/**/*.{h,m,mm}", "cpp/**/*.{h,cpp,c}" 18 | 19 | s.dependency "React-Core" 20 | 21 | s.pod_target_xcconfig = { 22 | "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", 23 | "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", 24 | "CLANG_CXX_LANGUAGE_STANDARD" => "c++17", 25 | "CLANG_CXX_LIBRARY" => "libc++" 26 | } 27 | s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" 28 | s.dependency "React-Codegen" 29 | s.dependency "RCT-Folly" 30 | s.dependency "RCTRequired" 31 | s.dependency "RCTTypeSafety" 32 | s.dependency "ReactCommon/turbomodule/core" 33 | 34 | end 35 | -------------------------------------------------------------------------------- /example/src/hooks/useWasmUri.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as WebAssembly from 'react-native-webassembly'; 3 | import type { WebassemblyInstantiateResult } from 'react-native-webassembly'; 4 | 5 | import { fetchWasm } from '../utils'; 6 | 7 | type State = Readonly< 8 | | { loading: true } 9 | | { loading: false; result: WebassemblyInstantiateResult } 10 | | { loading: false; error: Error } 11 | >; 12 | 13 | export function useWasmUri( 14 | uri: string, 15 | importObject: WebAssembly.WebAssemblyImportObject | undefined = undefined 16 | ): State { 17 | const [state, setState] = React.useState>({ 18 | loading: true, 19 | }); 20 | 21 | React.useEffect( 22 | () => 23 | void (async () => { 24 | try { 25 | setState({ 26 | loading: false, 27 | result: await WebAssembly.instantiate( 28 | await fetchWasm(uri), 29 | importObject 30 | ), 31 | }); 32 | } catch (cause) { 33 | setState({ 34 | loading: false, 35 | error: new Error( 36 | `Failed to instantiate "${uri}".`, 37 | cause as ErrorOptions 38 | ), 39 | }); 40 | } 41 | })(), 42 | [uri, importObject] 43 | ); 44 | 45 | return state; 46 | } 47 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/webassemblyexample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.webassemblyexample; 2 | 3 | import com.facebook.react.ReactActivity; 4 | import com.facebook.react.ReactActivityDelegate; 5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; 6 | import com.facebook.react.defaults.DefaultReactActivityDelegate; 7 | 8 | public class MainActivity extends ReactActivity { 9 | 10 | /** 11 | * Returns the name of the main component registered from JavaScript. This is used to schedule 12 | * rendering of the component. 13 | */ 14 | @Override 15 | protected String getMainComponentName() { 16 | return "WebassemblyExample"; 17 | } 18 | 19 | /** 20 | * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link 21 | * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React 22 | * (aka React 18) with two boolean flags. 23 | */ 24 | @Override 25 | protected ReactActivityDelegate createReactActivityDelegate() { 26 | return new DefaultReactActivityDelegate( 27 | this, 28 | getMainComponentName(), 29 | // If you opted-in for the New Architecture, we enable the Fabric Renderer. 30 | DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled 31 | // If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18). 32 | DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /example/ios/WebassemblyExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | WebassemblyExample 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UIViewControllerBasedStatusBarAppearance 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.125.0 29 | 30 | # Use this property to specify which architecture you want to build. 31 | # You can also override it from the CLI using 32 | # ./gradlew -PreactNativeArchitectures=x86_64 33 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 34 | 35 | # Use this property to enable support to the new architecture. 36 | # This will allow you to use TurboModules and the Fabric render in 37 | # your application. You should enable this flag either if you want 38 | # to write custom TurboModules/Fabric components OR use libraries that 39 | # are providing them. 40 | newArchEnabled=true 41 | 42 | # Use this property to enable or disable the Hermes JS engine. 43 | # If set to false, you will be using JSC instead. 44 | hermesEnabled=true 45 | -------------------------------------------------------------------------------- /android/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9.0) 2 | 3 | set (BUILD_DIR ${CMAKE_SOURCE_DIR}/build) 4 | set(CMAKE_VERBOSE_MAKEFILE ON) 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | find_library(log-lib log) 8 | 9 | add_library(cpp 10 | SHARED 11 | ../cpp/react-native-webassembly.cpp 12 | cpp-adapter.cpp 13 | # wasm3 14 | ../cpp/m3_api_libc.c 15 | ../cpp/m3_api_libc.h 16 | ../cpp/m3_api_meta_wasi.c 17 | ../cpp/m3_api_tracer.c 18 | ../cpp/m3_api_tracer.h 19 | ../cpp/m3_api_uvwasi.c 20 | ../cpp/m3_api_wasi.c 21 | ../cpp/m3_api_wasi.h 22 | ../cpp/m3_bind.c 23 | ../cpp/m3_bind.h 24 | ../cpp/m3_code.c 25 | ../cpp/m3_code.h 26 | ../cpp/m3_compile.c 27 | ../cpp/m3_compile.h 28 | ../cpp/m3_config.h 29 | ../cpp/m3_config_platforms.h 30 | ../cpp/m3_core.c 31 | ../cpp/m3_core.h 32 | ../cpp/m3_env.c 33 | ../cpp/m3_env.h 34 | ../cpp/m3_exception.h 35 | ../cpp/m3_exec.c 36 | ../cpp/m3_exec.h 37 | ../cpp/m3_exec_defs.h 38 | ../cpp/m3_function.c 39 | ../cpp/m3_function.h 40 | ../cpp/m3_info.c 41 | ../cpp/m3_info.h 42 | ../cpp/m3_math_utils.h 43 | ../cpp/m3_module.c 44 | ../cpp/m3_parse.c 45 | ../cpp/react-native-webassembly.h 46 | ../cpp/wasm3.h 47 | ../cpp/wasm3_cpp.h 48 | ../cpp/wasm3_defs.h 49 | ) 50 | 51 | # Specifies a path to native header files. 52 | include_directories( 53 | ../cpp 54 | ) 55 | 56 | set_target_properties( 57 | cpp PROPERTIES 58 | CXX_STANDARD 17 59 | CXX_EXTENSIONS OFF 60 | POSITION_INDEPENDENT_CODE ON 61 | ) 62 | 63 | find_package(ReactAndroid REQUIRED CONFIG) 64 | 65 | target_link_libraries( 66 | cpp 67 | ${log-lib} 68 | ReactAndroid::jsi 69 | android 70 | ) 71 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/webassemblyexample/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.webassemblyexample; 2 | 3 | import android.app.Application; 4 | import com.facebook.react.PackageList; 5 | import com.facebook.react.ReactApplication; 6 | import com.facebook.react.ReactNativeHost; 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; 9 | import com.facebook.react.defaults.DefaultReactNativeHost; 10 | import com.facebook.soloader.SoLoader; 11 | import java.util.List; 12 | 13 | public class MainApplication extends Application implements ReactApplication { 14 | 15 | private final ReactNativeHost mReactNativeHost = 16 | new DefaultReactNativeHost(this) { 17 | @Override 18 | public boolean getUseDeveloperSupport() { 19 | return BuildConfig.DEBUG; 20 | } 21 | 22 | @Override 23 | protected List getPackages() { 24 | @SuppressWarnings("UnnecessaryLocalVariable") 25 | List packages = new PackageList(this).getPackages(); 26 | // Packages that cannot be autolinked yet can be added manually here, for example: 27 | // packages.add(new MyReactNativePackage()); 28 | return packages; 29 | } 30 | 31 | @Override 32 | protected String getJSMainModuleName() { 33 | return "index"; 34 | } 35 | 36 | @Override 37 | protected boolean isNewArchEnabled() { 38 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; 39 | } 40 | 41 | @Override 42 | protected Boolean isHermesEnabled() { 43 | return BuildConfig.IS_HERMES_ENABLED; 44 | } 45 | }; 46 | 47 | @Override 48 | public ReactNativeHost getReactNativeHost() { 49 | return mReactNativeHost; 50 | } 51 | 52 | @Override 53 | public void onCreate() { 54 | super.onCreate(); 55 | SoLoader.init(this, /* native exopackage */ false); 56 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { 57 | // If you opted-in for the New Architecture, we load the native entry point for this app. 58 | DefaultNewArchitectureEntryPoint.load(); 59 | } 60 | ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /example/ios/WebassemblyExampleTests/WebassemblyExampleTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface WebassemblyExampleTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation WebassemblyExampleTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction( 38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 39 | if (level >= RCTLogLevelError) { 40 | redboxError = message; 41 | } 42 | }); 43 | #endif 44 | 45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 48 | 49 | foundElement = [self findSubviewInView:vc.view 50 | matching:^BOOL(UIView *view) { 51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 52 | return YES; 53 | } 54 | return NO; 55 | }]; 56 | } 57 | 58 | #ifdef DEBUG 59 | RCTSetLogFunction(RCTDefaultLogFunction); 60 | #endif 61 | 62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | ENV['RCT_NEW_ARCH_ENABLED'] = '1' 2 | 3 | require_relative '../node_modules/react-native/scripts/react_native_pods' 4 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 5 | 6 | platform :ios, min_ios_version_supported 7 | prepare_react_native_project! 8 | 9 | # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. 10 | # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded 11 | # 12 | # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` 13 | # ```js 14 | # module.exports = { 15 | # dependencies: { 16 | # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), 17 | # ``` 18 | flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled 19 | 20 | linkage = ENV['USE_FRAMEWORKS'] 21 | if linkage != nil 22 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 23 | use_frameworks! :linkage => linkage.to_sym 24 | end 25 | 26 | target 'WebassemblyExample' do 27 | config = use_native_modules! 28 | 29 | # Flags change depending on the env values. 30 | flags = get_default_flags() 31 | 32 | use_react_native!( 33 | :path => config[:reactNativePath], 34 | # Hermes is now enabled by default. Disable by setting this flag to false. 35 | # Upcoming versions of React Native may rely on get_default_flags(), but 36 | # we make it explicit here to aid in the React Native upgrade process. 37 | :hermes_enabled => flags[:hermes_enabled], 38 | :fabric_enabled => flags[:fabric_enabled], 39 | # Enables Flipper. 40 | # 41 | # Note that if you have use_frameworks! enabled, Flipper will not work and 42 | # you should disable the next line. 43 | :flipper_configuration => flipper_config, 44 | # An absolute path to your application root. 45 | :app_path => "#{Pod::Config.instance.installation_root}/.." 46 | ) 47 | 48 | target 'WebassemblyExampleTests' do 49 | inherit! :complete 50 | # Pods for testing 51 | end 52 | 53 | post_install do |installer| 54 | react_native_post_install( 55 | installer, 56 | # Set `mac_catalyst_enabled` to `true` in order to apply patches 57 | # necessary for Mac Catalyst builds 58 | :mac_catalyst_enabled => false 59 | ) 60 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /cpp/m3_exec_defs.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_exec_defs.h 3 | // 4 | // Created by Steven Massey on 5/1/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_exec_defs_h 9 | #define m3_exec_defs_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | # define m3MemData(mem) (u8*)(((M3MemoryHeader*)(mem))+1) 16 | # define m3MemRuntime(mem) (((M3MemoryHeader*)(mem))->runtime) 17 | # define m3MemInfo(mem) (&(((M3MemoryHeader*)(mem))->runtime->memory)) 18 | 19 | # define d_m3BaseOpSig pc_t _pc, m3stack_t _sp, M3MemoryHeader * _mem, m3reg_t _r0 20 | # define d_m3BaseOpArgs _sp, _mem, _r0 21 | # define d_m3BaseOpAllArgs _pc, _sp, _mem, _r0 22 | # define d_m3BaseOpDefaultArgs 0 23 | # define d_m3BaseClearRegisters _r0 = 0; 24 | 25 | # define d_m3ExpOpSig(...) d_m3BaseOpSig, __VA_ARGS__ 26 | # define d_m3ExpOpArgs(...) d_m3BaseOpArgs, __VA_ARGS__ 27 | # define d_m3ExpOpAllArgs(...) d_m3BaseOpAllArgs, __VA_ARGS__ 28 | # define d_m3ExpOpDefaultArgs(...) d_m3BaseOpDefaultArgs, __VA_ARGS__ 29 | # define d_m3ExpClearRegisters(...) d_m3BaseClearRegisters; __VA_ARGS__ 30 | 31 | # if d_m3HasFloat 32 | # define d_m3OpSig d_m3ExpOpSig (f64 _fp0) 33 | # define d_m3OpArgs d_m3ExpOpArgs (_fp0) 34 | # define d_m3OpAllArgs d_m3ExpOpAllArgs (_fp0) 35 | # define d_m3OpDefaultArgs d_m3ExpOpDefaultArgs (0.) 36 | # define d_m3ClearRegisters d_m3ExpClearRegisters (_fp0 = 0.;) 37 | # else 38 | # define d_m3OpSig d_m3BaseOpSig 39 | # define d_m3OpArgs d_m3BaseOpArgs 40 | # define d_m3OpAllArgs d_m3BaseOpAllArgs 41 | # define d_m3OpDefaultArgs d_m3BaseOpDefaultArgs 42 | # define d_m3ClearRegisters d_m3BaseClearRegisters 43 | # endif 44 | 45 | typedef m3ret_t (vectorcall * IM3Operation) (d_m3OpSig); 46 | 47 | #define d_m3RetSig static inline m3ret_t vectorcall 48 | #define d_m3Op(NAME) M3_NO_UBSAN d_m3RetSig op_##NAME (d_m3OpSig) 49 | 50 | #define nextOpImpl() ((IM3Operation)(* _pc))(_pc + 1, d_m3OpArgs) 51 | #define jumpOpImpl(PC) ((IM3Operation)(* PC))( PC + 1, d_m3OpArgs) 52 | 53 | #define nextOpDirect() M3_MUSTTAIL return nextOpImpl() 54 | #define jumpOpDirect(PC) M3_MUSTTAIL return jumpOpImpl((pc_t)(PC)) 55 | 56 | d_m3RetSig RunCode (d_m3OpSig) 57 | { 58 | nextOpDirect(); 59 | } 60 | 61 | d_m3EndExternC 62 | 63 | #endif // m3_exec_defs_h 64 | -------------------------------------------------------------------------------- /cpp/m3_code.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_code.h 3 | // 4 | // Created by Steven Massey on 4/19/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_code_h 9 | #define m3_code_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | typedef struct M3CodePage 16 | { 17 | M3CodePageHeader info; 18 | code_t code [1]; 19 | } 20 | M3CodePage; 21 | 22 | typedef M3CodePage * IM3CodePage; 23 | 24 | 25 | IM3CodePage NewCodePage (IM3Runtime i_runtime, u32 i_minNumLines); 26 | 27 | void FreeCodePages (IM3CodePage * io_list); 28 | 29 | u32 NumFreeLines (IM3CodePage i_page); 30 | pc_t GetPageStartPC (IM3CodePage i_page); 31 | pc_t GetPagePC (IM3CodePage i_page); 32 | void EmitWord_impl (IM3CodePage i_page, void* i_word); 33 | void EmitWord32 (IM3CodePage i_page, u32 i_word); 34 | void EmitWord64 (IM3CodePage i_page, u64 i_word); 35 | # if d_m3RecordBacktraces 36 | void EmitMappingEntry (IM3CodePage i_page, u32 i_moduleOffset); 37 | # endif // d_m3RecordBacktraces 38 | 39 | void PushCodePage (IM3CodePage * io_list, IM3CodePage i_codePage); 40 | IM3CodePage PopCodePage (IM3CodePage * io_list); 41 | 42 | IM3CodePage GetEndCodePage (IM3CodePage i_list); // i_list = NULL is valid 43 | u32 CountCodePages (IM3CodePage i_list); // i_list = NULL is valid 44 | 45 | # if d_m3RecordBacktraces 46 | bool ContainsPC (IM3CodePage i_page, pc_t i_pc); 47 | bool MapPCToOffset (IM3CodePage i_page, pc_t i_pc, u32 * o_moduleOffset); 48 | # endif // d_m3RecordBacktraces 49 | 50 | # ifdef DEBUG 51 | void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC); 52 | # endif 53 | 54 | #define EmitWord(page, val) EmitWord_impl(page, (void*)(val)) 55 | 56 | //--------------------------------------------------------------------------------------------------------------------------------- 57 | 58 | # if d_m3RecordBacktraces 59 | 60 | typedef struct M3CodeMapEntry 61 | { 62 | u32 pcOffset; 63 | u32 moduleOffset; 64 | } 65 | M3CodeMapEntry; 66 | 67 | typedef struct M3CodeMappingPage 68 | { 69 | pc_t basePC; 70 | u32 size; 71 | u32 capacity; 72 | M3CodeMapEntry entries []; 73 | } 74 | M3CodeMappingPage; 75 | 76 | # endif // d_m3RecordBacktraces 77 | 78 | d_m3EndExternC 79 | 80 | #endif // m3_code_h 81 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:7.2.1" 9 | } 10 | } 11 | 12 | def isNewArchitectureEnabled() { 13 | return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" 14 | } 15 | 16 | apply plugin: "com.android.library" 17 | 18 | 19 | def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') } 20 | 21 | if (isNewArchitectureEnabled()) { 22 | apply plugin: "com.facebook.react" 23 | } 24 | 25 | def getExtOrDefault(name) { 26 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["Webassembly_" + name] 27 | } 28 | 29 | def getExtOrIntegerDefault(name) { 30 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Webassembly_" + name]).toInteger() 31 | } 32 | 33 | android { 34 | ndkVersion getExtOrDefault("ndkVersion") 35 | compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") 36 | namespace("com.webassembly") 37 | 38 | defaultConfig { 39 | minSdkVersion getExtOrIntegerDefault("minSdkVersion") 40 | targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") 41 | buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() 42 | externalNativeBuild { 43 | cmake { 44 | cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all" 45 | arguments "-DANDROID_STL=c++_shared" 46 | abiFilters "x86", "x86_64", "armeabi-v7a", "arm64-v8a" 47 | } 48 | } 49 | } 50 | externalNativeBuild { 51 | cmake { 52 | path "CMakeLists.txt" 53 | } 54 | } 55 | buildTypes { 56 | release { 57 | minifyEnabled false 58 | } 59 | } 60 | 61 | buildFeatures { 62 | prefab true 63 | } 64 | 65 | lintOptions { 66 | disable "GradleCompatible" 67 | } 68 | 69 | compileOptions { 70 | sourceCompatibility JavaVersion.VERSION_1_8 71 | targetCompatibility JavaVersion.VERSION_1_8 72 | } 73 | 74 | sourceSets { 75 | main { 76 | if (isNewArchitectureEnabled()) { 77 | java.srcDirs += [ 78 | // This is needed to build Kotlin project with NewArch enabled 79 | "${project.buildDir}/generated/source/codegen/java" 80 | ] 81 | } 82 | } 83 | } 84 | } 85 | 86 | repositories { 87 | mavenCentral() 88 | google() 89 | } 90 | 91 | 92 | dependencies { 93 | // For < 0.71, this will be from the local maven repo 94 | // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin 95 | //noinspection GradleDynamicVersion 96 | implementation "com.facebook.react:react-native:+" 97 | } 98 | 99 | if (isNewArchitectureEnabled()) { 100 | react { 101 | jsRootDir = file("../src/") 102 | libraryName = "Webassembly" 103 | codegenJavaPackageName = "com.webassembly" 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /example/android/app/src/debug/java/com/webassemblyexample/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.webassemblyexample; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 21 | import com.facebook.react.ReactInstanceEventListener; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | /** 28 | * Class responsible of loading Flipper inside your React Native application. This is the debug 29 | * flavor of it. Here you can add your own plugins and customize the Flipper setup. 30 | */ 31 | public class ReactNativeFlipper { 32 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 33 | if (FlipperUtils.shouldEnableFlipper(context)) { 34 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 35 | 36 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 37 | client.addPlugin(new DatabasesFlipperPlugin(context)); 38 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 39 | client.addPlugin(CrashReporterPlugin.getInstance()); 40 | 41 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 42 | NetworkingModule.setCustomClientBuilder( 43 | new NetworkingModule.CustomClientBuilder() { 44 | @Override 45 | public void apply(OkHttpClient.Builder builder) { 46 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 47 | } 48 | }); 49 | client.addPlugin(networkFlipperPlugin); 50 | client.start(); 51 | 52 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 53 | // Hence we run if after all native modules have been initialized 54 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 55 | if (reactContext == null) { 56 | reactInstanceManager.addReactInstanceEventListener( 57 | new ReactInstanceEventListener() { 58 | @Override 59 | public void onReactContextInitialized(ReactContext reactContext) { 60 | reactInstanceManager.removeReactInstanceEventListener(this); 61 | reactContext.runOnNativeModulesQueueThread( 62 | new Runnable() { 63 | @Override 64 | public void run() { 65 | client.addPlugin(new FrescoFlipperPlugin()); 66 | } 67 | }); 68 | } 69 | }); 70 | } else { 71 | client.addPlugin(new FrescoFlipperPlugin()); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /example/ios/WebassemblyExample.xcodeproj/xcshareddata/xcschemes/WebassemblyExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /cpp/m3_function.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_function.h 3 | // 4 | // Created by Steven Massey on 4/7/21. 5 | // Copyright © 2021 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_function_h 9 | #define m3_function_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | //--------------------------------------------------------------------------------------------------------------------------------- 16 | 17 | typedef struct M3FuncType 18 | { 19 | struct M3FuncType * next; 20 | 21 | u16 numRets; 22 | u16 numArgs; 23 | u8 types []; // returns, then args 24 | } 25 | M3FuncType; 26 | 27 | typedef M3FuncType * IM3FuncType; 28 | 29 | 30 | M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numTypes); 31 | bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB); 32 | 33 | u16 GetFuncTypeNumParams (const IM3FuncType i_funcType); 34 | u8 GetFuncTypeParamType (const IM3FuncType i_funcType, u16 i_index); 35 | 36 | u16 GetFuncTypeNumResults (const IM3FuncType i_funcType); 37 | u8 GetFuncTypeResultType (const IM3FuncType i_funcType, u16 i_index); 38 | 39 | //--------------------------------------------------------------------------------------------------------------------------------- 40 | 41 | typedef struct M3Function 42 | { 43 | struct M3Module * module; 44 | 45 | M3ImportInfo import; 46 | 47 | bytes_t wasm; 48 | bytes_t wasmEnd; 49 | 50 | cstr_t names[d_m3MaxDuplicateFunctionImpl]; 51 | cstr_t export_name; // should be a part of "names" 52 | u16 numNames; // maximum of d_m3MaxDuplicateFunctionImpl 53 | 54 | IM3FuncType funcType; 55 | 56 | pc_t compiled; 57 | 58 | # if (d_m3EnableCodePageRefCounting) 59 | IM3CodePage * codePageRefs; // array of all pages used 60 | u32 numCodePageRefs; 61 | # endif 62 | 63 | # if defined (DEBUG) 64 | u32 hits; 65 | u32 index; 66 | # endif 67 | 68 | u16 maxStackSlots; 69 | 70 | u16 numRetSlots; 71 | u16 numRetAndArgSlots; 72 | 73 | u16 numLocals; // not including args 74 | u16 numLocalBytes; 75 | 76 | bool ownsWasmCode; 77 | 78 | u16 numConstantBytes; 79 | void * constants; 80 | } 81 | M3Function; 82 | 83 | void Function_Release (IM3Function i_function); 84 | void Function_FreeCompiledCode (IM3Function i_function); 85 | 86 | cstr_t GetFunctionImportModuleName (IM3Function i_function); 87 | cstr_t * GetFunctionNames (IM3Function i_function, u16 * o_numNames); 88 | u16 GetFunctionNumArgs (IM3Function i_function); 89 | u8 GetFunctionArgType (IM3Function i_function, u32 i_index); 90 | 91 | u16 GetFunctionNumReturns (IM3Function i_function); 92 | u8 GetFunctionReturnType (const IM3Function i_function, u16 i_index); 93 | 94 | u32 GetFunctionNumArgsAndLocals (IM3Function i_function); 95 | 96 | cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp); 97 | 98 | //--------------------------------------------------------------------------------------------------------------------------------- 99 | 100 | 101 | d_m3EndExternC 102 | 103 | #endif /* m3_function_h */ 104 | -------------------------------------------------------------------------------- /example/ios/WebassemblyExample/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-webassembly", 3 | "version": "0.3.4", 4 | "description": "⚛️ 🏎 WebAssembly for React Native JSI.", 5 | "main": "lib/commonjs/index", 6 | "module": "lib/module/index", 7 | "types": "lib/typescript/index.d.ts", 8 | "react-native": "src/index", 9 | "source": "src/index", 10 | "files": [ 11 | "src", 12 | "lib", 13 | "android", 14 | "ios", 15 | "cpp", 16 | "app.plugin.js", 17 | "*.podspec", 18 | "!lib/typescript/example", 19 | "!ios/build", 20 | "!android/build", 21 | "!android/gradle", 22 | "!android/gradlew", 23 | "!android/gradlew.bat", 24 | "!android/local.properties", 25 | "!**/__tests__", 26 | "!**/__fixtures__", 27 | "!**/__mocks__", 28 | "!**/.*" 29 | ], 30 | "scripts": { 31 | "test": "jest", 32 | "typecheck": "tsc --noEmit", 33 | "lint": "eslint \"**/*.{js,ts,tsx}\"", 34 | "prepack": "bob build", 35 | "build": "yarn prepack", 36 | "release": "release-it", 37 | "example": "yarn --cwd example", 38 | "bootstrap": "yarn example && yarn install && yarn example pods", 39 | "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build" 40 | }, 41 | "keywords": [ 42 | "react-native", 43 | "ios", 44 | "android" 45 | ], 46 | "repository": "https://github.com/cawfree/react-native-webassembly", 47 | "author": "cawfree (https://github.com/cawfree)", 48 | "license": "MIT", 49 | "bugs": { 50 | "url": "https://github.com/cawfree/react-native-webassembly/issues" 51 | }, 52 | "homepage": "https://github.com/cawfree/react-native-webassembly#readme", 53 | "publishConfig": { 54 | "registry": "https://registry.npmjs.org/" 55 | }, 56 | "devDependencies": { 57 | "@commitlint/config-conventional": "^17.0.2", 58 | "@evilmartians/lefthook": "^1.2.2", 59 | "@react-native-community/eslint-config": "^3.0.2", 60 | "@release-it/conventional-changelog": "^5.0.0", 61 | "@types/jest": "^28.1.2", 62 | "@types/react": "~17.0.21", 63 | "@types/react-native": "0.70.0", 64 | "commitlint": "^17.0.2", 65 | "del-cli": "^5.0.0", 66 | "eslint": "^8.4.1", 67 | "eslint-config-prettier": "^8.5.0", 68 | "eslint-plugin-prettier": "^4.0.0", 69 | "jest": "^28.1.1", 70 | "pod-install": "^0.1.0", 71 | "prettier": "^2.0.5", 72 | "react": "18.2.0", 73 | "react-native": "0.71.4", 74 | "react-native-builder-bob": "^0.20.4", 75 | "release-it": "^15.0.0", 76 | "typescript": "^4.5.2" 77 | }, 78 | "resolutions": { 79 | "@types/react": "17.0.21" 80 | }, 81 | "peerDependencies": { 82 | "react": "*", 83 | "react-native": "*" 84 | }, 85 | "engines": { 86 | "node": ">= 16.0.0" 87 | }, 88 | "packageManager": "^yarn@1.22.15", 89 | "jest": { 90 | "preset": "react-native", 91 | "modulePathIgnorePatterns": [ 92 | "/example/node_modules", 93 | "/lib/" 94 | ] 95 | }, 96 | "commitlint": { 97 | "extends": [ 98 | "@commitlint/config-conventional" 99 | ] 100 | }, 101 | "release-it": { 102 | "git": { 103 | "commitMessage": "chore: release ${version}", 104 | "tagName": "v${version}" 105 | }, 106 | "npm": { 107 | "publish": true 108 | }, 109 | "github": { 110 | "release": true 111 | }, 112 | "plugins": { 113 | "@release-it/conventional-changelog": { 114 | "preset": "angular" 115 | } 116 | } 117 | }, 118 | "eslintConfig": { 119 | "root": true, 120 | "extends": [ 121 | "@react-native-community", 122 | "prettier" 123 | ], 124 | "rules": { 125 | "prettier/prettier": [ 126 | "error", 127 | { 128 | "quoteProps": "consistent", 129 | "singleQuote": true, 130 | "tabWidth": 2, 131 | "trailingComma": "es5", 132 | "useTabs": false 133 | } 134 | ] 135 | } 136 | }, 137 | "eslintIgnore": [ 138 | "node_modules/", 139 | "lib/" 140 | ], 141 | "prettier": { 142 | "quoteProps": "consistent", 143 | "singleQuote": true, 144 | "tabWidth": 2, 145 | "trailingComma": "es5", 146 | "useTabs": false 147 | }, 148 | "react-native-builder-bob": { 149 | "source": "src", 150 | "output": "lib", 151 | "targets": [ 152 | "commonjs", 153 | "module", 154 | [ 155 | "typescript", 156 | { 157 | "project": "tsconfig.build.json" 158 | } 159 | ] 160 | ] 161 | }, 162 | "codegenConfig": { 163 | "name": "RNWebassemblySpec", 164 | "type": "modules", 165 | "jsSrcsDir": "src" 166 | }, 167 | "dependencies": { 168 | "buffer": "^6.0.3", 169 | "nanoid": "3.3.4" 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are always welcome, no matter how large or small! 4 | 5 | We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md). 6 | 7 | ## Development workflow 8 | 9 | To get started with the project, run `yarn` in the root directory to install the required dependencies for each package: 10 | 11 | ```sh 12 | yarn 13 | ``` 14 | 15 | > While it's possible to use [`npm`](https://github.com/npm/cli), the tooling is built around [`yarn`](https://classic.yarnpkg.com/), so you'll have an easier time if you use `yarn` for development. 16 | 17 | While developing, you can run the [example app](/example/) to test your changes. Any changes you make in your library's JavaScript code will be reflected in the example app without a rebuild. If you change any native code, then you'll need to rebuild the example app. 18 | 19 | To start the packager: 20 | 21 | ```sh 22 | yarn example start 23 | ``` 24 | 25 | To run the example app on Android: 26 | 27 | ```sh 28 | yarn example android 29 | ``` 30 | 31 | To run the example app on iOS: 32 | 33 | ```sh 34 | yarn example ios 35 | ``` 36 | 37 | To confirm that the app is running with the new architecture, you can check the Metro logs for a message like this: 38 | 39 | ```sh 40 | Running "WebassemblyExample" with {"fabric":true,"initialProps":{"concurrentRoot":true},"rootTag":1} 41 | ``` 42 | 43 | Note the `"fabric":true` and `"concurrentRoot":true` properties. 44 | 45 | Make sure your code passes TypeScript and ESLint. Run the following to verify: 46 | 47 | ```sh 48 | yarn typecheck 49 | yarn lint 50 | ``` 51 | 52 | To fix formatting errors, run the following: 53 | 54 | ```sh 55 | yarn lint --fix 56 | ``` 57 | 58 | Remember to add tests for your change if possible. Run the unit tests by: 59 | 60 | ```sh 61 | yarn test 62 | ``` 63 | 64 | To edit the Objective-C or Swift files, open `example/ios/WebassemblyExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > react-native-webassembly`. 65 | 66 | To edit the Java or Kotlin files, open `example/android` in Android studio and find the source files at `react-native-webassembly` under `Android`. 67 | 68 | 69 | ### Commit message convention 70 | 71 | We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages: 72 | 73 | - `fix`: bug fixes, e.g. fix crash due to deprecated method. 74 | - `feat`: new features, e.g. add new method to the module. 75 | - `refactor`: code refactor, e.g. migrate from class components to hooks. 76 | - `docs`: changes into documentation, e.g. add usage example for the module.. 77 | - `test`: adding or updating tests, e.g. add integration tests using detox. 78 | - `chore`: tooling changes, e.g. change CI config. 79 | 80 | Our pre-commit hooks verify that your commit message matches this format when committing. 81 | 82 | ### Linting and tests 83 | 84 | [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) 85 | 86 | We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing. 87 | 88 | Our pre-commit hooks verify that the linter and tests pass when committing. 89 | 90 | ### Publishing to npm 91 | 92 | We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc. 93 | 94 | To publish new versions, run the following: 95 | 96 | ```sh 97 | yarn release 98 | ``` 99 | 100 | ### Scripts 101 | 102 | The `package.json` file contains various scripts for common tasks: 103 | 104 | - `yarn bootstrap`: setup project by installing all dependencies and pods. 105 | - `yarn typecheck`: type-check files with TypeScript. 106 | - `yarn lint`: lint files with ESLint. 107 | - `yarn test`: run unit tests with Jest. 108 | - `yarn example start`: start the Metro server for the example app. 109 | - `yarn example android`: run the example app on Android. 110 | - `yarn example ios`: run the example app on iOS. 111 | 112 | ### Sending a pull request 113 | 114 | > **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github). 115 | 116 | When you're sending a pull request: 117 | 118 | - Prefer small pull requests focused on one change. 119 | - Verify that linters and tests are passing. 120 | - Review the documentation to make sure it looks good. 121 | - Follow the pull request template when opening a pull request. 122 | - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. 123 | -------------------------------------------------------------------------------- /cpp/m3_module.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_module.c 3 | // 4 | // Created by Steven Massey on 5/7/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #include "m3_env.h" 9 | #include "m3_exception.h" 10 | 11 | 12 | void Module_FreeFunctions (IM3Module i_module) 13 | { 14 | for (u32 i = 0; i < i_module->numFunctions; ++i) 15 | { 16 | IM3Function func = & i_module->functions [i]; 17 | Function_Release (func); 18 | } 19 | } 20 | 21 | 22 | void m3_FreeModule (IM3Module i_module) 23 | { 24 | if (i_module) 25 | { 26 | m3log (module, "freeing module: %s (funcs: %d; segments: %d)", 27 | i_module->name, i_module->numFunctions, i_module->numDataSegments); 28 | 29 | Module_FreeFunctions (i_module); 30 | 31 | m3_Free (i_module->functions); 32 | //m3_Free (i_module->imports); 33 | m3_Free (i_module->funcTypes); 34 | m3_Free (i_module->dataSegments); 35 | m3_Free (i_module->table0); 36 | 37 | for (u32 i = 0; i < i_module->numGlobals; ++i) 38 | { 39 | m3_Free (i_module->globals[i].name); 40 | FreeImportInfo(&(i_module->globals[i].import)); 41 | } 42 | m3_Free (i_module->globals); 43 | 44 | m3_Free (i_module); 45 | } 46 | } 47 | 48 | 49 | M3Result Module_AddGlobal (IM3Module io_module, IM3Global * o_global, u8 i_type, bool i_mutable, bool i_isImported) 50 | { 51 | _try { 52 | u32 index = io_module->numGlobals++; 53 | io_module->globals = m3_ReallocArray (M3Global, io_module->globals, io_module->numGlobals, index); 54 | _throwifnull (io_module->globals); 55 | M3Global * global = & io_module->globals [index]; 56 | 57 | global->type = i_type; 58 | global->imported = i_isImported; 59 | global->isMutable = i_mutable; 60 | 61 | if (o_global) 62 | * o_global = global; 63 | 64 | } _catch: 65 | return result; 66 | } 67 | 68 | M3Result Module_PreallocFunctions (IM3Module io_module, u32 i_totalFunctions) 69 | { 70 | _try { 71 | if (i_totalFunctions > io_module->allFunctions) { 72 | io_module->functions = m3_ReallocArray (M3Function, io_module->functions, i_totalFunctions, io_module->allFunctions); 73 | io_module->allFunctions = i_totalFunctions; 74 | _throwifnull (io_module->functions); 75 | } 76 | } _catch: 77 | return result; 78 | } 79 | 80 | M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo) 81 | { 82 | _try { 83 | 84 | u32 index = io_module->numFunctions++; 85 | _ (Module_PreallocFunctions(io_module, io_module->numFunctions)); 86 | 87 | _throwif ("type sig index out of bounds", i_typeIndex >= io_module->numFuncTypes); 88 | 89 | IM3FuncType ft = io_module->funcTypes [i_typeIndex]; 90 | 91 | IM3Function func = Module_GetFunction (io_module, index); 92 | func->funcType = ft; 93 | 94 | # ifdef DEBUG 95 | func->index = index; 96 | # endif 97 | 98 | if (i_importInfo and func->numNames == 0) 99 | { 100 | func->import = * i_importInfo; 101 | func->names[0] = i_importInfo->fieldUtf8; 102 | func->numNames = 1; 103 | } 104 | 105 | m3log (module, " added function: %3d; sig: %d", index, i_typeIndex); 106 | 107 | } _catch: 108 | return result; 109 | } 110 | 111 | #ifdef DEBUG 112 | void Module_GenerateNames (IM3Module i_module) 113 | { 114 | for (u32 i = 0; i < i_module->numFunctions; ++i) 115 | { 116 | IM3Function func = & i_module->functions [i]; 117 | 118 | if (func->numNames == 0) 119 | { 120 | char* buff = m3_AllocArray(char, 16); 121 | snprintf(buff, 16, "$func%d", i); 122 | func->names[0] = buff; 123 | func->numNames = 1; 124 | } 125 | } 126 | for (u32 i = 0; i < i_module->numGlobals; ++i) 127 | { 128 | IM3Global global = & i_module->globals [i]; 129 | 130 | if (global->name == NULL) 131 | { 132 | char* buff = m3_AllocArray(char, 16); 133 | snprintf(buff, 16, "$global%d", i); 134 | global->name = buff; 135 | } 136 | } 137 | } 138 | #endif 139 | 140 | IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex) 141 | { 142 | IM3Function func = NULL; 143 | 144 | if (i_functionIndex < i_module->numFunctions) 145 | { 146 | func = & i_module->functions [i_functionIndex]; 147 | //func->module = i_module; 148 | } 149 | 150 | return func; 151 | } 152 | 153 | 154 | const char* m3_GetModuleName (IM3Module i_module) 155 | { 156 | if (!i_module || !i_module->name) 157 | return ".unnamed"; 158 | 159 | return i_module->name; 160 | } 161 | 162 | void m3_SetModuleName (IM3Module i_module, const char* name) 163 | { 164 | if (i_module) i_module->name = name; 165 | } 166 | 167 | IM3Runtime m3_GetModuleRuntime (IM3Module i_module) 168 | { 169 | return i_module ? i_module->runtime : NULL; 170 | } 171 | 172 | -------------------------------------------------------------------------------- /cpp/m3_config.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_config.h 3 | // 4 | // Created by Steven Massey on 5/4/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_config_h 9 | #define m3_config_h 10 | 11 | #include "m3_config_platforms.h" 12 | 13 | // general -------------------------------------------------------------------- 14 | 15 | # ifndef d_m3CodePageAlignSize 16 | # define d_m3CodePageAlignSize 32*1024 17 | # endif 18 | 19 | # ifndef d_m3MaxFunctionStackHeight 20 | # define d_m3MaxFunctionStackHeight 2000 // max: 32768 21 | # endif 22 | 23 | # ifndef d_m3MaxLinearMemoryPages 24 | # define d_m3MaxLinearMemoryPages 65536 25 | # endif 26 | 27 | # ifndef d_m3MaxFunctionSlots 28 | # define d_m3MaxFunctionSlots ((d_m3MaxFunctionStackHeight)*2) 29 | # endif 30 | 31 | # ifndef d_m3MaxConstantTableSize 32 | # define d_m3MaxConstantTableSize 120 33 | # endif 34 | 35 | # ifndef d_m3MaxDuplicateFunctionImpl 36 | # define d_m3MaxDuplicateFunctionImpl 3 37 | # endif 38 | 39 | # ifndef d_m3CascadedOpcodes // Cascaded opcodes are slightly faster at the expense of some memory 40 | # define d_m3CascadedOpcodes 1 // Adds ~3Kb to operations table in m3_compile.c 41 | # endif 42 | 43 | # ifndef d_m3VerboseErrorMessages 44 | # define d_m3VerboseErrorMessages 1 45 | # endif 46 | 47 | # ifndef d_m3FixedHeap 48 | # define d_m3FixedHeap false 49 | //# define d_m3FixedHeap (32*1024) 50 | # endif 51 | 52 | # ifndef d_m3FixedHeapAlign 53 | # define d_m3FixedHeapAlign 16 54 | # endif 55 | 56 | # ifndef d_m3Use32BitSlots 57 | # define d_m3Use32BitSlots 1 58 | # endif 59 | 60 | # ifndef d_m3ProfilerSlotMask 61 | # define d_m3ProfilerSlotMask 0xFFFF 62 | # endif 63 | 64 | # ifndef d_m3RecordBacktraces 65 | # define d_m3RecordBacktraces 0 66 | # endif 67 | 68 | # ifndef d_m3EnableExceptionBreakpoint 69 | # define d_m3EnableExceptionBreakpoint 0 // see m3_exception.h 70 | # endif 71 | 72 | 73 | // profiling and tracing ------------------------------------------------------ 74 | 75 | # ifndef d_m3EnableOpProfiling 76 | # define d_m3EnableOpProfiling 0 // opcode usage counters 77 | # endif 78 | 79 | # ifndef d_m3EnableOpTracing 80 | # define d_m3EnableOpTracing 0 // only works with DEBUG 81 | # endif 82 | 83 | # ifndef d_m3EnableWasiTracing 84 | # define d_m3EnableWasiTracing 0 85 | # endif 86 | 87 | # ifndef d_m3EnableStrace 88 | # define d_m3EnableStrace 0 // 1 - trace exported function calls 89 | // 2 - trace all calls (structured) 90 | // 3 - all calls + loops + memory operations 91 | # endif 92 | 93 | 94 | // logging -------------------------------------------------------------------- 95 | 96 | # ifndef d_m3LogParse 97 | # define d_m3LogParse 0 // .wasm binary decoding info 98 | # endif 99 | 100 | # ifndef d_m3LogModule 101 | # define d_m3LogModule 0 // wasm module info 102 | # endif 103 | 104 | # ifndef d_m3LogCompile 105 | # define d_m3LogCompile 0 // wasm -> metacode generation phase 106 | # endif 107 | 108 | # ifndef d_m3LogWasmStack 109 | # define d_m3LogWasmStack 0 // dump the wasm stack when pushed or popped 110 | # endif 111 | 112 | # ifndef d_m3LogEmit 113 | # define d_m3LogEmit 0 // metacode generation info 114 | # endif 115 | 116 | # ifndef d_m3LogCodePages 117 | # define d_m3LogCodePages 0 // dump metacode pages when released 118 | # endif 119 | 120 | # ifndef d_m3LogRuntime 121 | # define d_m3LogRuntime 0 // higher-level runtime information 122 | # endif 123 | 124 | # ifndef d_m3LogNativeStack 125 | # define d_m3LogNativeStack 0 // track the memory usage of the C-stack 126 | # endif 127 | 128 | # ifndef d_m3LogHeapOps 129 | # define d_m3LogHeapOps 0 // track heap usage 130 | # endif 131 | 132 | # ifndef d_m3LogTimestamps 133 | # define d_m3LogTimestamps 0 // track timestamps on heap logs 134 | # endif 135 | 136 | // other ---------------------------------------------------------------------- 137 | 138 | # ifndef d_m3HasFloat 139 | # define d_m3HasFloat 1 // implement floating point ops 140 | # endif 141 | 142 | #if !d_m3HasFloat && !defined(d_m3NoFloatDynamic) 143 | # define d_m3NoFloatDynamic 1 // if no floats, do not fail until flops are actually executed 144 | #endif 145 | 146 | # ifndef d_m3SkipStackCheck 147 | # define d_m3SkipStackCheck 0 // skip stack overrun checks 148 | # endif 149 | 150 | # ifndef d_m3SkipMemoryBoundsCheck 151 | # define d_m3SkipMemoryBoundsCheck 0 // skip memory bounds checks 152 | # endif 153 | 154 | #define d_m3EnableCodePageRefCounting 0 // not supported currently 155 | 156 | #endif // m3_config_h 157 | -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Button, StyleSheet, View } from 'react-native'; 3 | 4 | import * as WebAssembly from 'react-native-webassembly'; 5 | 6 | import LocalHello from './sources/Local.Hello.wasm'; 7 | import LocalCallback from './sources/Local.Callback.wasm'; 8 | import LocalSimpleMemory from './sources/Local.SimpleMemory.wasm'; 9 | 10 | import { useWasmCircomRuntime, useWasmHelloWorld } from './hooks'; 11 | import { fetchWasm } from './utils'; 12 | 13 | export default function App() { 14 | const helloWorld = useWasmHelloWorld(); 15 | const helloWorldResult = 16 | 'result' in helloWorld ? helloWorld.result : undefined; 17 | const helloWorldError = 'error' in helloWorld ? helloWorld.error : undefined; 18 | 19 | const { calculateWTNSBin, error: circomError } = useWasmCircomRuntime(); 20 | 21 | /* Hook I/O. */ 22 | React.useEffect( 23 | () => void (helloWorldError && console.error(helloWorldError)), 24 | [helloWorldError] 25 | ); 26 | 27 | /* ZK Snark */ 28 | React.useEffect( 29 | () => void (circomError && console.error(circomError)), 30 | [circomError] 31 | ); 32 | 33 | /* Add. */ 34 | React.useEffect(() => { 35 | if (!helloWorldResult) return; 36 | 37 | const result = helloWorldResult.instance.exports.add(103, 202); 38 | 39 | if (result !== 305) throw new Error('Failed to add.'); 40 | }, [helloWorldResult]); 41 | 42 | /* Local imports. */ 43 | React.useEffect( 44 | () => 45 | void (async () => { 46 | try { 47 | const localModule = await WebAssembly.instantiate<{ 48 | readonly add: (a: number, b: number) => number; 49 | }>(LocalHello); 50 | 51 | const result = localModule.instance.exports.add(1000, 2000); 52 | 53 | if (result !== 3000) throw new Error('Failed to add. (Local)'); 54 | } catch (e) { 55 | console.error(e); 56 | } 57 | })(), 58 | [] 59 | ); 60 | 61 | /* complex allocation */ 62 | React.useEffect( 63 | () => 64 | void (async () => { 65 | try { 66 | /* complex */ 67 | await WebAssembly.instantiate( 68 | await fetchWasm( 69 | 'https://github.com/tact-lang/ton-wasm/raw/main/output/wasm/emulator-emscripten.wasm' 70 | ), 71 | {} 72 | ); 73 | } catch (e) { 74 | console.error(e); 75 | } 76 | })(), 77 | [] 78 | ); 79 | 80 | /* callback e2e */ 81 | React.useEffect( 82 | () => 83 | void (async () => { 84 | try { 85 | const localCallback = await WebAssembly.instantiate<{ 86 | readonly callBackFunction: (a: number) => number; 87 | }>(LocalCallback, { 88 | runtime: { 89 | callback: (a: number): number => a * 2, 90 | }, 91 | }); 92 | 93 | const result = localCallback.instance.exports.callBackFunction(25); 94 | 95 | if (result !== 50) throw new Error('Callback failure.'); 96 | } catch (e) { 97 | console.error(e); 98 | } 99 | })(), 100 | [] 101 | ); 102 | 103 | /* Simple memory. */ 104 | React.useEffect( 105 | () => 106 | void (async () => { 107 | try { 108 | const localSimpleMemory = await WebAssembly.instantiate<{ 109 | readonly write_byte_to_memory: (value: number) => void; 110 | readonly read_byte_from_memory: () => number; 111 | }>(LocalSimpleMemory); 112 | 113 | const testMemory = (withValue: number) => { 114 | localSimpleMemory.instance.exports.write_byte_to_memory(withValue); 115 | 116 | const wasmResult = 117 | localSimpleMemory.instance.exports.read_byte_from_memory(); 118 | 119 | if (wasmResult !== withValue) 120 | throw new Error( 121 | `Expected ${withValue}, encountered wasm ${wasmResult}.` 122 | ); 123 | 124 | const ab: ArrayBuffer = localSimpleMemory.instance.exports.memory!; 125 | 126 | const jsResult = new Uint8Array(ab.slice(0, 1))[0]; 127 | 128 | // Ensure the JavaScript buffer is up-to-date. 129 | if (jsResult !== withValue) 130 | throw new Error( 131 | `Expected ${withValue}, encountered js ${jsResult}.` 132 | ); 133 | }; 134 | 135 | for (let i = 0; i < 255; i += 1) testMemory(i); 136 | } catch (e) { 137 | console.error(e); 138 | } 139 | })(), 140 | [] 141 | ); 142 | 143 | return ( 144 | 145 |