From 846f1d4ab5e6ba59c77da73e211bc90c00309c33 Mon Sep 17 00:00:00 2001 From: cawfree Date: Mon, 20 Mar 2023 23:34:48 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8F=8E=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 15 + .gitattributes | 3 + .github/actions/setup/action.yml | 27 + .github/workflows/ci.yml | 48 + .gitignore | 75 + .npmignore | 4 + .nvmrc | 1 + .watchmanconfig | 1 + .yarnrc | 3 + CODE_OF_CONDUCT.md | 133 + CONTRIBUTING.md | 122 + LICENSE | 20 + README.md | 84 + android/CMakeLists.txt | 59 + android/build.gradle | 99 + android/cpp-adapter.cpp | 84 + android/gradle.properties | 5 + android/src/main/AndroidManifest.xml | 4 + .../com/webassembly/WebassemblyModule.java | 84 + .../com/webassembly/WebassemblyPackage.java | 44 + babel.config.js | 3 + cpp/m3_api_libc.c | 245 + cpp/m3_api_libc.h | 20 + cpp/m3_api_meta_wasi.c | 1101 ++ cpp/m3_api_tracer.c | 168 + cpp/m3_api_tracer.h | 19 + cpp/m3_api_uvwasi.c | 1219 ++ cpp/m3_api_wasi.c | 873 ++ cpp/m3_api_wasi.h | 38 + cpp/m3_bind.c | 176 + cpp/m3_bind.h | 20 + cpp/m3_code.c | 246 + cpp/m3_code.h | 80 + cpp/m3_compile.c | 2928 +++++ cpp/m3_compile.h | 206 + cpp/m3_config.h | 156 + cpp/m3_config_platforms.h | 208 + cpp/m3_core.c | 615 + cpp/m3_core.h | 311 + cpp/m3_env.c | 1189 ++ cpp/m3_env.h | 213 + cpp/m3_exception.h | 33 + cpp/m3_exec.c | 8 + cpp/m3_exec.h | 1500 +++ cpp/m3_exec_defs.h | 63 + cpp/m3_function.c | 233 + cpp/m3_function.h | 103 + cpp/m3_info.c | 564 + cpp/m3_info.h | 38 + cpp/m3_math_utils.h | 268 + cpp/m3_module.c | 171 + cpp/m3_parse.c | 650 ++ cpp/react-native-webassembly.cpp | 130 + cpp/react-native-webassembly.h | 29 + cpp/wasm3.h | 372 + cpp/wasm3_cpp.h | 491 + cpp/wasm3_defs.h | 293 + example/.bundle/config | 2 + example/.node-version | 1 + example/.ruby-version | 1 + example/.watchmanconfig | 1 + example/Gemfile | 6 + example/android/app/build.gradle | 170 + example/android/app/debug.keystore | Bin 0 -> 2257 bytes example/android/app/proguard-rules.pro | 10 + .../android/app/src/debug/AndroidManifest.xml | 13 + .../ReactNativeFlipper.java | 75 + .../android/app/src/main/AndroidManifest.xml | 25 + .../com/webassemblyexample/MainActivity.java | 35 + .../webassemblyexample/MainApplication.java | 62 + .../res/drawable/rn_edit_text_material.xml | 36 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3056 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5024 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2096 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2858 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4569 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7098 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6464 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10676 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9250 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15523 bytes .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/styles.xml | 9 + .../ReactNativeFlipper.java | 20 + example/android/build.gradle | 21 + example/android/gradle.properties | 44 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59821 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + example/android/gradlew | 234 + example/android/gradlew.bat | 89 + example/android/settings.gradle | 4 + example/app.json | 4 + example/babel.config.js | 17 + example/index.js | 5 + example/ios/.xcode.env | 11 + example/ios/File.swift | 6 + example/ios/Podfile | 62 + example/ios/Podfile.lock | 986 ++ .../ios/WebassemblyExample-Bridging-Header.h | 3 + .../project.pbxproj | 704 ++ .../xcschemes/WebassemblyExample.xcscheme | 88 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + example/ios/WebassemblyExample/AppDelegate.h | 6 + example/ios/WebassemblyExample/AppDelegate.mm | 36 + .../AppIcon.appiconset/Contents.json | 53 + .../Images.xcassets/Contents.json | 6 + example/ios/WebassemblyExample/Info.plist | 55 + .../LaunchScreen.storyboard | 47 + example/ios/WebassemblyExample/main.m | 10 + .../ios/WebassemblyExampleTests/Info.plist | 24 + .../WebassemblyExampleTests.m | 66 + example/metro.config.js | 40 + example/package.json | 23 + example/react-native.config.js | 10 + example/src/App.tsx | 48 + example/src/hooks/index.ts | 3 + example/src/hooks/useWasmCircomRuntime.ts | 228 + example/src/hooks/useWasmHelloWorld.ts | 8 + example/src/hooks/useWasmUri.ts | 51 + example/yarn.lock | 4799 ++++++++ ios/Webassembly.h | 15 + ios/Webassembly.mm | 49 + ios/Webassembly.xcodeproj/project.pbxproj | 278 + lefthook.yml | 16 + package.json | 169 + react-native-webassembly.podspec | 35 + scripts/bootstrap.js | 29 + src/NativeWebassembly.ts | 22 + src/__tests__/index.test.tsx | 1 + src/index.tsx | 62 + tsconfig.build.json | 5 + tsconfig.json | 28 + yarn.lock | 9928 +++++++++++++++++ 134 files changed, 34815 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/actions/setup/action.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 .nvmrc create mode 100644 .watchmanconfig create mode 100644 .yarnrc create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 android/CMakeLists.txt create mode 100644 android/build.gradle create mode 100644 android/cpp-adapter.cpp create mode 100644 android/gradle.properties create mode 100644 android/src/main/AndroidManifest.xml create mode 100644 android/src/main/java/com/webassembly/WebassemblyModule.java create mode 100644 android/src/main/java/com/webassembly/WebassemblyPackage.java create mode 100644 babel.config.js create mode 100644 cpp/m3_api_libc.c create mode 100644 cpp/m3_api_libc.h create mode 100644 cpp/m3_api_meta_wasi.c create mode 100644 cpp/m3_api_tracer.c create mode 100644 cpp/m3_api_tracer.h create mode 100644 cpp/m3_api_uvwasi.c create mode 100644 cpp/m3_api_wasi.c create mode 100644 cpp/m3_api_wasi.h create mode 100644 cpp/m3_bind.c create mode 100644 cpp/m3_bind.h create mode 100644 cpp/m3_code.c create mode 100644 cpp/m3_code.h create mode 100644 cpp/m3_compile.c create mode 100644 cpp/m3_compile.h create mode 100644 cpp/m3_config.h create mode 100644 cpp/m3_config_platforms.h create mode 100644 cpp/m3_core.c create mode 100644 cpp/m3_core.h create mode 100644 cpp/m3_env.c create mode 100644 cpp/m3_env.h create mode 100644 cpp/m3_exception.h create mode 100644 cpp/m3_exec.c create mode 100644 cpp/m3_exec.h create mode 100644 cpp/m3_exec_defs.h create mode 100644 cpp/m3_function.c create mode 100644 cpp/m3_function.h create mode 100644 cpp/m3_info.c create mode 100644 cpp/m3_info.h create mode 100644 cpp/m3_math_utils.h create mode 100644 cpp/m3_module.c create mode 100644 cpp/m3_parse.c create mode 100644 cpp/react-native-webassembly.cpp create mode 100644 cpp/react-native-webassembly.h create mode 100644 cpp/wasm3.h create mode 100644 cpp/wasm3_cpp.h create mode 100644 cpp/wasm3_defs.h create mode 100644 example/.bundle/config create mode 100644 example/.node-version create mode 100644 example/.ruby-version create mode 100644 example/.watchmanconfig create mode 100644 example/Gemfile create mode 100644 example/android/app/build.gradle create mode 100644 example/android/app/debug.keystore create mode 100644 example/android/app/proguard-rules.pro create mode 100644 example/android/app/src/debug/AndroidManifest.xml create mode 100644 example/android/app/src/debug/java/com/webassemblyexample/ReactNativeFlipper.java create mode 100644 example/android/app/src/main/AndroidManifest.xml create mode 100644 example/android/app/src/main/java/com/webassemblyexample/MainActivity.java create mode 100644 example/android/app/src/main/java/com/webassemblyexample/MainApplication.java create mode 100644 example/android/app/src/main/res/drawable/rn_edit_text_material.xml create mode 100644 example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 example/android/app/src/main/res/values/strings.xml create mode 100644 example/android/app/src/main/res/values/styles.xml create mode 100644 example/android/app/src/release/java/com/webassemblyexample/ReactNativeFlipper.java create mode 100644 example/android/build.gradle create mode 100644 example/android/gradle.properties create mode 100644 example/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 example/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 example/android/gradlew create mode 100644 example/android/gradlew.bat create mode 100644 example/android/settings.gradle create mode 100644 example/app.json create mode 100644 example/babel.config.js create mode 100644 example/index.js create mode 100644 example/ios/.xcode.env create mode 100644 example/ios/File.swift create mode 100644 example/ios/Podfile create mode 100644 example/ios/Podfile.lock create mode 100644 example/ios/WebassemblyExample-Bridging-Header.h create mode 100644 example/ios/WebassemblyExample.xcodeproj/project.pbxproj create mode 100644 example/ios/WebassemblyExample.xcodeproj/xcshareddata/xcschemes/WebassemblyExample.xcscheme create mode 100644 example/ios/WebassemblyExample.xcworkspace/contents.xcworkspacedata create mode 100644 example/ios/WebassemblyExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 example/ios/WebassemblyExample/AppDelegate.h create mode 100644 example/ios/WebassemblyExample/AppDelegate.mm create mode 100644 example/ios/WebassemblyExample/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 example/ios/WebassemblyExample/Images.xcassets/Contents.json create mode 100644 example/ios/WebassemblyExample/Info.plist create mode 100644 example/ios/WebassemblyExample/LaunchScreen.storyboard create mode 100644 example/ios/WebassemblyExample/main.m create mode 100644 example/ios/WebassemblyExampleTests/Info.plist create mode 100644 example/ios/WebassemblyExampleTests/WebassemblyExampleTests.m create mode 100644 example/metro.config.js create mode 100644 example/package.json create mode 100644 example/react-native.config.js create mode 100644 example/src/App.tsx create mode 100644 example/src/hooks/index.ts create mode 100644 example/src/hooks/useWasmCircomRuntime.ts create mode 100644 example/src/hooks/useWasmHelloWorld.ts create mode 100644 example/src/hooks/useWasmUri.ts create mode 100644 example/yarn.lock create mode 100644 ios/Webassembly.h create mode 100644 ios/Webassembly.mm create mode 100644 ios/Webassembly.xcodeproj/project.pbxproj create mode 100644 lefthook.yml create mode 100644 package.json create mode 100644 react-native-webassembly.podspec create mode 100644 scripts/bootstrap.js create mode 100644 src/NativeWebassembly.ts create mode 100644 src/__tests__/index.test.tsx create mode 100644 src/index.tsx create mode 100644 tsconfig.build.json create mode 100644 tsconfig.json create mode 100644 yarn.lock diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..65365be --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] + +indent_style = space +indent_size = 2 + +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..030ef14 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.pbxproj -text +# specific for windows script files +*.bat text eol=crlf \ No newline at end of file diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 0000000..d402f3d --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,27 @@ +name: Setup +description: Setup Node.js and install dependencies + +runs: + using: composite + steps: + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version-file: .nvmrc + + - name: Cache dependencies + id: yarn-cache + uses: actions/cache@v3 + with: + path: | + **/node_modules + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install dependencies + if: steps.yarn-cache.outputs.cache-hit != 'true' + run: | + yarn install --cwd example --frozen-lockfile + yarn install --frozen-lockfile + shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9c5ee1f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: CI +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup + uses: ./.github/actions/setup + + - name: Lint files + run: yarn lint + + - name: Typecheck files + run: yarn typecheck + + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup + uses: ./.github/actions/setup + + - name: Run unit tests + run: yarn test --maxWorkers=2 --coverage + + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup + uses: ./.github/actions/setup + + - name: Build package + run: yarn prepack diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..939c2fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,75 @@ +# OSX +# +.DS_Store + +# XDE +.expo/ + +# VSCode +.vscode/ +jsconfig.json + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Android/IJ +# +.classpath +.cxx +.gradle +.idea +.project +.settings +local.properties +android.iml + +# Cocoapods +# +example/ios/Pods + +# Ruby +example/vendor/ + +# node.js +# +node_modules/ +npm-debug.log +yarn-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ +android/app/libs +android/keystores/debug.keystore + +# Expo +.expo/ + +# Turborepo +.turbo/ + +# generated by bob +lib/ + +*.env +.idea/ +wasm3/ + diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..9500e52 --- /dev/null +++ b/.npmignore @@ -0,0 +1,4 @@ +*.env +.idea/ +wasm3/ + diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..5397c87 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +16.18.1 diff --git a/.watchmanconfig b/.watchmanconfig new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.watchmanconfig @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.yarnrc b/.yarnrc new file mode 100644 index 0000000..fedc0f1 --- /dev/null +++ b/.yarnrc @@ -0,0 +1,3 @@ +# Override Yarn command so we can automatically setup the repo on running `yarn` + +yarn-path "scripts/bootstrap.js" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..45d257b --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,133 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..71fdd4d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,122 @@ +# Contributing + +Contributions are always welcome, no matter how large or small! + +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). + +## Development workflow + +To get started with the project, run `yarn` in the root directory to install the required dependencies for each package: + +```sh +yarn +``` + +> 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. + +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. + +To start the packager: + +```sh +yarn example start +``` + +To run the example app on Android: + +```sh +yarn example android +``` + +To run the example app on iOS: + +```sh +yarn example ios +``` + +To confirm that the app is running with the new architecture, you can check the Metro logs for a message like this: + +```sh +Running "WebassemblyExample" with {"fabric":true,"initialProps":{"concurrentRoot":true},"rootTag":1} +``` + +Note the `"fabric":true` and `"concurrentRoot":true` properties. + +Make sure your code passes TypeScript and ESLint. Run the following to verify: + +```sh +yarn typecheck +yarn lint +``` + +To fix formatting errors, run the following: + +```sh +yarn lint --fix +``` + +Remember to add tests for your change if possible. Run the unit tests by: + +```sh +yarn test +``` + +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`. + +To edit the Java or Kotlin files, open `example/android` in Android studio and find the source files at `react-native-webassembly` under `Android`. + + +### Commit message convention + +We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages: + +- `fix`: bug fixes, e.g. fix crash due to deprecated method. +- `feat`: new features, e.g. add new method to the module. +- `refactor`: code refactor, e.g. migrate from class components to hooks. +- `docs`: changes into documentation, e.g. add usage example for the module.. +- `test`: adding or updating tests, e.g. add integration tests using detox. +- `chore`: tooling changes, e.g. change CI config. + +Our pre-commit hooks verify that your commit message matches this format when committing. + +### Linting and tests + +[ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) + +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. + +Our pre-commit hooks verify that the linter and tests pass when committing. + +### Publishing to npm + +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. + +To publish new versions, run the following: + +```sh +yarn release +``` + +### Scripts + +The `package.json` file contains various scripts for common tasks: + +- `yarn bootstrap`: setup project by installing all dependencies and pods. +- `yarn typecheck`: type-check files with TypeScript. +- `yarn lint`: lint files with ESLint. +- `yarn test`: run unit tests with Jest. +- `yarn example start`: start the Metro server for the example app. +- `yarn example android`: run the example app on Android. +- `yarn example ios`: run the example app on iOS. + +### Sending a pull request + +> **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). + +When you're sending a pull request: + +- Prefer small pull requests focused on one change. +- Verify that linters and tests are passing. +- Review the documentation to make sure it looks good. +- Follow the pull request template when opening a pull request. +- For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..31ed9f1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +MIT License + +Copyright (c) 2023 cawfree +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a818ae3 --- /dev/null +++ b/README.md @@ -0,0 +1,84 @@ +# react-native-webassembly + +This package enables [__WebAssembly__](https://webassembly.org/) for [__React Native__](https://reactnative.dev) powered by C++ [__TurboModules__](https://reactnative.dev/docs/next/the-new-architecture/cxx-cxxturbomodules) and [__Wasm3__](https://github.com/wasm3/wasm3), a fast and universal WebAssembly runtime. + +[`react-native-webassembly`](https://github.com/cawfree/react-native-webassembly) provides React Native applications with the capability to execute universal [__Wasm__](https://webassembly.org/) binaries with native speed. + +> ✏️ This project is still in __active development__. The following tasks are still remaining to be completed: +> +> - Sanitize C++ memory management practices. +> - Normalize execution and result handling of userland `export` functions. +> - Test framework implementation. +> +> [__Pull Requests are welcome!__](https://github.com/cawfree/react-native-webassembly/pulls) 🙏 + +### 📡 Installation + +1. First, ensure your React Native application supports the [__New Architecture__](https://reactnative.dev/docs/new-architecture-intro): + - [__iOS__](https://reactnative.dev/docs/new-architecture-library-ios) + - [__Android__](https://reactnative.dev/docs/new-architecture-library-android) + - At the time of writing, the new architecture is [__not yet enabled__](https://reactnative.dev/docs/next/the-new-architecture/use-app-template#development-environment) for [__Expo__](https://expo.io). +2. Install `react-native-webassembly`: + ```shell + yarn add react-native-webassembly + ``` + +### ✍️ Usage + +The goal of [`react-native-webassembly`](https://github.com/cawfree/react-native-webassembly) is to export a [__browser-equivalent interface__](https://developer.mozilla.org/en-US/docs/WebAssembly) to the WebAssembly API. + +To initialize a new WebAssembly module, we'll need to `instantiate` an module using a buffer populated with a `.wasm` binary. In the snippet below, we show how to download and instantiate the reference [__Hello World__](https://github.com/torch2424/wasm-by-example) example: + +```typescript +import axios from 'axios'; +import * as WebAssembly from 'react-native-webassembly'; + +const { + data: bufferSource, +} = await axios({ + url: 'https://github.com/torch2424/wasm-by-example/raw/master/examples/hello-world/demo/assemblyscript/hello-world.wasm', + method: 'get', + responseType: 'arraybuffer', +}); + +const module = WebAssembly.instantiate<{ + add: (a: number, b: number) => number; +}>(bufferSource); +``` + +You'll notice that in our call to `instantiate`, we can also pass typing information for the `Exports` of the module. In this case, the `hello-world.wasm` binary exports a function to add two numbers, `add`. + +Once configured, we can execute the compiled `wasm` module from our JavaScript code, using the type-safe exported interface: + +```typescript +module.instance.exports.add(1, 2); // 3. +``` + +It's also possible to declare an `importObject` to receive callbacks from the compiled module, which declares a list of callback function implementations which can be invoked by the WebAssembly runtime. + +> **Warning** +> +> Some native modules __require__ the presence of certain function implementations. Without specifying module-specific required dependencies, instantiation will fail. + +For example, the [__Circom__](https://github.com/iden3/circom) library converts arithmetic circuits used for generating, evaluating and verifying [__SNARK__](https://consensys.net/blog/developers/introduction-to-zk-snarks/)s are expressed as WASM modules which require the runtime to define an `exceptionHandler` function. + +It's simple to define an `importObject`: + +```typescript +const module = WebAssembly.instantiate<{ + getVersion: () => number; + getFieldNumLen32: () => number; + // ... +}>(bufferSource, { + imports: { + exceptionHandler: (value: number) => console.error(value), + }, +}); +``` + +Here, we declare an `exceptionHandler` as `imports` to the compiled module. Without declaring this required dependency, the module would fail to compile. + +You can find a working implementation of this process in the [__Example App__](example/src/App.tsx). + +### ✌️ License +[__MIT__](LICENSE) diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt new file mode 100644 index 0000000..9a167a1 --- /dev/null +++ b/android/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.4.1) + +set (CMAKE_VERBOSE_MAKEFILE ON) +set (CMAKE_CXX_STANDARD 11) +set (CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=c++17") + +find_library(log-lib log) + +add_library(cpp + SHARED + ../cpp/react-native-webassembly.cpp + cpp-adapter.cpp + # wasm3 + ../cpp/m3_api_libc.c + ../cpp/m3_api_libc.h + ../cpp/m3_api_meta_wasi.c + ../cpp/m3_api_tracer.c + ../cpp/m3_api_tracer.h + ../cpp/m3_api_uvwasi.c + ../cpp/m3_api_wasi.c + ../cpp/m3_api_wasi.h + ../cpp/m3_bind.c + ../cpp/m3_bind.h + ../cpp/m3_code.c + ../cpp/m3_code.h + ../cpp/m3_compile.c + ../cpp/m3_compile.h + ../cpp/m3_config.h + ../cpp/m3_config_platforms.h + ../cpp/m3_core.c + ../cpp/m3_core.h + ../cpp/m3_env.c + ../cpp/m3_env.h + ../cpp/m3_exception.h + ../cpp/m3_exec.c + ../cpp/m3_exec.h + ../cpp/m3_exec_defs.h + ../cpp/m3_function.c + ../cpp/m3_function.h + ../cpp/m3_info.c + ../cpp/m3_info.h + ../cpp/m3_math_utils.h + ../cpp/m3_module.c + ../cpp/m3_parse.c + ../cpp/react-native-webassembly.h + ../cpp/wasm3.h + ../cpp/wasm3_cpp.h + ../cpp/wasm3_defs.h + ) + +# Specifies a path to native header files. +include_directories( + ../cpp +) + +target_link_libraries( + cpp + ${log-lib} +) diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..0fbacb6 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,99 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath "com.android.tools.build:gradle:7.2.1" + } +} + +def isNewArchitectureEnabled() { + return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" +} + +apply plugin: "com.android.library" + + +def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') } + +if (isNewArchitectureEnabled()) { + apply plugin: "com.facebook.react" +} + +def getExtOrDefault(name) { + return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["Webassembly_" + name] +} + +def getExtOrIntegerDefault(name) { + return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Webassembly_" + name]).toInteger() +} + +android { + ndkVersion getExtOrDefault("ndkVersion") + compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") + + defaultConfig { + minSdkVersion getExtOrIntegerDefault("minSdkVersion") + targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() + externalNativeBuild { + cmake { + cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all" + abiFilters "x86", "x86_64", "armeabi-v7a", "arm64-v8a" + } + } + } + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } + buildTypes { + release { + minifyEnabled false + } + } + + lintOptions { + disable "GradleCompatible" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + sourceSets { + main { + if (isNewArchitectureEnabled()) { + java.srcDirs += [ + // This is needed to build Kotlin project with NewArch enabled + "${project.buildDir}/generated/source/codegen/java" + ] + } + } + } +} + +repositories { + mavenCentral() + google() +} + + +dependencies { + // For < 0.71, this will be from the local maven repo + // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin + //noinspection GradleDynamicVersion + implementation "com.facebook.react:react-native:+" +} + +if (isNewArchitectureEnabled()) { + react { + jsRootDir = file("../src/") + libraryName = "Webassembly" + codegenJavaPackageName = "com.webassembly" + } +} diff --git a/android/cpp-adapter.cpp b/android/cpp-adapter.cpp new file mode 100644 index 0000000..09c90fa --- /dev/null +++ b/android/cpp-adapter.cpp @@ -0,0 +1,84 @@ +#include +#include +#include + +#include "react-native-webassembly.h" + +extern "C" JNIEXPORT jdouble JNICALL +Java_com_webassembly_WebassemblyModule_nativeInstantiate( + JNIEnv *env, + jclass type, + jstring iid, + jbyteArray bufferSource, + jlong bufferSourceLength, + jdouble stackSizeInBytes, + jobjectArray rawFunctions +) { + + std::string iid_str = std::string(env->GetStringUTFChars(iid, NULL)); + + uint8_t* uint8Buffer = new uint8_t[bufferSourceLength]; + memcpy(uint8Buffer, env->GetByteArrayElements(bufferSource, NULL), bufferSourceLength); + + std::vector rawFunctionsVec; + + for (jsize i = 0; i < env->GetArrayLength(rawFunctions); i++) { + jstring str = (jstring) env->GetObjectArrayElement(rawFunctions, i); + const char* rawString = env->GetStringUTFChars(str, 0); + rawFunctionsVec.push_back(std::string(rawString)); + } + + RNWebassemblyInstantiateParams params = { + iid_str, + uint8Buffer, + static_cast(bufferSourceLength), + static_cast(stackSizeInBytes), + &rawFunctionsVec + }; + + return webassembly::instantiate(¶ms); +} + +extern "C" JNIEXPORT jdouble JNICALL +Java_com_webassembly_WebassemblyModule_nativeInvoke( + JNIEnv *env, + jclass cls, + jstring iid, + jstring func, + jobjectArray args, + jobject result +) { + + std::string iid_str = std::string(env->GetStringUTFChars(iid, NULL)); + std::string func_str = std::string(env->GetStringUTFChars(func, NULL)); + + std::vector argsVec; + + for (jsize i = 0; i < env->GetArrayLength(args); i++) { + jstring str = (jstring) env->GetObjectArrayElement(args, i); + const char* rawString = env->GetStringUTFChars(str, 0); + argsVec.push_back(std::string(rawString)); + } + + std::vector resultVec; + + RNWebassemblyInvokeParams params = { + iid_str, + func_str, + &argsVec, + &resultVec + }; + + double xxx = webassembly::invoke(¶ms); + + jclass arrayListClass = env->FindClass("java/util/ArrayList"); + jmethodID arrayListAddMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); + + for (const auto& elem : resultVec) { + jstring jElem = env->NewStringUTF(elem.c_str()); + env->CallBooleanMethod(result, arrayListAddMethod, jElem); + env->DeleteLocalRef(jElem); + } + + return xxx; +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..510f14e --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,5 @@ +Webassembly_kotlinVersion=1.7.0 +Webassembly_minSdkVersion=21 +Webassembly_targetSdkVersion=31 +Webassembly_compileSdkVersion=31 +Webassembly_ndkversion=21.4.7075529 diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3070612 --- /dev/null +++ b/android/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/android/src/main/java/com/webassembly/WebassemblyModule.java b/android/src/main/java/com/webassembly/WebassemblyModule.java new file mode 100644 index 0000000..89283bb --- /dev/null +++ b/android/src/main/java/com/webassembly/WebassemblyModule.java @@ -0,0 +1,84 @@ +package com.webassembly; + +import android.util.Base64; + +import androidx.annotation.NonNull; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableNativeArray; +import com.facebook.react.module.annotations.ReactModule; + +import java.util.ArrayList; + +@ReactModule(name = WebassemblyModule.NAME) +public class WebassemblyModule extends NativeWebassemblySpec { + public static final String NAME = "Webassembly"; + + public WebassemblyModule(ReactApplicationContext reactContext) { + super(reactContext); + } + + @Override + @NonNull + public String getName() { + return NAME; + } + + static { + System.loadLibrary("cpp"); + } + + private static native double nativeInstantiate(String iid, byte[] bufferSource, long bufferSourceLength, double stackSizeInBytes, String[] rawFunctions); + + private static native double nativeInvoke(String iid, String func, String[] args, ArrayList result); + + @Override public final double instantiate(final ReadableMap pParams) { + final String iid = pParams.getString("iid"); + final String bufferSource = pParams.getString("bufferSource"); + final int stackSizeInBytes = pParams.getInt("stackSizeInBytes"); + final ReadableArray rawFunctionsArray = pParams.getArray("rawFunctions"); + + final String[] rawFunctions = new String[rawFunctionsArray.size()]; + + for (int i = 0; i < rawFunctionsArray.size(); i += 1) { + rawFunctions[i] = rawFunctionsArray.getString(i); + } + + final byte[] bufferSourceBytes = Base64.decode(bufferSource, Base64.DEFAULT); + + return nativeInstantiate( + iid, + bufferSourceBytes, + bufferSourceBytes.length, + stackSizeInBytes, + rawFunctions + ); + } + + @Override public final WritableArray invoke(final ReadableMap pParams) { + final String iid = pParams.getString("iid"); + final String func = pParams.getString("func"); + final ReadableArray args = pParams.getArray("args"); + + final ArrayList result = new ArrayList<>(); + + final String[] args2 = new String[args.size()]; + + for (int i = 0; i < args.size(); i += 1) + args2[i] = args.getString(i); + + if (nativeInvoke(iid, func, args2, result) != 0) + throw new RuntimeException("Invocation of " + func + " failed."); + + final WritableArray writableArray = new WritableNativeArray(); + + for (int i = 0; i < result.size(); i += 1) + writableArray.pushString(result.get(i)); + + return writableArray; + } + +} diff --git a/android/src/main/java/com/webassembly/WebassemblyPackage.java b/android/src/main/java/com/webassembly/WebassemblyPackage.java new file mode 100644 index 0000000..176dbbb --- /dev/null +++ b/android/src/main/java/com/webassembly/WebassemblyPackage.java @@ -0,0 +1,44 @@ +package com.webassembly; + +import androidx.annotation.Nullable; + +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.module.model.ReactModuleInfo; +import com.facebook.react.module.model.ReactModuleInfoProvider; +import com.facebook.react.TurboReactPackage; + +import java.util.HashMap; +import java.util.Map; + +public class WebassemblyPackage extends TurboReactPackage { + + @Nullable + @Override + public NativeModule getModule(String name, ReactApplicationContext reactContext) { + if (name.equals(WebassemblyModule.NAME)) { + return new WebassemblyModule(reactContext); + } else { + return null; + } + } + + @Override + public ReactModuleInfoProvider getReactModuleInfoProvider() { + return () -> { + final Map moduleInfos = new HashMap<>(); + moduleInfos.put( + WebassemblyModule.NAME, + new ReactModuleInfo( + WebassemblyModule.NAME, + WebassemblyModule.NAME, + false, // canOverrideExistingModule + false, // needsEagerInit + true, // hasConstants + false, // isCxxModule + true // isTurboModule + )); + return moduleInfos; + }; + } +} diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..f842b77 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['module:metro-react-native-babel-preset'], +}; diff --git a/cpp/m3_api_libc.c b/cpp/m3_api_libc.c new file mode 100644 index 0000000..1c9649a --- /dev/null +++ b/cpp/m3_api_libc.c @@ -0,0 +1,245 @@ +// +// m3_api_libc.c +// +// Created by Volodymyr Shymanskyy on 11/20/19. +// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. +// + +#define _POSIX_C_SOURCE 200809L + +#include "m3_api_libc.h" + +#include "m3_env.h" +#include "m3_exception.h" + +#include +#include +#include + +typedef uint32_t wasm_ptr_t; +typedef uint32_t wasm_size_t; + +m3ApiRawFunction(m3_libc_abort) +{ + m3ApiTrap(m3Err_trapAbort); +} + +m3ApiRawFunction(m3_libc_exit) +{ + m3ApiGetArg (int32_t, code) + + m3ApiTrap(m3Err_trapExit); +} + + +m3ApiRawFunction(m3_libc_memset) +{ + m3ApiReturnType (int32_t) + + m3ApiGetArgMem (void*, i_ptr) + m3ApiGetArg (int32_t, i_value) + m3ApiGetArg (wasm_size_t, i_size) + + m3ApiCheckMem(i_ptr, i_size); + + u32 result = m3ApiPtrToOffset(memset (i_ptr, i_value, i_size)); + m3ApiReturn(result); +} + +m3ApiRawFunction(m3_libc_memmove) +{ + m3ApiReturnType (int32_t) + + m3ApiGetArgMem (void*, o_dst) + m3ApiGetArgMem (void*, i_src) + m3ApiGetArg (wasm_size_t, i_size) + + m3ApiCheckMem(o_dst, i_size); + m3ApiCheckMem(i_src, i_size); + + u32 result = m3ApiPtrToOffset(memmove (o_dst, i_src, i_size)); + m3ApiReturn(result); +} + +m3ApiRawFunction(m3_libc_print) +{ + m3ApiReturnType (uint32_t) + + m3ApiGetArgMem (void*, i_ptr) + m3ApiGetArg (wasm_size_t, i_size) + + m3ApiCheckMem(i_ptr, i_size); + + fwrite(i_ptr, i_size, 1, stdout); + fflush(stdout); + + m3ApiReturn(i_size); +} + +static +void internal_itoa(int n, char s[], int radix) +{ + static char const HEXDIGITS[0x10] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + int i, j, sign; + char c; + + if ((sign = n) < 0) { n = -n; } + i = 0; + do { + s[i++] = HEXDIGITS[n % radix]; + } while ((n /= radix) > 0); + + if (sign < 0) { s[i++] = '-'; } + s[i] = '\0'; + + // reverse + for (i = 0, j = strlen(s)-1; i +# define USE_NEW_WASI +# define WASI_STAT_FIELD(f) f + +#elif __has_include("wasi/core.h") +# warning "Using legacy WASI headers" +# include +# define __WASI_ERRNO_SUCCESS __WASI_ESUCCESS +# define __WASI_ERRNO_INVAL __WASI_EINVAL +# define WASI_STAT_FIELD(f) st_##f + +#else +# error "Missing WASI headers" +#endif + +static m3_wasi_context_t* wasi_context; + +typedef size_t __wasi_size_t; + +static inline +const void* copy_iov_to_host(IM3Runtime runtime, void* _mem, __wasi_iovec_t* host_iov, __wasi_iovec_t* wasi_iov, int32_t iovs_len) +{ + // Convert wasi memory offsets to host addresses + for (int i = 0; i < iovs_len; i++) { + host_iov[i].buf = m3ApiOffsetToPtr(wasi_iov[i].buf); + host_iov[i].buf_len = wasi_iov[i].buf_len; + m3ApiCheckMem(host_iov[i].buf, host_iov[i].buf_len); + } + m3ApiSuccess(); +} + +#if d_m3EnableWasiTracing + +const char* wasi_errno2str(__wasi_errno_t err) +{ + switch (err) { + case 0: return "ESUCCESS"; + case 1: return "E2BIG"; + case 2: return "EACCES"; + case 3: return "EADDRINUSE"; + case 4: return "EADDRNOTAVAIL"; + case 5: return "EAFNOSUPPORT"; + case 6: return "EAGAIN"; + case 7: return "EALREADY"; + case 8: return "EBADF"; + case 9: return "EBADMSG"; + case 10: return "EBUSY"; + case 11: return "ECANCELED"; + case 12: return "ECHILD"; + case 13: return "ECONNABORTED"; + case 14: return "ECONNREFUSED"; + case 15: return "ECONNRESET"; + case 16: return "EDEADLK"; + case 17: return "EDESTADDRREQ"; + case 18: return "EDOM"; + case 19: return "EDQUOT"; + case 20: return "EEXIST"; + case 21: return "EFAULT"; + case 22: return "EFBIG"; + case 23: return "EHOSTUNREACH"; + case 24: return "EIDRM"; + case 25: return "EILSEQ"; + case 26: return "EINPROGRESS"; + case 27: return "EINTR"; + case 28: return "EINVAL"; + case 29: return "EIO"; + case 30: return "EISCONN"; + case 31: return "EISDIR"; + case 32: return "ELOOP"; + case 33: return "EMFILE"; + case 34: return "EMLINK"; + case 35: return "EMSGSIZE"; + case 36: return "EMULTIHOP"; + case 37: return "ENAMETOOLONG"; + case 38: return "ENETDOWN"; + case 39: return "ENETRESET"; + case 40: return "ENETUNREACH"; + case 41: return "ENFILE"; + case 42: return "ENOBUFS"; + case 43: return "ENODEV"; + case 44: return "ENOENT"; + case 45: return "ENOEXEC"; + case 46: return "ENOLCK"; + case 47: return "ENOLINK"; + case 48: return "ENOMEM"; + case 49: return "ENOMSG"; + case 50: return "ENOPROTOOPT"; + case 51: return "ENOSPC"; + case 52: return "ENOSYS"; + case 53: return "ENOTCONN"; + case 54: return "ENOTDIR"; + case 55: return "ENOTEMPTY"; + case 56: return "ENOTRECOVERABLE"; + case 57: return "ENOTSOCK"; + case 58: return "ENOTSUP"; + case 59: return "ENOTTY"; + case 60: return "ENXIO"; + case 61: return "EOVERFLOW"; + case 62: return "EOWNERDEAD"; + case 63: return "EPERM"; + case 64: return "EPIPE"; + case 65: return "EPROTO"; + case 66: return "EPROTONOSUPPORT"; + case 67: return "EPROTOTYPE"; + case 68: return "ERANGE"; + case 69: return "EROFS"; + case 70: return "ESPIPE"; + case 71: return "ESRCH"; + case 72: return "ESTALE"; + case 73: return "ETIMEDOUT"; + case 74: return "ETXTBSY"; + case 75: return "EXDEV"; + case 76: return "ENOTCAPABLE"; + default: return ""; + } +} + +const char* wasi_whence2str(__wasi_whence_t whence) +{ + switch (whence) { + case __WASI_WHENCE_SET: return "SET"; + case __WASI_WHENCE_CUR: return "CUR"; + case __WASI_WHENCE_END: return "END"; + default: return ""; + } +} + +# define WASI_TRACE(fmt, ...) { fprintf(stderr, "%s " fmt, __FUNCTION__+16, ##__VA_ARGS__); fprintf(stderr, " => %s\n", wasi_errno2str(ret)); } +#else +# define WASI_TRACE(fmt, ...) +#endif + +/* + * WASI API implementation + */ + +m3ApiRawFunction(m3_wasi_generic_args_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (uint32_t * , argv) + m3ApiGetArgMem (char * , argv_buf) + + m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata); + + if (context == NULL) { m3ApiReturn(__WASI_ERRNO_INVAL); } + + m3ApiCheckMem(argv, context->argc * sizeof(uint32_t)); + + for (u32 i = 0; i < context->argc; ++i) + { + m3ApiWriteMem32(&argv[i], m3ApiPtrToOffset(argv_buf)); + + size_t len = strlen (context->argv[i]); + + m3ApiCheckMem(argv_buf, len); + memcpy (argv_buf, context->argv[i], len); + argv_buf += len; + * argv_buf++ = 0; + } + + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_args_sizes_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (__wasi_size_t * , argc) + m3ApiGetArgMem (__wasi_size_t * , argv_buf_size) + + m3ApiCheckMem(argc, sizeof(__wasi_size_t)); + m3ApiCheckMem(argv_buf_size, sizeof(__wasi_size_t)); + + m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata); + + if (context == NULL) { m3ApiReturn(__WASI_ERRNO_INVAL); } + + __wasi_size_t buf_len = 0; + for (u32 i = 0; i < context->argc; ++i) + { + buf_len += strlen (context->argv[i]) + 1; + } + + m3ApiWriteMem32(argc, context->argc); + m3ApiWriteMem32(argv_buf_size, buf_len); + + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_environ_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (uint32_t * , env) + m3ApiGetArgMem (char * , env_buf) + + __wasi_errno_t ret; + __wasi_size_t env_count, env_buf_size; + + ret = __wasi_environ_sizes_get(&env_count, &env_buf_size); + if (ret != __WASI_ERRNO_SUCCESS) m3ApiReturn(ret); + + m3ApiCheckMem(env, env_count * sizeof(uint32_t)); + m3ApiCheckMem(env_buf, env_buf_size); + + ret = __wasi_environ_get(env, env_buf); + if (ret != __WASI_ERRNO_SUCCESS) m3ApiReturn(ret); + + for (u32 i = 0; i < env_count; ++i) { + env[i] = m3ApiPtrToOffset (env[i]); + } + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_environ_sizes_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (__wasi_size_t * , env_count) + m3ApiGetArgMem (__wasi_size_t * , env_buf_size) + + m3ApiCheckMem(env_count, sizeof(__wasi_size_t)); + m3ApiCheckMem(env_buf_size, sizeof(__wasi_size_t)); + + __wasi_errno_t ret = __wasi_environ_sizes_get(env_count, env_buf_size); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_prestat_dir_name) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (char * , path) + m3ApiGetArg (__wasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + __wasi_errno_t ret = __wasi_fd_prestat_dir_name(fd, path, path_len); + + WASI_TRACE("fd:%d, len:%d | path:%s", fd, path_len, path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_prestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (__wasi_prestat_t * , buf) + + m3ApiCheckMem(buf, sizeof(__wasi_prestat_t)); + + __wasi_errno_t ret = __wasi_fd_prestat_get(fd, buf); + + WASI_TRACE("fd:%d | type:%d, name_len:%d", fd, buf->pr_type, buf->u.dir.pr_name_len); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (__wasi_fdstat_t * , fdstat) + + m3ApiCheckMem(fdstat, sizeof(__wasi_fdstat_t)); + + __wasi_errno_t ret = __wasi_fd_fdstat_get(fd, fdstat); + + WASI_TRACE("fd:%d", fd); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_set_flags) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_fdflags_t , flags) + + __wasi_errno_t ret = __wasi_fd_fdstat_set_flags(fd, flags); + + WASI_TRACE("fd:%d, flags:0x%x", fd, flags); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_set_rights) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_rights_t , rights_base) + m3ApiGetArg (__wasi_rights_t , rights_inheriting) + +#if 0 + __wasi_errno_t ret = __wasi_fd_fdstat_set_rights(fd, rights_base, rights_inheriting); +#else + __wasi_errno_t ret = __WASI_ERRNO_INVAL; +#endif + + WASI_TRACE("fd:%d, base:0x%" PRIx64 ", inheriting:0x%" PRIx64, fd, rights_base, rights_inheriting); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_filestat_set_size) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_filesize_t , size) + + __wasi_errno_t ret = __wasi_fd_filestat_set_size(fd, size); + + WASI_TRACE("fd:%d, size:%" PRIu64, fd, size); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_filestat_set_times) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_timestamp_t , atim) + m3ApiGetArg (__wasi_timestamp_t , mtim) + m3ApiGetArg (__wasi_fstflags_t , fst_flags) + + __wasi_errno_t ret = __wasi_fd_filestat_set_times(fd, atim, mtim, fst_flags); + + WASI_TRACE("fd:%d, atim:%" PRIu64 ", mtim:%" PRIu64 ", flags:%d", fd, atim, mtim, fst_flags); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_unstable_fd_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(buf, 56); // wasi_filestat_t + + __wasi_filestat_t stat; + + __wasi_errno_t ret = __wasi_fd_filestat_get(fd, &stat); + + WASI_TRACE("fd:%d | fs.size:%" PRIu64, fd, stat.WASI_STAT_FIELD(size)); + + if (ret != __WASI_ERRNO_SUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 56); + m3ApiWriteMem64(buf+0, stat.WASI_STAT_FIELD(dev)); + m3ApiWriteMem64(buf+8, stat.WASI_STAT_FIELD(ino)); + m3ApiWriteMem8 (buf+16, stat.WASI_STAT_FIELD(filetype)); + m3ApiWriteMem32(buf+20, stat.WASI_STAT_FIELD(nlink)); + m3ApiWriteMem64(buf+24, stat.WASI_STAT_FIELD(size)); + m3ApiWriteMem64(buf+32, stat.WASI_STAT_FIELD(atim)); + m3ApiWriteMem64(buf+40, stat.WASI_STAT_FIELD(mtim)); + m3ApiWriteMem64(buf+48, stat.WASI_STAT_FIELD(ctim)); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(buf, 64); // wasi_filestat_t + + __wasi_filestat_t stat; + + __wasi_errno_t ret = __wasi_fd_filestat_get(fd, &stat); + + WASI_TRACE("fd:%d | fs.size:%" PRIu64, fd, stat.WASI_STAT_FIELD(size)); + + if (ret != __WASI_ERRNO_SUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 64); + m3ApiWriteMem64(buf+0, stat.WASI_STAT_FIELD(dev)); + m3ApiWriteMem64(buf+8, stat.WASI_STAT_FIELD(ino)); + m3ApiWriteMem8 (buf+16, stat.WASI_STAT_FIELD(filetype)); + m3ApiWriteMem64(buf+24, stat.WASI_STAT_FIELD(nlink)); + m3ApiWriteMem64(buf+32, stat.WASI_STAT_FIELD(size)); + m3ApiWriteMem64(buf+40, stat.WASI_STAT_FIELD(atim)); + m3ApiWriteMem64(buf+48, stat.WASI_STAT_FIELD(mtim)); + m3ApiWriteMem64(buf+56, stat.WASI_STAT_FIELD(ctim)); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_unstable_fd_seek) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_filedelta_t , offset) + m3ApiGetArg (uint32_t , wasi_whence) + m3ApiGetArgMem (__wasi_filesize_t * , result) + + m3ApiCheckMem(result, sizeof(__wasi_filesize_t)); + + __wasi_whence_t whence = -1; + switch (wasi_whence) { + case 0: whence = __WASI_WHENCE_CUR; break; + case 1: whence = __WASI_WHENCE_END; break; + case 2: whence = __WASI_WHENCE_SET; break; + } + + __wasi_filesize_t pos; + __wasi_errno_t ret = __wasi_fd_seek(fd, offset, whence, &pos); + + WASI_TRACE("fd:%d, offset:%" PRIu64 ", whence:%s | result:%" PRIu64, + fd, offset, wasi_whence2str(whence), pos); + + *result = pos; + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_seek) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_filedelta_t , offset) + m3ApiGetArg (uint32_t , wasi_whence) + m3ApiGetArgMem (__wasi_filesize_t * , result) + + m3ApiCheckMem(result, sizeof(__wasi_filesize_t)); + + __wasi_whence_t whence = -1; + switch (wasi_whence) { + case 0: whence = __WASI_WHENCE_SET; break; + case 1: whence = __WASI_WHENCE_CUR; break; + case 2: whence = __WASI_WHENCE_END; break; + } + + __wasi_filesize_t pos; + __wasi_errno_t ret = __wasi_fd_seek(fd, offset, whence, &pos); + + WASI_TRACE("fd:%d, offset:%" PRIu64 ", whence:%s | result:%" PRIu64, + fd, offset, wasi_whence2str(whence), pos); + + *result = pos; + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_renumber) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , from) + m3ApiGetArg (__wasi_fd_t , to) + + __wasi_errno_t ret = __wasi_fd_renumber(from, to); + + WASI_TRACE("from:%d, to:%d", from, to); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_sync) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + + __wasi_errno_t ret = __wasi_fd_sync(fd); + + WASI_TRACE("fd:%d", fd); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_tell) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (__wasi_filesize_t * , result) + + m3ApiCheckMem(result, sizeof(__wasi_filesize_t)); + + __wasi_filesize_t pos; + __wasi_errno_t ret = __wasi_fd_tell(fd, &pos); + + WASI_TRACE("fd:%d | result:%" PRIu64, fd, pos); + + *result = pos; + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_create_directory) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (__wasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + __wasi_errno_t ret = __wasi_path_create_directory(fd, path, path_len); + + WASI_TRACE("fd:%d, path:%s", fd, path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_readlink) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (__wasi_size_t , path_len) + m3ApiGetArgMem (char * , buf) + m3ApiGetArg (__wasi_size_t , buf_len) + m3ApiGetArgMem (__wasi_size_t * , bufused) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(buf, buf_len); + m3ApiCheckMem(bufused, sizeof(__wasi_size_t)); + + __wasi_errno_t ret = __wasi_path_readlink(fd, path, path_len, buf, buf_len, bufused); + + WASI_TRACE("fd:%d, path:%s | buf:%s, bufused:%d", fd, path, buf, *bufused); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_remove_directory) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (__wasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + __wasi_errno_t ret = __wasi_path_remove_directory(fd, path, path_len); + + WASI_TRACE("fd:%d, path:%s", fd, path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_rename) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , old_fd) + m3ApiGetArgMem (const char * , old_path) + m3ApiGetArg (__wasi_size_t , old_path_len) + m3ApiGetArg (__wasi_fd_t , new_fd) + m3ApiGetArgMem (const char * , new_path) + m3ApiGetArg (__wasi_size_t , new_path_len) + + m3ApiCheckMem(old_path, old_path_len); + m3ApiCheckMem(new_path, new_path_len); + + __wasi_errno_t ret = __wasi_path_rename(old_fd, old_path, old_path_len, + new_fd, new_path, new_path_len); + + WASI_TRACE("old_fd:%d, old_path:%s, new_fd:%d, new_path:%s", old_fd, old_path, new_fd, new_path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_symlink) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (const char * , old_path) + m3ApiGetArg (__wasi_size_t , old_path_len) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (const char * , new_path) + m3ApiGetArg (__wasi_size_t , new_path_len) + + m3ApiCheckMem(old_path, old_path_len); + m3ApiCheckMem(new_path, new_path_len); + + __wasi_errno_t ret = __wasi_path_symlink(old_path, old_path_len, + fd, new_path, new_path_len); + + WASI_TRACE("old_path:%s, fd:%d, new_path:%s", old_path, fd, new_path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_unlink_file) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (__wasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + __wasi_errno_t ret = __wasi_path_unlink_file(fd, path, path_len); + + WASI_TRACE("fd:%d, path:%s", fd, path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_open) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , dirfd) + m3ApiGetArg (__wasi_lookupflags_t , dirflags) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (__wasi_size_t , path_len) + m3ApiGetArg (__wasi_oflags_t , oflags) + m3ApiGetArg (__wasi_rights_t , fs_rights_base) + m3ApiGetArg (__wasi_rights_t , fs_rights_inheriting) + m3ApiGetArg (__wasi_fdflags_t , fs_flags) + m3ApiGetArgMem (__wasi_fd_t * , fd) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(fd, sizeof(__wasi_fd_t)); + + __wasi_errno_t ret = __wasi_path_open(dirfd, + dirflags, + path, + path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd); + + WASI_TRACE("dirfd:%d, dirflags:0x%x, path:%s, oflags:0x%x, fs_flags:0x%x | fd:%d", dirfd, dirflags, path, oflags, fs_flags, *fd); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_unstable_path_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_lookupflags_t , flags) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uint32_t , path_len) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(buf, 56); // wasi_filestat_t + + __wasi_filestat_t stat; + + __wasi_errno_t ret = __wasi_path_filestat_get(fd, flags, path, path_len, &stat); + + WASI_TRACE("fd:%d, flags:0x%x, path:%s | fs.size:%" PRIu64, fd, flags, path, stat.WASI_STAT_FIELD(size)); + + if (ret != __WASI_ERRNO_SUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 56); + m3ApiWriteMem64(buf+0, stat.WASI_STAT_FIELD(dev)); + m3ApiWriteMem64(buf+8, stat.WASI_STAT_FIELD(ino)); + m3ApiWriteMem8 (buf+16, stat.WASI_STAT_FIELD(filetype)); + m3ApiWriteMem32(buf+20, stat.WASI_STAT_FIELD(nlink)); + m3ApiWriteMem64(buf+24, stat.WASI_STAT_FIELD(size)); + m3ApiWriteMem64(buf+32, stat.WASI_STAT_FIELD(atim)); + m3ApiWriteMem64(buf+40, stat.WASI_STAT_FIELD(mtim)); + m3ApiWriteMem64(buf+48, stat.WASI_STAT_FIELD(ctim)); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_path_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_lookupflags_t , flags) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uint32_t , path_len) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(buf, 64); // wasi_filestat_t + + __wasi_filestat_t stat; + + __wasi_errno_t ret = __wasi_path_filestat_get(fd, flags, path, path_len, &stat); + + WASI_TRACE("fd:%d, flags:0x%x, path:%s | fs.size:%" PRIu64, fd, flags, path, stat.WASI_STAT_FIELD(size)); + + if (ret != __WASI_ERRNO_SUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 64); + m3ApiWriteMem64(buf+0, stat.WASI_STAT_FIELD(dev)); + m3ApiWriteMem64(buf+8, stat.WASI_STAT_FIELD(ino)); + m3ApiWriteMem8 (buf+16, stat.WASI_STAT_FIELD(filetype)); + m3ApiWriteMem64(buf+24, stat.WASI_STAT_FIELD(nlink)); + m3ApiWriteMem64(buf+32, stat.WASI_STAT_FIELD(size)); + m3ApiWriteMem64(buf+40, stat.WASI_STAT_FIELD(atim)); + m3ApiWriteMem64(buf+48, stat.WASI_STAT_FIELD(mtim)); + m3ApiWriteMem64(buf+56, stat.WASI_STAT_FIELD(ctim)); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_pread) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (__wasi_iovec_t * , wasi_iovs) + m3ApiGetArg (__wasi_size_t , iovs_len) + m3ApiGetArg (__wasi_filesize_t , offset) + m3ApiGetArgMem (__wasi_size_t * , nread) + + m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(__wasi_iovec_t)); + m3ApiCheckMem(nread, sizeof(__wasi_size_t)); + + __wasi_iovec_t iovs[iovs_len]; + const void* mem_check = copy_iov_to_host(runtime, _mem, iovs, wasi_iovs, iovs_len); + if (mem_check != m3Err_none) { + return mem_check; + } + + __wasi_errno_t ret = __wasi_fd_pread(fd, iovs, iovs_len, offset, nread); + + WASI_TRACE("fd:%d | nread:%d", fd, *nread); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_read) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (__wasi_iovec_t * , wasi_iovs) + m3ApiGetArg (__wasi_size_t , iovs_len) + m3ApiGetArgMem (__wasi_size_t * , nread) + + m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(__wasi_iovec_t)); + m3ApiCheckMem(nread, sizeof(__wasi_size_t)); + + __wasi_iovec_t iovs[iovs_len]; + const void* mem_check = copy_iov_to_host(runtime, _mem, iovs, wasi_iovs, iovs_len); + if (mem_check != m3Err_none) { + return mem_check; + } + + __wasi_errno_t ret = __wasi_fd_read(fd, iovs, iovs_len, nread); + + WASI_TRACE("fd:%d | nread:%d", fd, *nread); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_write) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (__wasi_iovec_t * , wasi_iovs) + m3ApiGetArg (__wasi_size_t , iovs_len) + m3ApiGetArgMem (__wasi_size_t * , nwritten) + + m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(__wasi_iovec_t)); + m3ApiCheckMem(nwritten, sizeof(__wasi_size_t)); + + __wasi_iovec_t iovs[iovs_len]; + const void* mem_check = copy_iov_to_host(runtime, _mem, iovs, wasi_iovs, iovs_len); + if (mem_check != m3Err_none) { + return mem_check; + } + + __wasi_errno_t ret = __wasi_fd_write(fd, (__wasi_ciovec_t*)iovs, iovs_len, nwritten); + + WASI_TRACE("fd:%d | nwritten:%d", fd, *nwritten); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_pwrite) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (__wasi_iovec_t * , wasi_iovs) + m3ApiGetArg (__wasi_size_t , iovs_len) + m3ApiGetArg (__wasi_filesize_t , offset) + m3ApiGetArgMem (__wasi_size_t * , nwritten) + + m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(__wasi_iovec_t)); + m3ApiCheckMem(nwritten, sizeof(__wasi_size_t)); + + __wasi_iovec_t iovs[iovs_len]; + const void* mem_check = copy_iov_to_host(runtime, _mem, iovs, wasi_iovs, iovs_len); + if (mem_check != m3Err_none) { + return mem_check; + } + + __wasi_errno_t ret = __wasi_fd_pwrite(fd, (__wasi_ciovec_t*)iovs, iovs_len, offset, nwritten); + + WASI_TRACE("fd:%d | nwritten:%d", fd, *nwritten); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_readdir) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (void * , buf) + m3ApiGetArg (__wasi_size_t , buf_len) + m3ApiGetArg (__wasi_dircookie_t , cookie) + m3ApiGetArgMem (__wasi_size_t * , bufused) + + m3ApiCheckMem(buf, buf_len); + m3ApiCheckMem(bufused, sizeof(__wasi_size_t)); + + __wasi_errno_t ret = __wasi_fd_readdir(fd, buf, buf_len, cookie, bufused); + + WASI_TRACE("fd:%d | bufused:%d", fd, *bufused); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_advise) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_filesize_t , offset) + m3ApiGetArg (__wasi_filesize_t , length) + m3ApiGetArg (__wasi_advice_t , advice) + + __wasi_errno_t ret = __wasi_fd_advise(fd, offset, length, advice); + + WASI_TRACE("fd:%d, offset:%" PRIu64 ", length:%" PRIu64 ", advice:%d", fd, offset, length, advice); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_allocate) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_filesize_t , offset) + m3ApiGetArg (__wasi_filesize_t , length) + + __wasi_errno_t ret = __wasi_fd_allocate(fd, offset, length); + + WASI_TRACE("fd:%d, offset:%" PRIu64 ", length:%" PRIu64, fd, offset, length); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_close) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t, fd) + + __wasi_errno_t ret = __wasi_fd_close(fd); + + WASI_TRACE("fd:%d", fd); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_datasync) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t, fd) + + __wasi_errno_t ret = __wasi_fd_datasync(fd); + + WASI_TRACE("fd:%d", fd); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_random_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (uint8_t * , buf) + m3ApiGetArg (__wasi_size_t , buf_len) + + m3ApiCheckMem(buf, buf_len); + + __wasi_errno_t ret = __wasi_random_get(buf, buf_len); + + WASI_TRACE("len:%d", buf_len); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_clock_res_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_clockid_t , wasi_clk_id) + m3ApiGetArgMem (__wasi_timestamp_t * , resolution) + + m3ApiCheckMem(resolution, sizeof(__wasi_timestamp_t)); + + __wasi_timestamp_t t; + __wasi_errno_t ret = __wasi_clock_res_get(wasi_clk_id, &t); + + WASI_TRACE("clk_id:%d | res:%" PRIu64, wasi_clk_id, t); + + *resolution = t; + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_clock_time_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_clockid_t , wasi_clk_id) + m3ApiGetArg (__wasi_timestamp_t , precision) + m3ApiGetArgMem (__wasi_timestamp_t * , time) + + m3ApiCheckMem(time, sizeof(__wasi_timestamp_t)); + + __wasi_timestamp_t t; + __wasi_errno_t ret = __wasi_clock_time_get(wasi_clk_id, precision, &t); + + WASI_TRACE("clk_id:%d | res:%" PRIu64, wasi_clk_id, t); + + *time = t; + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_poll_oneoff) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (const __wasi_subscription_t * , in) + m3ApiGetArgMem (__wasi_event_t * , out) + m3ApiGetArg (__wasi_size_t , nsubscriptions) + m3ApiGetArgMem (__wasi_size_t * , nevents) + + m3ApiCheckMem(in, nsubscriptions * sizeof(__wasi_subscription_t)); + m3ApiCheckMem(out, nsubscriptions * sizeof(__wasi_event_t)); + m3ApiCheckMem(nevents, sizeof(__wasi_size_t)); + + // TODO: unstable/snapshot_preview1 compatibility + + __wasi_errno_t ret = __wasi_poll_oneoff(in, out, nsubscriptions, nevents); + + WASI_TRACE("nsubscriptions:%d | nevents:%d", nsubscriptions, *nevents); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_proc_exit) +{ + m3ApiGetArg (uint32_t, code) + + m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata); + + if (context) { + context->exit_code = code; + } + + m3ApiTrap(m3Err_trapExit); +} + +m3ApiRawFunction(m3_wasi_generic_proc_raise) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_signal_t, sig) + + __wasi_errno_t ret = __WASI_ERRNO_INVAL; +#if 0 + ret = __wasi_proc_raise(sig); +#endif + + WASI_TRACE("sig:%d", sig); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_sched_yield) +{ + m3ApiReturnType (uint32_t) + __wasi_errno_t ret = __wasi_sched_yield(); + + WASI_TRACE(""); + + m3ApiReturn(ret); +} + + +static +M3Result SuppressLookupFailure(M3Result i_result) +{ + if (i_result == m3Err_functionLookupFailed) + return m3Err_none; + else + return i_result; +} + +m3_wasi_context_t* m3_GetWasiContext() +{ + return wasi_context; +} + + +M3Result m3_LinkWASI (IM3Module module) +{ + M3Result result = m3Err_none; + + if (!wasi_context) { + wasi_context = (m3_wasi_context_t*)malloc(sizeof(m3_wasi_context_t)); + wasi_context->exit_code = 0; + wasi_context->argc = 0; + wasi_context->argv = 0; + } + + static const char* namespaces[2] = { "wasi_unstable", "wasi_snapshot_preview1" }; + + // Some functions are incompatible between WASI versions +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_seek", "i(iIi*)", &m3_wasi_unstable_fd_seek))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_seek", "i(iIi*)", &m3_wasi_snapshot_preview1_fd_seek))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_filestat_get", "i(i*)", &m3_wasi_unstable_fd_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_filestat_get", "i(i*)", &m3_wasi_snapshot_preview1_fd_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "path_filestat_get", "i(ii*i*)", &m3_wasi_unstable_path_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "path_filestat_get", "i(ii*i*)", &m3_wasi_snapshot_preview1_path_filestat_get))); + + for (int i=0; i<2; i++) + { + const char* wasi = namespaces[i]; + +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_get", "i(**)", &m3_wasi_generic_args_get, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_sizes_get", "i(**)", &m3_wasi_generic_args_sizes_get, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_res_get", "i(i*)", &m3_wasi_generic_clock_res_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_time_get", "i(iI*)", &m3_wasi_generic_clock_time_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_get", "i(**)", &m3_wasi_generic_environ_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_sizes_get", "i(**)", &m3_wasi_generic_environ_sizes_get))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_advise", "i(iIIi)", &m3_wasi_generic_fd_advise))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_allocate", "i(iII)", &m3_wasi_generic_fd_allocate))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_close", "i(i)", &m3_wasi_generic_fd_close))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_datasync", "i(i)", &m3_wasi_generic_fd_datasync))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_get", "i(i*)", &m3_wasi_generic_fd_fdstat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_flags", "i(ii)", &m3_wasi_generic_fd_fdstat_set_flags))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_rights", "i(iII)", &m3_wasi_generic_fd_fdstat_set_rights))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_size", "i(iI)", &m3_wasi_generic_fd_filestat_set_size))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_times","i(iIIi)", &m3_wasi_generic_fd_filestat_set_times))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pread", "i(i*iI*)",&m3_wasi_generic_fd_pread))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_get", "i(i*)", &m3_wasi_generic_fd_prestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_dir_name", "i(i*i)", &m3_wasi_generic_fd_prestat_dir_name))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pwrite", "i(i*iI*)",&m3_wasi_generic_fd_pwrite))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_read", "i(i*i*)", &m3_wasi_generic_fd_read))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_readdir", "i(i*iI*)",&m3_wasi_generic_fd_readdir))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_renumber", "i(ii)", &m3_wasi_generic_fd_renumber))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_sync", "i(i)", &m3_wasi_generic_fd_sync))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_tell", "i(i*)", &m3_wasi_generic_fd_tell))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_write", "i(i*i*)", &m3_wasi_generic_fd_write))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_create_directory", "i(i*i)", &m3_wasi_generic_path_create_directory))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_set_times", "i(ii*iIIi)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_link", "i(ii*ii*i)", ))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_open", "i(ii*iiIIi*)", &m3_wasi_generic_path_open))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_readlink", "i(i*i*i*)", &m3_wasi_generic_path_readlink))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_remove_directory", "i(i*i)", &m3_wasi_generic_path_remove_directory))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_rename", "i(i*ii*i)", &m3_wasi_generic_path_rename))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_symlink", "i(*ii*i)", &m3_wasi_generic_path_symlink))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_unlink_file", "i(i*i)", &m3_wasi_generic_path_unlink_file))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "poll_oneoff", "i(**i*)", &m3_wasi_generic_poll_oneoff))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "proc_exit", "v(i)", &m3_wasi_generic_proc_exit, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "proc_raise", "i(i)", &m3_wasi_generic_proc_raise))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "random_get", "i(*i)", &m3_wasi_generic_random_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sched_yield", "i()", &m3_wasi_generic_sched_yield))); + +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_recv", "i(i*ii**)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_send", "i(i*ii*)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_shutdown", "i(ii)", ))); + } + +_catch: + return result; +} + +#endif // d_m3HasMetaWASI + diff --git a/cpp/m3_api_tracer.c b/cpp/m3_api_tracer.c new file mode 100644 index 0000000..4abedbd --- /dev/null +++ b/cpp/m3_api_tracer.c @@ -0,0 +1,168 @@ +// +// m3_api_tracer.c +// +// Created by Volodymyr Shymanskyy on 02/18/20. +// Copyright © 2020 Volodymyr Shymanskyy. All rights reserved. +// + +#include "m3_api_tracer.h" + +#include "m3_env.h" +#include "m3_exception.h" + +#if defined(d_m3HasTracer) + + +static FILE* trace = NULL; + +m3ApiRawFunction(m3_env_log_execution) +{ + m3ApiGetArg (uint32_t, id) + fprintf(trace, "exec;%d\n", id); + m3ApiSuccess(); +} + +m3ApiRawFunction(m3_env_log_exec_enter) +{ + m3ApiGetArg (uint32_t, id) + m3ApiGetArg (uint32_t, func) + fprintf(trace, "enter;%d;%d\n", id, func); + m3ApiSuccess(); +} + +m3ApiRawFunction(m3_env_log_exec_exit) +{ + m3ApiGetArg (uint32_t, id) + m3ApiGetArg (uint32_t, func) + fprintf(trace, "exit;%d;%d\n", id, func); + m3ApiSuccess(); +} + +m3ApiRawFunction(m3_env_log_exec_loop) +{ + m3ApiGetArg (uint32_t, id) + fprintf(trace, "loop;%d\n", id); + m3ApiSuccess(); +} + +m3ApiRawFunction(m3_env_load_ptr) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uint32_t, id) + m3ApiGetArg (uint32_t, align) + m3ApiGetArg (uint32_t, offset) + m3ApiGetArg (uint32_t, address) + fprintf(trace, "load ptr;%d;%d;%d;%d\n", id, align, offset, address); + m3ApiReturn(address); +} + +m3ApiRawFunction(m3_env_store_ptr) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uint32_t, id) + m3ApiGetArg (uint32_t, align) + m3ApiGetArg (uint32_t, offset) + m3ApiGetArg (uint32_t, address) + fprintf(trace, "store ptr;%d;%d;%d;%d\n", id, align, offset, address); + m3ApiReturn(address); +} + + +#define d_m3TraceMemory(FUNC, NAME, TYPE, FMT) \ +m3ApiRawFunction(m3_env_##FUNC) \ +{ \ + m3ApiReturnType (TYPE) \ + m3ApiGetArg (uint32_t, id) \ + m3ApiGetArg (TYPE, val) \ + fprintf(trace, NAME ";%d;" FMT "\n", id, val); \ + m3ApiReturn(val); \ +} + +d_m3TraceMemory( load_val_i32, "load i32", int32_t, "%" PRIi32) +d_m3TraceMemory(store_val_i32, "store i32", int32_t, "%" PRIi32) +d_m3TraceMemory( load_val_i64, "load i64", int64_t, "%" PRIi64) +d_m3TraceMemory(store_val_i64, "store i64", int64_t, "%" PRIi64) +d_m3TraceMemory( load_val_f32, "load f32", float, "%" PRIf32) +d_m3TraceMemory(store_val_f32, "store f32", float, "%" PRIf32) +d_m3TraceMemory( load_val_f64, "load f64", double, "%" PRIf64) +d_m3TraceMemory(store_val_f64, "store f64", double, "%" PRIf64) + + +#define d_m3TraceLocal(FUNC, NAME, TYPE, FMT) \ +m3ApiRawFunction(m3_env_##FUNC) \ +{ \ + m3ApiReturnType (TYPE) \ + m3ApiGetArg (uint32_t, id) \ + m3ApiGetArg (uint32_t, local) \ + m3ApiGetArg (TYPE, val) \ + fprintf(trace, NAME ";%d;%d;" FMT "\n", id, local, val); \ + m3ApiReturn(val); \ +} + + +d_m3TraceLocal(get_i32, "get i32", int32_t, "%" PRIi32) +d_m3TraceLocal(set_i32, "set i32", int32_t, "%" PRIi32) +d_m3TraceLocal(get_i64, "get i64", int64_t, "%" PRIi64) +d_m3TraceLocal(set_i64, "set i64", int64_t, "%" PRIi64) +d_m3TraceLocal(get_f32, "get f32", float, "%" PRIf32) +d_m3TraceLocal(set_f32, "set f32", float, "%" PRIf32) +d_m3TraceLocal(get_f64, "get f64", double, "%" PRIf64) +d_m3TraceLocal(set_f64, "set f64", double, "%" PRIf64) + + +static +M3Result SuppressLookupFailure(M3Result i_result) +{ + if (i_result == m3Err_none) { + // If any trace function is found in the module, open the trace file + if (!trace) { + trace = fopen ("wasm3_trace.csv","w"); + } + } else if (i_result == m3Err_functionLookupFailed) { + i_result = m3Err_none; + } + return i_result; +} + + +M3Result m3_LinkTracer (IM3Module module) +{ + M3Result result = m3Err_none; + + const char* env = "env"; + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "log_execution", "v(i)", &m3_env_log_execution))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "log_exec_enter", "v(ii)", &m3_env_log_exec_enter))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "log_exec_exit", "v(ii)", &m3_env_log_exec_exit))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "log_exec_loop", "v(i)", &m3_env_log_exec_loop))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "load_ptr", "i(iiii)", &m3_env_load_ptr))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "store_ptr", "i(iiii)", &m3_env_store_ptr))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "load_val_i32", "i(ii)", &m3_env_load_val_i32))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "load_val_i64", "I(iI)", &m3_env_load_val_i64))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "load_val_f32", "f(if)", &m3_env_load_val_f32))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "load_val_f64", "F(iF)", &m3_env_load_val_f64))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "store_val_i32", "i(ii)", &m3_env_store_val_i32))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "store_val_i64", "I(iI)", &m3_env_store_val_i64))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "store_val_f32", "f(if)", &m3_env_store_val_f32))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "store_val_f64", "F(iF)", &m3_env_store_val_f64))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "get_i32", "i(iii)", &m3_env_get_i32))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "get_i64", "I(iiI)", &m3_env_get_i64))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "get_f32", "f(iif)", &m3_env_get_f32))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "get_f64", "F(iiF)", &m3_env_get_f64))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "set_i32", "i(iii)", &m3_env_set_i32))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "set_i64", "I(iiI)", &m3_env_set_i64))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "set_f32", "f(iif)", &m3_env_set_f32))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "set_f64", "F(iiF)", &m3_env_set_f64))); + +_catch: + return result; +} + +#endif // d_m3HasTracer + diff --git a/cpp/m3_api_tracer.h b/cpp/m3_api_tracer.h new file mode 100644 index 0000000..5744b87 --- /dev/null +++ b/cpp/m3_api_tracer.h @@ -0,0 +1,19 @@ +// +// m3_api_tracer.h +// +// Created by Volodymyr Shymanskyy on 02/18/20. +// Copyright © 2020 Volodymyr Shymanskyy. All rights reserved. +// + +#ifndef m3_api_tracer_h +#define m3_api_tracer_h + +#include "m3_core.h" + +d_m3BeginExternC + +M3Result m3_LinkTracer (IM3Module io_module); + +d_m3EndExternC + +#endif // m3_api_tracer_h diff --git a/cpp/m3_api_uvwasi.c b/cpp/m3_api_uvwasi.c new file mode 100644 index 0000000..e31ac4e --- /dev/null +++ b/cpp/m3_api_uvwasi.c @@ -0,0 +1,1219 @@ +// +// m3_api_uvwasi.c +// +// Created by Colin J. Ihrig on 4/20/20. +// Copyright © 2020 Colin J. Ihrig, Volodymyr Shymanskyy. All rights reserved. +// + +#define _POSIX_C_SOURCE 200809L + +#include "m3_api_wasi.h" + +#include "m3_env.h" +#include "m3_exception.h" + +#if defined(d_m3HasUVWASI) + +#include +#include + +#ifdef __APPLE__ +# include +# define environ (*_NSGetEnviron()) +#elif !defined(_MSC_VER) +extern char** environ; +#endif + +static m3_wasi_context_t* wasi_context; +static uvwasi_t uvwasi; + +typedef struct wasi_iovec_t +{ + uvwasi_size_t buf; + uvwasi_size_t buf_len; +} wasi_iovec_t; + +#if d_m3EnableWasiTracing + +const char* wasi_errno2str(uvwasi_errno_t err) +{ + switch (err) { + case 0: return "ESUCCESS"; + case 1: return "E2BIG"; + case 2: return "EACCES"; + case 3: return "EADDRINUSE"; + case 4: return "EADDRNOTAVAIL"; + case 5: return "EAFNOSUPPORT"; + case 6: return "EAGAIN"; + case 7: return "EALREADY"; + case 8: return "EBADF"; + case 9: return "EBADMSG"; + case 10: return "EBUSY"; + case 11: return "ECANCELED"; + case 12: return "ECHILD"; + case 13: return "ECONNABORTED"; + case 14: return "ECONNREFUSED"; + case 15: return "ECONNRESET"; + case 16: return "EDEADLK"; + case 17: return "EDESTADDRREQ"; + case 18: return "EDOM"; + case 19: return "EDQUOT"; + case 20: return "EEXIST"; + case 21: return "EFAULT"; + case 22: return "EFBIG"; + case 23: return "EHOSTUNREACH"; + case 24: return "EIDRM"; + case 25: return "EILSEQ"; + case 26: return "EINPROGRESS"; + case 27: return "EINTR"; + case 28: return "EINVAL"; + case 29: return "EIO"; + case 30: return "EISCONN"; + case 31: return "EISDIR"; + case 32: return "ELOOP"; + case 33: return "EMFILE"; + case 34: return "EMLINK"; + case 35: return "EMSGSIZE"; + case 36: return "EMULTIHOP"; + case 37: return "ENAMETOOLONG"; + case 38: return "ENETDOWN"; + case 39: return "ENETRESET"; + case 40: return "ENETUNREACH"; + case 41: return "ENFILE"; + case 42: return "ENOBUFS"; + case 43: return "ENODEV"; + case 44: return "ENOENT"; + case 45: return "ENOEXEC"; + case 46: return "ENOLCK"; + case 47: return "ENOLINK"; + case 48: return "ENOMEM"; + case 49: return "ENOMSG"; + case 50: return "ENOPROTOOPT"; + case 51: return "ENOSPC"; + case 52: return "ENOSYS"; + case 53: return "ENOTCONN"; + case 54: return "ENOTDIR"; + case 55: return "ENOTEMPTY"; + case 56: return "ENOTRECOVERABLE"; + case 57: return "ENOTSOCK"; + case 58: return "ENOTSUP"; + case 59: return "ENOTTY"; + case 60: return "ENXIO"; + case 61: return "EOVERFLOW"; + case 62: return "EOWNERDEAD"; + case 63: return "EPERM"; + case 64: return "EPIPE"; + case 65: return "EPROTO"; + case 66: return "EPROTONOSUPPORT"; + case 67: return "EPROTOTYPE"; + case 68: return "ERANGE"; + case 69: return "EROFS"; + case 70: return "ESPIPE"; + case 71: return "ESRCH"; + case 72: return "ESTALE"; + case 73: return "ETIMEDOUT"; + case 74: return "ETXTBSY"; + case 75: return "EXDEV"; + case 76: return "ENOTCAPABLE"; + default: return ""; + } +} + +const char* wasi_whence2str(uvwasi_whence_t whence) +{ + switch (whence) { + case UVWASI_WHENCE_SET: return "SET"; + case UVWASI_WHENCE_CUR: return "CUR"; + case UVWASI_WHENCE_END: return "END"; + default: return ""; + } +} + +# define WASI_TRACE(fmt, ...) { fprintf(stderr, "%s " fmt, __FUNCTION__+16, ##__VA_ARGS__); fprintf(stderr, " => %s\n", wasi_errno2str(ret)); } +#else +# define WASI_TRACE(fmt, ...) +#endif + +/* + * WASI API implementation + */ + +m3ApiRawFunction(m3_wasi_generic_args_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (uint32_t * , argv) + m3ApiGetArgMem (char * , argv_buf) + + m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata); + + if (context == NULL) { m3ApiReturn(UVWASI_EINVAL); } + + m3ApiCheckMem(argv, context->argc * sizeof(uint32_t)); + + for (u32 i = 0; i < context->argc; ++i) + { + m3ApiWriteMem32(&argv[i], m3ApiPtrToOffset(argv_buf)); + + size_t len = strlen (context->argv[i]); + + m3ApiCheckMem(argv_buf, len); + memcpy (argv_buf, context->argv[i], len); + argv_buf += len; + * argv_buf++ = 0; + } + + m3ApiReturn(UVWASI_ESUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_args_sizes_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (uvwasi_size_t * , argc) + m3ApiGetArgMem (uvwasi_size_t * , argv_buf_size) + + m3ApiCheckMem(argc, sizeof(uvwasi_size_t)); + m3ApiCheckMem(argv_buf_size, sizeof(uvwasi_size_t)); + + m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata); + + if (context == NULL) { m3ApiReturn(UVWASI_EINVAL); } + + uvwasi_size_t buf_len = 0; + for (u32 i = 0; i < context->argc; ++i) + { + buf_len += strlen (context->argv[i]) + 1; + } + + m3ApiWriteMem32(argc, context->argc); + m3ApiWriteMem32(argv_buf_size, buf_len); + + m3ApiReturn(UVWASI_ESUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_environ_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (uint32_t * , env) + m3ApiGetArgMem (char * , env_buf) + + char **environment; + uvwasi_errno_t ret; + uvwasi_size_t env_count, env_buf_size; + + ret = uvwasi_environ_sizes_get(&uvwasi, &env_count, &env_buf_size); + if (ret != UVWASI_ESUCCESS) { + m3ApiReturn(ret); + } + + m3ApiCheckMem(env, env_count * sizeof(uint32_t)); + m3ApiCheckMem(env_buf, env_buf_size); + + environment = calloc(env_count, sizeof(char *)); + if (environment == NULL) { + m3ApiReturn(UVWASI_ENOMEM); + } + + ret = uvwasi_environ_get(&uvwasi, environment, env_buf); + if (ret != UVWASI_ESUCCESS) { + free(environment); + m3ApiReturn(ret); + } + + uint32_t environ_buf_offset = m3ApiPtrToOffset(env_buf); + + for (u32 i = 0; i < env_count; ++i) + { + uint32_t offset = environ_buf_offset + + (environment[i] - environment[0]); + m3ApiWriteMem32(&env[i], offset); + } + + free(environment); + m3ApiReturn(UVWASI_ESUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_environ_sizes_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (uvwasi_size_t * , env_count) + m3ApiGetArgMem (uvwasi_size_t * , env_buf_size) + + m3ApiCheckMem(env_count, sizeof(uvwasi_size_t)); + m3ApiCheckMem(env_buf_size, sizeof(uvwasi_size_t)); + + uvwasi_size_t count; + uvwasi_size_t buf_size; + + uvwasi_errno_t ret = uvwasi_environ_sizes_get(&uvwasi, &count, &buf_size); + + m3ApiWriteMem32(env_count, count); + m3ApiWriteMem32(env_buf_size, buf_size); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_prestat_dir_name) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (char * , path) + m3ApiGetArg (uvwasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + uvwasi_errno_t ret = uvwasi_fd_prestat_dir_name(&uvwasi, fd, path, path_len); + + WASI_TRACE("fd:%d, len:%d | path:%s", fd, path_len, path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_prestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(buf, 8); + + uvwasi_prestat_t prestat; + + uvwasi_errno_t ret = uvwasi_fd_prestat_get(&uvwasi, fd, &prestat); + + WASI_TRACE("fd:%d | type:%d, name_len:%d", fd, prestat.pr_type, prestat.u.dir.pr_name_len); + + if (ret != UVWASI_ESUCCESS) { + m3ApiReturn(ret); + } + + m3ApiWriteMem32(buf+0, prestat.pr_type); + m3ApiWriteMem32(buf+4, prestat.u.dir.pr_name_len); + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(buf, 24); + + uvwasi_fdstat_t stat; + uvwasi_errno_t ret = uvwasi_fd_fdstat_get(&uvwasi, fd, &stat); + + WASI_TRACE("fd:%d", fd); + + if (ret != UVWASI_ESUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 24); + m3ApiWriteMem8 (buf+0, stat.fs_filetype); + m3ApiWriteMem16(buf+2, stat.fs_flags); + m3ApiWriteMem64(buf+8, stat.fs_rights_base); + m3ApiWriteMem64(buf+16, stat.fs_rights_inheriting); + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_set_flags) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArg (uvwasi_fdflags_t , flags) + + uvwasi_errno_t ret = uvwasi_fd_fdstat_set_flags(&uvwasi, fd, flags); + + WASI_TRACE("fd:%d, flags:0x%x", fd, flags); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_set_rights) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArg (uvwasi_rights_t , rights_base) + m3ApiGetArg (uvwasi_rights_t , rights_inheriting) + + uvwasi_errno_t ret = uvwasi_fd_fdstat_set_rights(&uvwasi, fd, rights_base, rights_inheriting); + + WASI_TRACE("fd:%d, base:0x%" PRIx64 ", inheriting:0x%" PRIx64, fd, rights_base, rights_inheriting); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_filestat_set_size) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArg (uvwasi_filesize_t , size) + + uvwasi_errno_t ret = uvwasi_fd_filestat_set_size(&uvwasi, fd, size); + + WASI_TRACE("fd:%d, size:%" PRIu64, fd, size); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_filestat_set_times) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArg (uvwasi_timestamp_t , atim) + m3ApiGetArg (uvwasi_timestamp_t , mtim) + m3ApiGetArg (uvwasi_fstflags_t , fst_flags) + + uvwasi_errno_t ret = uvwasi_fd_filestat_set_times(&uvwasi, fd, atim, mtim, fst_flags); + + WASI_TRACE("fd:%d, atim:%" PRIu64 ", mtim:%" PRIu64 ", flags:%d", fd, atim, mtim, fst_flags); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_unstable_fd_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(buf, 56); // wasi_filestat_t + + uvwasi_filestat_t stat; + + uvwasi_errno_t ret = uvwasi_fd_filestat_get(&uvwasi, fd, &stat); + + WASI_TRACE("fd:%d | fs.size:%" PRIu64, fd, stat.st_size); + + if (ret != UVWASI_ESUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 56); + m3ApiWriteMem64(buf+0, stat.st_dev); + m3ApiWriteMem64(buf+8, stat.st_ino); + m3ApiWriteMem8 (buf+16, stat.st_filetype); + m3ApiWriteMem32(buf+20, stat.st_nlink); + m3ApiWriteMem64(buf+24, stat.st_size); + m3ApiWriteMem64(buf+32, stat.st_atim); + m3ApiWriteMem64(buf+40, stat.st_mtim); + m3ApiWriteMem64(buf+48, stat.st_ctim); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(buf, 64); // wasi_filestat_t + + uvwasi_filestat_t stat; + + uvwasi_errno_t ret = uvwasi_fd_filestat_get(&uvwasi, fd, &stat); + + WASI_TRACE("fd:%d | fs.size:%" PRIu64, fd, stat.st_size); + + if (ret != UVWASI_ESUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 64); + m3ApiWriteMem64(buf+0, stat.st_dev); + m3ApiWriteMem64(buf+8, stat.st_ino); + m3ApiWriteMem8 (buf+16, stat.st_filetype); + m3ApiWriteMem64(buf+24, stat.st_nlink); + m3ApiWriteMem64(buf+32, stat.st_size); + m3ApiWriteMem64(buf+40, stat.st_atim); + m3ApiWriteMem64(buf+48, stat.st_mtim); + m3ApiWriteMem64(buf+56, stat.st_ctim); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_unstable_fd_seek) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArg (uvwasi_filedelta_t , offset) + m3ApiGetArg (uint32_t , wasi_whence) + m3ApiGetArgMem (uvwasi_filesize_t * , result) + + m3ApiCheckMem(result, sizeof(uvwasi_filesize_t)); + + uvwasi_whence_t whence = -1; + switch (wasi_whence) { + case 0: whence = UVWASI_WHENCE_CUR; break; + case 1: whence = UVWASI_WHENCE_END; break; + case 2: whence = UVWASI_WHENCE_SET; break; + } + + uvwasi_filesize_t pos; + uvwasi_errno_t ret = uvwasi_fd_seek(&uvwasi, fd, offset, whence, &pos); + + WASI_TRACE("fd:%d, offset:%" PRIu64 ", whence:%s | result:%" PRIu64, + fd, offset, wasi_whence2str(whence), pos); + + m3ApiWriteMem64(result, pos); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_seek) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArg (uvwasi_filedelta_t , offset) + m3ApiGetArg (uint32_t , wasi_whence) + m3ApiGetArgMem (uvwasi_filesize_t * , result) + + m3ApiCheckMem(result, sizeof(uvwasi_filesize_t)); + + uvwasi_whence_t whence = -1; + switch (wasi_whence) { + case 0: whence = UVWASI_WHENCE_SET; break; + case 1: whence = UVWASI_WHENCE_CUR; break; + case 2: whence = UVWASI_WHENCE_END; break; + } + + uvwasi_filesize_t pos; + uvwasi_errno_t ret = uvwasi_fd_seek(&uvwasi, fd, offset, whence, &pos); + + WASI_TRACE("fd:%d, offset:%" PRIu64 ", whence:%s | result:%" PRIu64, + fd, offset, wasi_whence2str(whence), pos); + + m3ApiWriteMem64(result, pos); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_renumber) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , from) + m3ApiGetArg (uvwasi_fd_t , to) + + uvwasi_errno_t ret = uvwasi_fd_renumber(&uvwasi, from, to); + + WASI_TRACE("from:%d, to:%d", from, to); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_sync) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + + uvwasi_errno_t ret = uvwasi_fd_sync(&uvwasi, fd); + + WASI_TRACE("fd:%d", fd); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_tell) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (uvwasi_filesize_t * , result) + + m3ApiCheckMem(result, sizeof(uvwasi_filesize_t)); + + uvwasi_filesize_t pos; + uvwasi_errno_t ret = uvwasi_fd_tell(&uvwasi, fd, &pos); + + WASI_TRACE("fd:%d | result:%" PRIu64, fd, pos); + + m3ApiWriteMem64(result, pos); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_create_directory) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uvwasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + uvwasi_errno_t ret = uvwasi_path_create_directory(&uvwasi, fd, path, path_len); + + WASI_TRACE("fd:%d, path:%s", fd, path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_readlink) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uvwasi_size_t , path_len) + m3ApiGetArgMem (char * , buf) + m3ApiGetArg (uvwasi_size_t , buf_len) + m3ApiGetArgMem (uvwasi_size_t * , bufused) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(buf, buf_len); + m3ApiCheckMem(bufused, sizeof(uvwasi_size_t)); + + uvwasi_size_t uvbufused; + + uvwasi_errno_t ret = uvwasi_path_readlink(&uvwasi, fd, path, path_len, buf, buf_len, &uvbufused); + + WASI_TRACE("fd:%d, path:%s | buf:%s, bufused:%d", fd, path, buf, uvbufused); + + m3ApiWriteMem32(bufused, uvbufused); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_remove_directory) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uvwasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + uvwasi_errno_t ret = uvwasi_path_remove_directory(&uvwasi, fd, path, path_len); + + WASI_TRACE("fd:%d, path:%s", fd, path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_rename) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , old_fd) + m3ApiGetArgMem (const char * , old_path) + m3ApiGetArg (uvwasi_size_t , old_path_len) + m3ApiGetArg (uvwasi_fd_t , new_fd) + m3ApiGetArgMem (const char * , new_path) + m3ApiGetArg (uvwasi_size_t , new_path_len) + + m3ApiCheckMem(old_path, old_path_len); + m3ApiCheckMem(new_path, new_path_len); + + uvwasi_errno_t ret = uvwasi_path_rename(&uvwasi, old_fd, old_path, old_path_len, + new_fd, new_path, new_path_len); + + WASI_TRACE("old_fd:%d, old_path:%s, new_fd:%d, new_path:%s", old_fd, old_path, new_fd, new_path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_symlink) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (const char * , old_path) + m3ApiGetArg (uvwasi_size_t , old_path_len) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (const char * , new_path) + m3ApiGetArg (uvwasi_size_t , new_path_len) + + m3ApiCheckMem(old_path, old_path_len); + m3ApiCheckMem(new_path, new_path_len); + + uvwasi_errno_t ret = uvwasi_path_symlink(&uvwasi, old_path, old_path_len, + fd, new_path, new_path_len); + + WASI_TRACE("old_path:%s, fd:%d, new_path:%s", old_path, fd, new_path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_unlink_file) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uvwasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + uvwasi_errno_t ret = uvwasi_path_unlink_file(&uvwasi, fd, path, path_len); + + WASI_TRACE("fd:%d, path:%s", fd, path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_open) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , dirfd) + m3ApiGetArg (uvwasi_lookupflags_t , dirflags) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uvwasi_size_t , path_len) + m3ApiGetArg (uvwasi_oflags_t , oflags) + m3ApiGetArg (uvwasi_rights_t , fs_rights_base) + m3ApiGetArg (uvwasi_rights_t , fs_rights_inheriting) + m3ApiGetArg (uvwasi_fdflags_t , fs_flags) + m3ApiGetArgMem (uvwasi_fd_t * , fd) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(fd, sizeof(uvwasi_fd_t)); + + uvwasi_fd_t uvfd; + + uvwasi_errno_t ret = uvwasi_path_open(&uvwasi, + dirfd, + dirflags, + path, + path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + &uvfd); + + WASI_TRACE("dirfd:%d, dirflags:0x%x, path:%s, oflags:0x%x, fs_flags:0x%x | fd:%d", dirfd, dirflags, path, oflags, fs_flags, uvfd); + + m3ApiWriteMem32(fd, uvfd); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_unstable_path_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArg (uvwasi_lookupflags_t , flags) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uint32_t , path_len) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(buf, 56); // wasi_filestat_t + + uvwasi_filestat_t stat; + + uvwasi_errno_t ret = uvwasi_path_filestat_get(&uvwasi, fd, flags, path, path_len, &stat); + + WASI_TRACE("fd:%d, flags:0x%x, path:%s | fs.size:%" PRIu64, fd, flags, path, stat.st_size); + + if (ret != UVWASI_ESUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 56); + m3ApiWriteMem64(buf+0, stat.st_dev); + m3ApiWriteMem64(buf+8, stat.st_ino); + m3ApiWriteMem8 (buf+16, stat.st_filetype); + m3ApiWriteMem32(buf+20, stat.st_nlink); + m3ApiWriteMem64(buf+24, stat.st_size); + m3ApiWriteMem64(buf+32, stat.st_atim); + m3ApiWriteMem64(buf+40, stat.st_mtim); + m3ApiWriteMem64(buf+48, stat.st_ctim); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_path_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArg (uvwasi_lookupflags_t , flags) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uint32_t , path_len) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(buf, 64); // wasi_filestat_t + + uvwasi_filestat_t stat; + + uvwasi_errno_t ret = uvwasi_path_filestat_get(&uvwasi, fd, flags, path, path_len, &stat); + + WASI_TRACE("fd:%d, flags:0x%x, path:%s | fs.size:%" PRIu64, fd, flags, path, stat.st_size); + + if (ret != UVWASI_ESUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 64); + m3ApiWriteMem64(buf+0, stat.st_dev); + m3ApiWriteMem64(buf+8, stat.st_ino); + m3ApiWriteMem8 (buf+16, stat.st_filetype); + m3ApiWriteMem64(buf+24, stat.st_nlink); + m3ApiWriteMem64(buf+32, stat.st_size); + m3ApiWriteMem64(buf+40, stat.st_atim); + m3ApiWriteMem64(buf+48, stat.st_mtim); + m3ApiWriteMem64(buf+56, stat.st_ctim); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_pread) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (wasi_iovec_t * , wasi_iovs) + m3ApiGetArg (uvwasi_size_t , iovs_len) + m3ApiGetArg (uvwasi_filesize_t , offset) + m3ApiGetArgMem (uvwasi_size_t * , nread) + + m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(wasi_iovec_t)); + m3ApiCheckMem(nread, sizeof(uvwasi_size_t)); + +#if defined(M3_COMPILER_MSVC) + if (iovs_len > 32) m3ApiReturn(UVWASI_EINVAL); + uvwasi_iovec_t iovs[32]; +#else + if (iovs_len > 128) m3ApiReturn(UVWASI_EINVAL); + uvwasi_iovec_t iovs[iovs_len]; +#endif + + for (uvwasi_size_t i = 0; i < iovs_len; ++i) { + iovs[i].buf = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf)); + iovs[i].buf_len = m3ApiReadMem32(&wasi_iovs[i].buf_len); + m3ApiCheckMem(iovs[i].buf, iovs[i].buf_len); + //fprintf(stderr, "> fd_pread fd:%d iov%d.len:%d\n", fd, i, iovs[i].buf_len); + } + + uvwasi_size_t num_read; + + uvwasi_errno_t ret = uvwasi_fd_pread(&uvwasi, fd, iovs, iovs_len, offset, &num_read); + + WASI_TRACE("fd:%d | nread:%d", fd, num_read); + + m3ApiWriteMem32(nread, num_read); + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_read) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (wasi_iovec_t * , wasi_iovs) + m3ApiGetArg (uvwasi_size_t , iovs_len) + m3ApiGetArgMem (uvwasi_size_t * , nread) + + m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(wasi_iovec_t)); + m3ApiCheckMem(nread, sizeof(uvwasi_size_t)); + +#if defined(M3_COMPILER_MSVC) + if (iovs_len > 32) m3ApiReturn(UVWASI_EINVAL); + uvwasi_iovec_t iovs[32]; +#else + if (iovs_len > 128) m3ApiReturn(UVWASI_EINVAL); + uvwasi_iovec_t iovs[iovs_len]; +#endif + uvwasi_size_t num_read; + uvwasi_errno_t ret; + + for (uvwasi_size_t i = 0; i < iovs_len; ++i) { + iovs[i].buf = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf)); + iovs[i].buf_len = m3ApiReadMem32(&wasi_iovs[i].buf_len); + m3ApiCheckMem(iovs[i].buf, iovs[i].buf_len); + //fprintf(stderr, "> fd_read fd:%d iov%d.len:%d\n", fd, i, iovs[i].buf_len); + } + + ret = uvwasi_fd_read(&uvwasi, fd, iovs, iovs_len, &num_read); + + WASI_TRACE("fd:%d | nread:%d", fd, num_read); + + m3ApiWriteMem32(nread, num_read); + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_write) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (wasi_iovec_t * , wasi_iovs) + m3ApiGetArg (uvwasi_size_t , iovs_len) + m3ApiGetArgMem (uvwasi_size_t * , nwritten) + + m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(wasi_iovec_t)); + m3ApiCheckMem(nwritten, sizeof(uvwasi_size_t)); + +#if defined(M3_COMPILER_MSVC) + if (iovs_len > 32) m3ApiReturn(UVWASI_EINVAL); + uvwasi_ciovec_t iovs[32]; +#else + if (iovs_len > 128) m3ApiReturn(UVWASI_EINVAL); + uvwasi_ciovec_t iovs[iovs_len]; +#endif + uvwasi_size_t num_written; + uvwasi_errno_t ret; + + for (uvwasi_size_t i = 0; i < iovs_len; ++i) { + iovs[i].buf = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf)); + iovs[i].buf_len = m3ApiReadMem32(&wasi_iovs[i].buf_len); + m3ApiCheckMem(iovs[i].buf, iovs[i].buf_len); + } + + ret = uvwasi_fd_write(&uvwasi, fd, iovs, iovs_len, &num_written); + + WASI_TRACE("fd:%d | nwritten:%d", fd, num_written); + + m3ApiWriteMem32(nwritten, num_written); + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_pwrite) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (wasi_iovec_t * , wasi_iovs) + m3ApiGetArg (uvwasi_size_t , iovs_len) + m3ApiGetArg (uvwasi_filesize_t , offset) + m3ApiGetArgMem (uvwasi_size_t * , nwritten) + + m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(wasi_iovec_t)); + m3ApiCheckMem(nwritten, sizeof(uvwasi_size_t)); + +#if defined(M3_COMPILER_MSVC) + if (iovs_len > 32) m3ApiReturn(UVWASI_EINVAL); + uvwasi_ciovec_t iovs[32]; +#else + if (iovs_len > 128) m3ApiReturn(UVWASI_EINVAL); + uvwasi_ciovec_t iovs[iovs_len]; +#endif + uvwasi_size_t num_written; + uvwasi_errno_t ret; + + for (uvwasi_size_t i = 0; i < iovs_len; ++i) { + iovs[i].buf = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf)); + iovs[i].buf_len = m3ApiReadMem32(&wasi_iovs[i].buf_len); + m3ApiCheckMem(iovs[i].buf, iovs[i].buf_len); + } + + ret = uvwasi_fd_pwrite(&uvwasi, fd, iovs, iovs_len, offset, &num_written); + + WASI_TRACE("fd:%d | nwritten:%d", fd, num_written); + + m3ApiWriteMem32(nwritten, num_written); + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_readdir) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (void * , buf) + m3ApiGetArg (uvwasi_size_t , buf_len) + m3ApiGetArg (uvwasi_dircookie_t , cookie) + m3ApiGetArgMem (uvwasi_size_t * , bufused) + + m3ApiCheckMem(buf, buf_len); + m3ApiCheckMem(bufused, sizeof(uvwasi_size_t)); + + uvwasi_size_t uvbufused; + uvwasi_errno_t ret = uvwasi_fd_readdir(&uvwasi, fd, buf, buf_len, cookie, &uvbufused); + + WASI_TRACE("fd:%d | bufused:%d", fd, uvbufused); + + m3ApiWriteMem32(bufused, uvbufused); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_advise) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArg (uvwasi_filesize_t , offset) + m3ApiGetArg (uvwasi_filesize_t , length) + m3ApiGetArg (uvwasi_advice_t , advice) + + uvwasi_errno_t ret = uvwasi_fd_advise(&uvwasi, fd, offset, length, advice); + + WASI_TRACE("fd:%d, offset:%" PRIu64 ", length:%" PRIu64 ", advice:%d", fd, offset, length, advice); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_allocate) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArg (uvwasi_filesize_t , offset) + m3ApiGetArg (uvwasi_filesize_t , length) + + uvwasi_errno_t ret = uvwasi_fd_allocate(&uvwasi, fd, offset, length); + + WASI_TRACE("fd:%d, offset:%" PRIu64 ", length:%" PRIu64, fd, offset, length); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_close) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t, fd) + + uvwasi_errno_t ret = uvwasi_fd_close(&uvwasi, fd); + + WASI_TRACE("fd:%d", fd); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_datasync) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t, fd) + + uvwasi_errno_t ret = uvwasi_fd_datasync(&uvwasi, fd); + + WASI_TRACE("fd:%d", fd); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_random_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (uint8_t * , buf) + m3ApiGetArg (uvwasi_size_t , buf_len) + + m3ApiCheckMem(buf, buf_len); + + uvwasi_errno_t ret = uvwasi_random_get(&uvwasi, buf, buf_len); + + WASI_TRACE("len:%d", buf_len); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_clock_res_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_clockid_t , wasi_clk_id) + m3ApiGetArgMem (uvwasi_timestamp_t * , resolution) + + m3ApiCheckMem(resolution, sizeof(uvwasi_timestamp_t)); + + uvwasi_timestamp_t t; + uvwasi_errno_t ret = uvwasi_clock_res_get(&uvwasi, wasi_clk_id, &t); + + WASI_TRACE("clk_id:%d | res:%" PRIu64, wasi_clk_id, t); + + m3ApiWriteMem64(resolution, t); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_clock_time_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_clockid_t , wasi_clk_id) + m3ApiGetArg (uvwasi_timestamp_t , precision) + m3ApiGetArgMem (uvwasi_timestamp_t * , time) + + m3ApiCheckMem(time, sizeof(uvwasi_timestamp_t)); + + uvwasi_timestamp_t t; + uvwasi_errno_t ret = uvwasi_clock_time_get(&uvwasi, wasi_clk_id, precision, &t); + + WASI_TRACE("clk_id:%d | res:%" PRIu64, wasi_clk_id, t); + + m3ApiWriteMem64(time, t); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_poll_oneoff) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (const uvwasi_subscription_t * , in) + m3ApiGetArgMem (uvwasi_event_t * , out) + m3ApiGetArg (uvwasi_size_t , nsubscriptions) + m3ApiGetArgMem (uvwasi_size_t * , nevents) + + m3ApiCheckMem(in, nsubscriptions * sizeof(uvwasi_subscription_t)); + m3ApiCheckMem(out, nsubscriptions * sizeof(uvwasi_event_t)); + m3ApiCheckMem(nevents, sizeof(uvwasi_size_t)); + + // TODO: unstable/snapshot_preview1 compatibility + + uvwasi_errno_t ret = uvwasi_poll_oneoff(&uvwasi, in, out, nsubscriptions, nevents); + + WASI_TRACE("nsubscriptions:%d | nevents:%d", nsubscriptions, *nevents); + + //TODO: m3ApiWriteMem + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_proc_exit) +{ + m3ApiGetArg (uint32_t, code) + + m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata); + + if (context) { + context->exit_code = code; + } + + //TODO: fprintf(stderr, "proc_exit code:%d\n", code); + + m3ApiTrap(m3Err_trapExit); +} + +m3ApiRawFunction(m3_wasi_generic_proc_raise) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_signal_t, sig) + + uvwasi_errno_t ret = uvwasi_proc_raise(&uvwasi, sig); + + WASI_TRACE("sig:%d", sig); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_sched_yield) +{ + m3ApiReturnType (uint32_t) + uvwasi_errno_t ret = uvwasi_sched_yield(&uvwasi); + + WASI_TRACE(""); + + m3ApiReturn(ret); +} + + +static +M3Result SuppressLookupFailure(M3Result i_result) +{ + if (i_result == m3Err_functionLookupFailed) + return m3Err_none; + else + return i_result; +} + +m3_wasi_context_t* m3_GetWasiContext() +{ + return wasi_context; +} + + +M3Result m3_LinkWASI (IM3Module module) +{ + #define ENV_COUNT 9 + + char* env[ENV_COUNT]; + env[0] = "TERM=xterm-256color"; + env[1] = "COLORTERM=truecolor"; + env[2] = "LANG=en_US.UTF-8"; + env[3] = "PWD=/"; + env[4] = "HOME=/"; + env[5] = "PATH=/"; + env[6] = "WASM3=1"; + env[7] = "WASM3_ARCH=" M3_ARCH; + env[8] = NULL; + + #define PREOPENS_COUNT 2 + + uvwasi_preopen_t preopens[PREOPENS_COUNT]; + preopens[0].mapped_path = "/"; + preopens[0].real_path = "."; + preopens[1].mapped_path = "./"; + preopens[1].real_path = "."; + + uvwasi_options_t init_options; + uvwasi_options_init(&init_options); + init_options.argc = 0; // runtime->argc is not initialized at this point, so we implement args_get directly + init_options.envp = (const char **) env; + init_options.preopenc = PREOPENS_COUNT; + init_options.preopens = preopens; + + return m3_LinkWASIWithOptions(module, init_options); +} + +M3Result m3_LinkWASIWithOptions (IM3Module module, uvwasi_options_t init_options) +{ + M3Result result = m3Err_none; + + if (!wasi_context) { + wasi_context = (m3_wasi_context_t*)malloc(sizeof(m3_wasi_context_t)); + wasi_context->exit_code = 0; + wasi_context->argc = 0; + wasi_context->argv = 0; + + uvwasi_errno_t ret = uvwasi_init(&uvwasi, &init_options); + + if (ret != UVWASI_ESUCCESS) { + return "uvwasi_init failed"; + } + } + + static const char* namespaces[2] = { "wasi_unstable", "wasi_snapshot_preview1" }; + + // Some functions are incompatible between WASI versions +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_seek", "i(iIi*)", &m3_wasi_unstable_fd_seek))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_seek", "i(iIi*)", &m3_wasi_snapshot_preview1_fd_seek))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_filestat_get", "i(i*)", &m3_wasi_unstable_fd_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_filestat_get", "i(i*)", &m3_wasi_snapshot_preview1_fd_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "path_filestat_get", "i(ii*i*)", &m3_wasi_unstable_path_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "path_filestat_get", "i(ii*i*)", &m3_wasi_snapshot_preview1_path_filestat_get))); + + for (int i=0; i<2; i++) + { + const char* wasi = namespaces[i]; + +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_get", "i(**)", &m3_wasi_generic_args_get, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_sizes_get", "i(**)", &m3_wasi_generic_args_sizes_get, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_res_get", "i(i*)", &m3_wasi_generic_clock_res_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_time_get", "i(iI*)", &m3_wasi_generic_clock_time_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_get", "i(**)", &m3_wasi_generic_environ_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_sizes_get", "i(**)", &m3_wasi_generic_environ_sizes_get))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_advise", "i(iIIi)", &m3_wasi_generic_fd_advise))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_allocate", "i(iII)", &m3_wasi_generic_fd_allocate))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_close", "i(i)", &m3_wasi_generic_fd_close))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_datasync", "i(i)", &m3_wasi_generic_fd_datasync))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_get", "i(i*)", &m3_wasi_generic_fd_fdstat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_flags", "i(ii)", &m3_wasi_generic_fd_fdstat_set_flags))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_rights", "i(iII)", &m3_wasi_generic_fd_fdstat_set_rights))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_size", "i(iI)", &m3_wasi_generic_fd_filestat_set_size))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_times","i(iIIi)", &m3_wasi_generic_fd_filestat_set_times))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pread", "i(i*iI*)",&m3_wasi_generic_fd_pread))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_get", "i(i*)", &m3_wasi_generic_fd_prestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_dir_name", "i(i*i)", &m3_wasi_generic_fd_prestat_dir_name))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pwrite", "i(i*iI*)",&m3_wasi_generic_fd_pwrite))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_read", "i(i*i*)", &m3_wasi_generic_fd_read))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_readdir", "i(i*iI*)",&m3_wasi_generic_fd_readdir))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_renumber", "i(ii)", &m3_wasi_generic_fd_renumber))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_sync", "i(i)", &m3_wasi_generic_fd_sync))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_tell", "i(i*)", &m3_wasi_generic_fd_tell))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_write", "i(i*i*)", &m3_wasi_generic_fd_write))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_create_directory", "i(i*i)", &m3_wasi_generic_path_create_directory))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_set_times", "i(ii*iIIi)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_link", "i(ii*ii*i)", ))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_open", "i(ii*iiIIi*)", &m3_wasi_generic_path_open))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_readlink", "i(i*i*i*)", &m3_wasi_generic_path_readlink))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_remove_directory", "i(i*i)", &m3_wasi_generic_path_remove_directory))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_rename", "i(i*ii*i)", &m3_wasi_generic_path_rename))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_symlink", "i(*ii*i)", &m3_wasi_generic_path_symlink))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_unlink_file", "i(i*i)", &m3_wasi_generic_path_unlink_file))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "poll_oneoff", "i(**i*)", &m3_wasi_generic_poll_oneoff))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "proc_exit", "v(i)", &m3_wasi_generic_proc_exit, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "proc_raise", "i(i)", &m3_wasi_generic_proc_raise))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "random_get", "i(*i)", &m3_wasi_generic_random_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sched_yield", "i()", &m3_wasi_generic_sched_yield))); + +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_recv", "i(i*ii**)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_send", "i(i*ii*)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_shutdown", "i(ii)", ))); + } + +_catch: + return result; +} + +#endif // d_m3HasUVWASI + diff --git a/cpp/m3_api_wasi.c b/cpp/m3_api_wasi.c new file mode 100644 index 0000000..52714ec --- /dev/null +++ b/cpp/m3_api_wasi.c @@ -0,0 +1,873 @@ +// +// m3_api_wasi.c +// +// Created by Volodymyr Shymanskyy on 11/20/19. +// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. +// + +#define _POSIX_C_SOURCE 200809L + +#include "m3_api_wasi.h" + +#include "m3_env.h" +#include "m3_exception.h" + +#if defined(d_m3HasWASI) + +// Fixup wasi_core.h +#if defined (M3_COMPILER_MSVC) +# define _Static_assert(...) +# define __attribute__(...) +# define _Noreturn +#endif + +#include "extra/wasi_core.h" + +#include +#include +#include +#include +#include +#include + +#if defined(APE) +// Actually Portable Executable +// All functions are already included in cosmopolitan.h +#elif defined(__wasi__) || defined(__APPLE__) || defined(__ANDROID_API__) || defined(__OpenBSD__) || defined(__linux__) || defined(__EMSCRIPTEN__) || defined(__CYGWIN__) +# include +# include +# if defined(__APPLE__) +# include +# if TARGET_OS_OSX // TARGET_OS_MAC includes iOS +# include +# else // iOS / Simulator +# include +# endif +# else +# include +# endif +# define HAS_IOVEC +#elif defined(_WIN32) +# include +# include +// See http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx +# define SystemFunction036 NTAPI SystemFunction036 +# include +# undef SystemFunction036 +# define ssize_t SSIZE_T + +# define open _open +# define read _read +# define write _write +# define close _close +#endif + +static m3_wasi_context_t* wasi_context; + +typedef struct wasi_iovec_t +{ + __wasi_size_t buf; + __wasi_size_t buf_len; +} wasi_iovec_t; + +#define PREOPEN_CNT 5 + +typedef struct Preopen { + int fd; + const char* path; + const char* real_path; +} Preopen; + +Preopen preopen[PREOPEN_CNT] = { + { 0, "" , "" }, + { 1, "", "" }, + { 2, "", "" }, + { -1, "/" , "." }, + { -1, "./" , "." }, +}; + +#if defined(APE) +# define APE_SWITCH_BEG +# define APE_SWITCH_END {} +# define APE_CASE_RET(e1,e2) if (errnum == e1) return e2; else +#else +# define APE_SWITCH_BEG switch (errnum) { +# define APE_SWITCH_END } +# define APE_CASE_RET(e1,e2) case e1: return e2; break; +#endif + +static +__wasi_errno_t errno_to_wasi(int errnum) { + APE_SWITCH_BEG + APE_CASE_RET( EPERM , __WASI_ERRNO_PERM ) + APE_CASE_RET( ENOENT , __WASI_ERRNO_NOENT ) + APE_CASE_RET( ESRCH , __WASI_ERRNO_SRCH ) + APE_CASE_RET( EINTR , __WASI_ERRNO_INTR ) + APE_CASE_RET( EIO , __WASI_ERRNO_IO ) + APE_CASE_RET( ENXIO , __WASI_ERRNO_NXIO ) + APE_CASE_RET( E2BIG , __WASI_ERRNO_2BIG ) + APE_CASE_RET( ENOEXEC , __WASI_ERRNO_NOEXEC ) + APE_CASE_RET( EBADF , __WASI_ERRNO_BADF ) + APE_CASE_RET( ECHILD , __WASI_ERRNO_CHILD ) + APE_CASE_RET( EAGAIN , __WASI_ERRNO_AGAIN ) + APE_CASE_RET( ENOMEM , __WASI_ERRNO_NOMEM ) + APE_CASE_RET( EACCES , __WASI_ERRNO_ACCES ) + APE_CASE_RET( EFAULT , __WASI_ERRNO_FAULT ) + APE_CASE_RET( EBUSY , __WASI_ERRNO_BUSY ) + APE_CASE_RET( EEXIST , __WASI_ERRNO_EXIST ) + APE_CASE_RET( EXDEV , __WASI_ERRNO_XDEV ) + APE_CASE_RET( ENODEV , __WASI_ERRNO_NODEV ) + APE_CASE_RET( ENOTDIR , __WASI_ERRNO_NOTDIR ) + APE_CASE_RET( EISDIR , __WASI_ERRNO_ISDIR ) + APE_CASE_RET( EINVAL , __WASI_ERRNO_INVAL ) + APE_CASE_RET( ENFILE , __WASI_ERRNO_NFILE ) + APE_CASE_RET( EMFILE , __WASI_ERRNO_MFILE ) + APE_CASE_RET( ENOTTY , __WASI_ERRNO_NOTTY ) + APE_CASE_RET( ETXTBSY , __WASI_ERRNO_TXTBSY ) + APE_CASE_RET( EFBIG , __WASI_ERRNO_FBIG ) + APE_CASE_RET( ENOSPC , __WASI_ERRNO_NOSPC ) + APE_CASE_RET( ESPIPE , __WASI_ERRNO_SPIPE ) + APE_CASE_RET( EROFS , __WASI_ERRNO_ROFS ) + APE_CASE_RET( EMLINK , __WASI_ERRNO_MLINK ) + APE_CASE_RET( EPIPE , __WASI_ERRNO_PIPE ) + APE_CASE_RET( EDOM , __WASI_ERRNO_DOM ) + APE_CASE_RET( ERANGE , __WASI_ERRNO_RANGE ) + APE_SWITCH_END + return __WASI_ERRNO_INVAL; +} + +#if defined(_WIN32) + +#if !defined(__MINGW32__) + +static inline +int clock_gettime(int clk_id, struct timespec *spec) +{ + __int64 wintime; GetSystemTimeAsFileTime((FILETIME*)&wintime); + wintime -= 116444736000000000i64; //1jan1601 to 1jan1970 + spec->tv_sec = wintime / 10000000i64; //seconds + spec->tv_nsec = wintime % 10000000i64 *100; //nano-seconds + return 0; +} + +static inline +int clock_getres(int clk_id, struct timespec *spec) { + return -1; // Defaults to 1000000 +} + +#endif + +static inline +int convert_clockid(__wasi_clockid_t in) { + return 0; +} + +#else // _WIN32 + +static inline +int convert_clockid(__wasi_clockid_t in) { + switch (in) { + case __WASI_CLOCKID_MONOTONIC: return CLOCK_MONOTONIC; + case __WASI_CLOCKID_PROCESS_CPUTIME_ID: return CLOCK_PROCESS_CPUTIME_ID; + case __WASI_CLOCKID_REALTIME: return CLOCK_REALTIME; + case __WASI_CLOCKID_THREAD_CPUTIME_ID: return CLOCK_THREAD_CPUTIME_ID; + default: return -1; + } +} + +#endif // _WIN32 + +static inline +__wasi_timestamp_t convert_timespec(const struct timespec *ts) { + if (ts->tv_sec < 0) + return 0; + if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000) + return UINT64_MAX; + return (__wasi_timestamp_t)ts->tv_sec * 1000000000 + ts->tv_nsec; +} + +#if defined(HAS_IOVEC) + +static inline +const void* copy_iov_to_host(IM3Runtime runtime, void* _mem, struct iovec* host_iov, wasi_iovec_t* wasi_iov, int32_t iovs_len) +{ + // Convert wasi memory offsets to host addresses + for (int i = 0; i < iovs_len; i++) { + host_iov[i].iov_base = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iov[i].buf)); + host_iov[i].iov_len = m3ApiReadMem32(&wasi_iov[i].buf_len); + m3ApiCheckMem(host_iov[i].iov_base, host_iov[i].iov_len); + } + m3ApiSuccess(); +} + +#endif + +/* + * WASI API implementation + */ + +m3ApiRawFunction(m3_wasi_generic_args_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (uint32_t * , argv) + m3ApiGetArgMem (char * , argv_buf) + + m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata); + + if (context == NULL) { m3ApiReturn(__WASI_ERRNO_INVAL); } + + m3ApiCheckMem(argv, context->argc * sizeof(uint32_t)); + + for (u32 i = 0; i < context->argc; ++i) + { + m3ApiWriteMem32(&argv[i], m3ApiPtrToOffset(argv_buf)); + + size_t len = strlen (context->argv[i]); + + m3ApiCheckMem(argv_buf, len); + memcpy (argv_buf, context->argv[i], len); + argv_buf += len; + * argv_buf++ = 0; + } + + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_args_sizes_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (__wasi_size_t * , argc) + m3ApiGetArgMem (__wasi_size_t * , argv_buf_size) + + m3ApiCheckMem(argc, sizeof(__wasi_size_t)); + m3ApiCheckMem(argv_buf_size, sizeof(__wasi_size_t)); + + m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata); + + if (context == NULL) { m3ApiReturn(__WASI_ERRNO_INVAL); } + + __wasi_size_t buf_len = 0; + for (u32 i = 0; i < context->argc; ++i) + { + buf_len += strlen (context->argv[i]) + 1; + } + + m3ApiWriteMem32(argc, context->argc); + m3ApiWriteMem32(argv_buf_size, buf_len); + + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_environ_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (uint32_t * , env) + m3ApiGetArgMem (char * , env_buf) + + // TODO + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_environ_sizes_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (__wasi_size_t * , env_count) + m3ApiGetArgMem (__wasi_size_t * , env_buf_size) + + m3ApiCheckMem(env_count, sizeof(__wasi_size_t)); + m3ApiCheckMem(env_buf_size, sizeof(__wasi_size_t)); + + // TODO + m3ApiWriteMem32(env_count, 0); + m3ApiWriteMem32(env_buf_size, 0); + + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_fd_prestat_dir_name) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (char * , path) + m3ApiGetArg (__wasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + if (fd < 3 || fd >= PREOPEN_CNT) { m3ApiReturn(__WASI_ERRNO_BADF); } + size_t slen = strlen(preopen[fd].path) + 1; + memcpy(path, preopen[fd].path, M3_MIN(slen, path_len)); + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_fd_prestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(buf, 8); + + if (fd < 3 || fd >= PREOPEN_CNT) { m3ApiReturn(__WASI_ERRNO_BADF); } + + m3ApiWriteMem32(buf+0, __WASI_PREOPENTYPE_DIR); + m3ApiWriteMem32(buf+4, strlen(preopen[fd].path) + 1); + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (__wasi_fdstat_t * , fdstat) + + m3ApiCheckMem(fdstat, sizeof(__wasi_fdstat_t)); + +#ifdef _WIN32 + + // TODO: This needs a proper implementation + if (fd < PREOPEN_CNT) { + fdstat->fs_filetype= __WASI_FILETYPE_DIRECTORY; + } else { + fdstat->fs_filetype= __WASI_FILETYPE_REGULAR_FILE; + } + + fdstat->fs_flags = 0; + fdstat->fs_rights_base = (uint64_t)-1; // all rights + fdstat->fs_rights_inheriting = (uint64_t)-1; // all rights + m3ApiReturn(__WASI_ERRNO_SUCCESS); +#else + struct stat fd_stat; + +#if !defined(APE) // TODO: not implemented in Cosmopolitan + int fl = fcntl(fd, F_GETFL); + if (fl < 0) { m3ApiReturn(errno_to_wasi(errno)); } +#endif + + fstat(fd, &fd_stat); + int mode = fd_stat.st_mode; + fdstat->fs_filetype = (S_ISBLK(mode) ? __WASI_FILETYPE_BLOCK_DEVICE : 0) | + (S_ISCHR(mode) ? __WASI_FILETYPE_CHARACTER_DEVICE : 0) | + (S_ISDIR(mode) ? __WASI_FILETYPE_DIRECTORY : 0) | + (S_ISREG(mode) ? __WASI_FILETYPE_REGULAR_FILE : 0) | + //(S_ISSOCK(mode) ? __WASI_FILETYPE_SOCKET_STREAM : 0) | + (S_ISLNK(mode) ? __WASI_FILETYPE_SYMBOLIC_LINK : 0); +#if !defined(APE) + m3ApiWriteMem16(&fdstat->fs_flags, + ((fl & O_APPEND) ? __WASI_FDFLAGS_APPEND : 0) | + ((fl & O_DSYNC) ? __WASI_FDFLAGS_DSYNC : 0) | + ((fl & O_NONBLOCK) ? __WASI_FDFLAGS_NONBLOCK : 0) | + //((fl & O_RSYNC) ? __WASI_FDFLAGS_RSYNC : 0) | + ((fl & O_SYNC) ? __WASI_FDFLAGS_SYNC : 0)); +#endif // APE + + fdstat->fs_rights_base = (uint64_t)-1; // all rights + + // Make descriptors 0,1,2 look like a TTY + if (fd <= 2) { + fdstat->fs_rights_base &= ~(__WASI_RIGHTS_FD_SEEK | __WASI_RIGHTS_FD_TELL); + } + + fdstat->fs_rights_inheriting = (uint64_t)-1; // all rights + m3ApiReturn(__WASI_ERRNO_SUCCESS); +#endif +} + +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_set_flags) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_fdflags_t , flags) + + // TODO + + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_unstable_fd_seek) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_filedelta_t , offset) + m3ApiGetArg (uint32_t , wasi_whence) + m3ApiGetArgMem (__wasi_filesize_t * , result) + + m3ApiCheckMem(result, sizeof(__wasi_filesize_t)); + + int whence; + + switch (wasi_whence) { + case 0: whence = SEEK_CUR; break; + case 1: whence = SEEK_END; break; + case 2: whence = SEEK_SET; break; + default: m3ApiReturn(__WASI_ERRNO_INVAL); + } + + int64_t ret; +#if defined(M3_COMPILER_MSVC) || defined(__MINGW32__) + ret = _lseeki64(fd, offset, whence); +#else + ret = lseek(fd, offset, whence); +#endif + if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); } + m3ApiWriteMem64(result, ret); + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_seek) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_filedelta_t , offset) + m3ApiGetArg (uint32_t , wasi_whence) + m3ApiGetArgMem (__wasi_filesize_t * , result) + + m3ApiCheckMem(result, sizeof(__wasi_filesize_t)); + + int whence; + + switch (wasi_whence) { + case 0: whence = SEEK_SET; break; + case 1: whence = SEEK_CUR; break; + case 2: whence = SEEK_END; break; + default: m3ApiReturn(__WASI_ERRNO_INVAL); + } + + int64_t ret; +#if defined(M3_COMPILER_MSVC) || defined(__MINGW32__) + ret = _lseeki64(fd, offset, whence); +#else + ret = lseek(fd, offset, whence); +#endif + if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); } + m3ApiWriteMem64(result, ret); + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + + +m3ApiRawFunction(m3_wasi_generic_path_open) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , dirfd) + m3ApiGetArg (__wasi_lookupflags_t , dirflags) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (__wasi_size_t , path_len) + m3ApiGetArg (__wasi_oflags_t , oflags) + m3ApiGetArg (__wasi_rights_t , fs_rights_base) + m3ApiGetArg (__wasi_rights_t , fs_rights_inheriting) + m3ApiGetArg (__wasi_fdflags_t , fs_flags) + m3ApiGetArgMem (__wasi_fd_t * , fd) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(fd, sizeof(__wasi_fd_t)); + + if (path_len >= 512) + m3ApiReturn(__WASI_ERRNO_INVAL); + + // copy path so we can ensure it is NULL terminated +#if defined(M3_COMPILER_MSVC) + char host_path[512]; +#else + char host_path[path_len+1]; +#endif + memcpy (host_path, path, path_len); + host_path[path_len] = '\0'; // NULL terminator + +#if defined(APE) + // TODO: This all needs a proper implementation + + int flags = ((oflags & __WASI_OFLAGS_CREAT) ? O_CREAT : 0) | + ((oflags & __WASI_OFLAGS_EXCL) ? O_EXCL : 0) | + ((oflags & __WASI_OFLAGS_TRUNC) ? O_TRUNC : 0) | + ((fs_flags & __WASI_FDFLAGS_APPEND) ? O_APPEND : 0); + + if ((fs_rights_base & __WASI_RIGHTS_FD_READ) && + (fs_rights_base & __WASI_RIGHTS_FD_WRITE)) { + flags |= O_RDWR; + } else if ((fs_rights_base & __WASI_RIGHTS_FD_WRITE)) { + flags |= O_WRONLY; + } else if ((fs_rights_base & __WASI_RIGHTS_FD_READ)) { + flags |= O_RDONLY; // no-op because O_RDONLY is 0 + } + int mode = 0644; + + int host_fd = open (host_path, flags, mode); + + if (host_fd < 0) + { + m3ApiReturn(errno_to_wasi (errno)); + } + else + { + m3ApiWriteMem32(fd, host_fd); + m3ApiReturn(__WASI_ERRNO_SUCCESS); + } +#elif defined(_WIN32) + // TODO: This all needs a proper implementation + + int flags = ((oflags & __WASI_OFLAGS_CREAT) ? _O_CREAT : 0) | + ((oflags & __WASI_OFLAGS_EXCL) ? _O_EXCL : 0) | + ((oflags & __WASI_OFLAGS_TRUNC) ? _O_TRUNC : 0) | + ((fs_flags & __WASI_FDFLAGS_APPEND) ? _O_APPEND : 0) | + _O_BINARY; + + if ((fs_rights_base & __WASI_RIGHTS_FD_READ) && + (fs_rights_base & __WASI_RIGHTS_FD_WRITE)) { + flags |= _O_RDWR; + } else if ((fs_rights_base & __WASI_RIGHTS_FD_WRITE)) { + flags |= _O_WRONLY; + } else if ((fs_rights_base & __WASI_RIGHTS_FD_READ)) { + flags |= _O_RDONLY; // no-op because O_RDONLY is 0 + } + int mode = 0644; + + int host_fd = open (host_path, flags, mode); + + if (host_fd < 0) + { + m3ApiReturn(errno_to_wasi (errno)); + } + else + { + m3ApiWriteMem32(fd, host_fd); + m3ApiReturn(__WASI_ERRNO_SUCCESS); + } +#else + // translate o_flags and fs_flags into flags and mode + int flags = ((oflags & __WASI_OFLAGS_CREAT) ? O_CREAT : 0) | + //((oflags & __WASI_OFLAGS_DIRECTORY) ? O_DIRECTORY : 0) | + ((oflags & __WASI_OFLAGS_EXCL) ? O_EXCL : 0) | + ((oflags & __WASI_OFLAGS_TRUNC) ? O_TRUNC : 0) | + ((fs_flags & __WASI_FDFLAGS_APPEND) ? O_APPEND : 0) | + ((fs_flags & __WASI_FDFLAGS_DSYNC) ? O_DSYNC : 0) | + ((fs_flags & __WASI_FDFLAGS_NONBLOCK) ? O_NONBLOCK : 0) | + //((fs_flags & __WASI_FDFLAGS_RSYNC) ? O_RSYNC : 0) | + ((fs_flags & __WASI_FDFLAGS_SYNC) ? O_SYNC : 0); + if ((fs_rights_base & __WASI_RIGHTS_FD_READ) && + (fs_rights_base & __WASI_RIGHTS_FD_WRITE)) { + flags |= O_RDWR; + } else if ((fs_rights_base & __WASI_RIGHTS_FD_WRITE)) { + flags |= O_WRONLY; + } else if ((fs_rights_base & __WASI_RIGHTS_FD_READ)) { + flags |= O_RDONLY; // no-op because O_RDONLY is 0 + } + int mode = 0644; + int host_fd = openat (preopen[dirfd].fd, host_path, flags, mode); + + if (host_fd < 0) + { + m3ApiReturn(errno_to_wasi (errno)); + } + else + { + m3ApiWriteMem32(fd, host_fd); + m3ApiReturn(__WASI_ERRNO_SUCCESS); + } +#endif +} + +m3ApiRawFunction(m3_wasi_generic_fd_read) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (wasi_iovec_t * , wasi_iovs) + m3ApiGetArg (__wasi_size_t , iovs_len) + m3ApiGetArgMem (__wasi_size_t * , nread) + + m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(wasi_iovec_t)); + m3ApiCheckMem(nread, sizeof(__wasi_size_t)); + +#if defined(HAS_IOVEC) + struct iovec iovs[iovs_len]; + const void* mem_check = copy_iov_to_host(runtime, _mem, iovs, wasi_iovs, iovs_len); + if (mem_check != m3Err_none) { + return mem_check; + } + + ssize_t ret = readv(fd, iovs, iovs_len); + if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); } + m3ApiWriteMem32(nread, ret); + m3ApiReturn(__WASI_ERRNO_SUCCESS); +#else + ssize_t res = 0; + for (__wasi_size_t i = 0; i < iovs_len; i++) { + void* addr = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf)); + size_t len = m3ApiReadMem32(&wasi_iovs[i].buf_len); + if (len == 0) continue; + m3ApiCheckMem(addr, len); + int ret = read (fd, addr, len); + if (ret < 0) m3ApiReturn(errno_to_wasi(errno)); + res += ret; + if ((size_t)ret < len) break; + } + m3ApiWriteMem32(nread, res); + m3ApiReturn(__WASI_ERRNO_SUCCESS); +#endif +} + +m3ApiRawFunction(m3_wasi_generic_fd_write) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (wasi_iovec_t * , wasi_iovs) + m3ApiGetArg (__wasi_size_t , iovs_len) + m3ApiGetArgMem (__wasi_size_t * , nwritten) + + m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(wasi_iovec_t)); + m3ApiCheckMem(nwritten, sizeof(__wasi_size_t)); + +#if defined(HAS_IOVEC) + struct iovec iovs[iovs_len]; + const void* mem_check = copy_iov_to_host(runtime, _mem, iovs, wasi_iovs, iovs_len); + if (mem_check != m3Err_none) { + return mem_check; + } + + ssize_t ret = writev(fd, iovs, iovs_len); + if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); } + m3ApiWriteMem32(nwritten, ret); + m3ApiReturn(__WASI_ERRNO_SUCCESS); +#else + ssize_t res = 0; + for (__wasi_size_t i = 0; i < iovs_len; i++) { + void* addr = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf)); + size_t len = m3ApiReadMem32(&wasi_iovs[i].buf_len); + if (len == 0) continue; + m3ApiCheckMem(addr, len); + int ret = write (fd, addr, len); + if (ret < 0) m3ApiReturn(errno_to_wasi(errno)); + res += ret; + if ((size_t)ret < len) break; + } + m3ApiWriteMem32(nwritten, res); + m3ApiReturn(__WASI_ERRNO_SUCCESS); +#endif +} + +m3ApiRawFunction(m3_wasi_generic_fd_close) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t, fd) + + int ret = close(fd); + m3ApiReturn(ret == 0 ? __WASI_ERRNO_SUCCESS : ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_datasync) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t, fd) + +#if defined(_WIN32) + int ret = _commit(fd); +#elif defined(__APPLE__) + int ret = fsync(fd); +#elif defined(__ANDROID_API__) || defined(__OpenBSD__) || defined(__linux__) || defined(__EMSCRIPTEN__) + int ret = fdatasync(fd); +#else + int ret = __WASI_ERRNO_NOSYS; +#endif + m3ApiReturn(ret == 0 ? __WASI_ERRNO_SUCCESS : ret); +} + +m3ApiRawFunction(m3_wasi_generic_random_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (uint8_t * , buf) + m3ApiGetArg (__wasi_size_t , buf_len) + + m3ApiCheckMem(buf, buf_len); + + while (1) { + ssize_t retlen = 0; + +#if defined(__wasi__) || defined(__APPLE__) || defined(__ANDROID_API__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) + size_t reqlen = M3_MIN (buf_len, 256); +# if defined(__APPLE__) && (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR) + retlen = SecRandomCopyBytes(kSecRandomDefault, reqlen, buf) < 0 ? -1 : reqlen; +# else + retlen = getentropy(buf, reqlen) < 0 ? -1 : reqlen; +# endif +#elif defined(__FreeBSD__) || defined(__linux__) + retlen = getrandom(buf, buf_len, 0); +#elif defined(_WIN32) + if (RtlGenRandom(buf, buf_len) == TRUE) { + m3ApiReturn(__WASI_ERRNO_SUCCESS); + } +#else + m3ApiReturn(__WASI_ERRNO_NOSYS); +#endif + if (retlen < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + m3ApiReturn(errno_to_wasi(errno)); + } else if (retlen == buf_len) { + m3ApiReturn(__WASI_ERRNO_SUCCESS); + } else { + buf += retlen; + buf_len -= retlen; + } + } +} + +m3ApiRawFunction(m3_wasi_generic_clock_res_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_clockid_t , wasi_clk_id) + m3ApiGetArgMem (__wasi_timestamp_t * , resolution) + + m3ApiCheckMem(resolution, sizeof(__wasi_timestamp_t)); + + int clk = convert_clockid(wasi_clk_id); + if (clk < 0) m3ApiReturn(__WASI_ERRNO_INVAL); + + struct timespec tp; + if (clock_getres(clk, &tp) != 0) { + m3ApiWriteMem64(resolution, 1000000); + } else { + m3ApiWriteMem64(resolution, convert_timespec(&tp)); + } + + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_clock_time_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_clockid_t , wasi_clk_id) + m3ApiGetArg (__wasi_timestamp_t , precision) + m3ApiGetArgMem (__wasi_timestamp_t * , time) + + m3ApiCheckMem(time, sizeof(__wasi_timestamp_t)); + + int clk = convert_clockid(wasi_clk_id); + if (clk < 0) m3ApiReturn(__WASI_ERRNO_INVAL); + + struct timespec tp; + if (clock_gettime(clk, &tp) != 0) { + m3ApiReturn(errno_to_wasi(errno)); + } + + m3ApiWriteMem64(time, convert_timespec(&tp)); + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_proc_exit) +{ + m3ApiGetArg (uint32_t, code) + + m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata); + + if (context) { + context->exit_code = code; + } + + m3ApiTrap(m3Err_trapExit); +} + + +static +M3Result SuppressLookupFailure(M3Result i_result) +{ + if (i_result == m3Err_functionLookupFailed) + return m3Err_none; + else + return i_result; +} + +m3_wasi_context_t* m3_GetWasiContext() +{ + return wasi_context; +} + + +M3Result m3_LinkWASI (IM3Module module) +{ + M3Result result = m3Err_none; + +#ifdef _WIN32 + setmode(fileno(stdin), O_BINARY); + setmode(fileno(stdout), O_BINARY); + setmode(fileno(stderr), O_BINARY); + +#else + // Preopen dirs + for (int i = 3; i < PREOPEN_CNT; i++) { + preopen[i].fd = open(preopen[i].real_path, O_RDONLY); + } +#endif + + if (!wasi_context) { + wasi_context = (m3_wasi_context_t*)malloc(sizeof(m3_wasi_context_t)); + wasi_context->exit_code = 0; + wasi_context->argc = 0; + wasi_context->argv = 0; + } + + static const char* namespaces[2] = { "wasi_unstable", "wasi_snapshot_preview1" }; + + // Some functions are incompatible between WASI versions +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_seek", "i(iIi*)", &m3_wasi_unstable_fd_seek))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_seek", "i(iIi*)", &m3_wasi_snapshot_preview1_fd_seek))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_filestat_get", "i(i*)", &m3_wasi_unstable_fd_filestat_get))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_filestat_get", "i(i*)", &m3_wasi_snapshot_preview1_fd_filestat_get))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "path_filestat_get", "i(ii*i*)", &m3_wasi_unstable_path_filestat_get))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "path_filestat_get", "i(ii*i*)", &m3_wasi_snapshot_preview1_path_filestat_get))); + + for (int i=0; i<2; i++) + { + const char* wasi = namespaces[i]; + +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_get", "i(**)", &m3_wasi_generic_args_get, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_sizes_get", "i(**)", &m3_wasi_generic_args_sizes_get, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_res_get", "i(i*)", &m3_wasi_generic_clock_res_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_time_get", "i(iI*)", &m3_wasi_generic_clock_time_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_get", "i(**)", &m3_wasi_generic_environ_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_sizes_get", "i(**)", &m3_wasi_generic_environ_sizes_get))); + +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_advise", "i(iIIi)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_allocate", "i(iII)", ))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_close", "i(i)", &m3_wasi_generic_fd_close))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_datasync", "i(i)", &m3_wasi_generic_fd_datasync))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_get", "i(i*)", &m3_wasi_generic_fd_fdstat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_flags", "i(ii)", &m3_wasi_generic_fd_fdstat_set_flags))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_rights", "i(iII)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_size", "i(iI)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_times","i(iIIi)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pread", "i(i*iI*)",))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_get", "i(i*)", &m3_wasi_generic_fd_prestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_dir_name", "i(i*i)", &m3_wasi_generic_fd_prestat_dir_name))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pwrite", "i(i*iI*)",))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_read", "i(i*i*)", &m3_wasi_generic_fd_read))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_readdir", "i(i*iI*)",))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_renumber", "i(ii)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_sync", "i(i)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_tell", "i(i*)", ))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_write", "i(i*i*)", &m3_wasi_generic_fd_write))); + +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_create_directory", "i(i*i)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_set_times", "i(ii*iIIi)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_link", "i(ii*ii*i)", ))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_open", "i(ii*iiIIi*)", &m3_wasi_generic_path_open))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_readlink", "i(i*i*i*)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_remove_directory", "i(i*i)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_rename", "i(i*ii*i)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_symlink", "i(*ii*i)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_unlink_file", "i(i*i)", ))); + +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "poll_oneoff", "i(**i*)", &m3_wasi_generic_poll_oneoff))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "proc_exit", "v(i)", &m3_wasi_generic_proc_exit, wasi_context))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "proc_raise", "i(i)", ))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "random_get", "i(*i)", &m3_wasi_generic_random_get))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sched_yield", "i()", ))); + +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_recv", "i(i*ii**)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_send", "i(i*ii*)", ))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_shutdown", "i(ii)", ))); + } + +_catch: + return result; +} + +#endif // d_m3HasWASI diff --git a/cpp/m3_api_wasi.h b/cpp/m3_api_wasi.h new file mode 100644 index 0000000..5f7ffcd --- /dev/null +++ b/cpp/m3_api_wasi.h @@ -0,0 +1,38 @@ +// +// m3_api_wasi.h +// +// Created by Volodymyr Shymanskyy on 11/20/19. +// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. +// + +#ifndef m3_api_wasi_h +#define m3_api_wasi_h + +#include "m3_core.h" + +#if defined(d_m3HasUVWASI) +#include "uvwasi.h" +#endif + +d_m3BeginExternC + +typedef struct m3_wasi_context_t +{ + i32 exit_code; + u32 argc; + ccstr_t * argv; +} m3_wasi_context_t; + +M3Result m3_LinkWASI (IM3Module io_module); + +#if defined(d_m3HasUVWASI) + +M3Result m3_LinkWASIWithOptions (IM3Module io_module, uvwasi_options_t uvwasiOptions); + +#endif + +m3_wasi_context_t* m3_GetWasiContext(); + +d_m3EndExternC + +#endif // m3_api_wasi_h diff --git a/cpp/m3_bind.c b/cpp/m3_bind.c new file mode 100644 index 0000000..65c72ee --- /dev/null +++ b/cpp/m3_bind.c @@ -0,0 +1,176 @@ +// +// m3_bind.c +// +// Created by Steven Massey on 4/29/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#include "m3_env.h" +#include "m3_exception.h" +#include "m3_info.h" + + +u8 ConvertTypeCharToTypeId (char i_code) +{ + switch (i_code) { + case 'v': return c_m3Type_none; + case 'i': return c_m3Type_i32; + case 'I': return c_m3Type_i64; + case 'f': return c_m3Type_f32; + case 'F': return c_m3Type_f64; + case '*': return c_m3Type_i32; + } + return c_m3Type_unknown; +} + + +M3Result SignatureToFuncType (IM3FuncType * o_functionType, ccstr_t i_signature) +{ + IM3FuncType funcType = NULL; + +_try { + if (not o_functionType) + _throw ("null function type"); + + if (not i_signature) + _throw ("null function signature"); + + cstr_t sig = i_signature; + + size_t maxNumTypes = strlen (i_signature); + + // assume min signature is "()" + _throwif (m3Err_malformedFunctionSignature, maxNumTypes < 2); + maxNumTypes -= 2; + + _throwif (m3Err_tooManyArgsRets, maxNumTypes > d_m3MaxSaneFunctionArgRetCount); + +_ (AllocFuncType (& funcType, (u32) maxNumTypes)); + + u8 * typelist = funcType->types; + + bool parsingRets = true; + while (* sig) + { + char typeChar = * sig++; + + if (typeChar == '(') + { + parsingRets = false; + continue; + } + else if ( typeChar == ' ') + continue; + else if (typeChar == ')') + break; + + u8 type = ConvertTypeCharToTypeId (typeChar); + + _throwif ("unknown argument type char", c_m3Type_unknown == type); + + if (type == c_m3Type_none) + continue; + + if (parsingRets) + { + _throwif ("malformed signature; return count overflow", funcType->numRets >= maxNumTypes); + funcType->numRets++; + *typelist++ = type; + } + else + { + _throwif ("malformed signature; arg count overflow", (u32)(funcType->numRets) + funcType->numArgs >= maxNumTypes); + funcType->numArgs++; + *typelist++ = type; + } + } + +} _catch: + + if (result) + m3_Free (funcType); + + * o_functionType = funcType; + + return result; +} + + +static +M3Result ValidateSignature (IM3Function i_function, ccstr_t i_linkingSignature) +{ + M3Result result = m3Err_none; + + IM3FuncType ftype = NULL; +_ (SignatureToFuncType (& ftype, i_linkingSignature)); + + if (not AreFuncTypesEqual (ftype, i_function->funcType)) + { + m3log (module, "expected: %s", SPrintFuncTypeSignature (ftype)); + m3log (module, " found: %s", SPrintFuncTypeSignature (i_function->funcType)); + + _throw ("function signature mismatch"); + } + + _catch: + + m3_Free (ftype); + + return result; +} + + +M3Result FindAndLinkFunction (IM3Module io_module, + ccstr_t i_moduleName, + ccstr_t i_functionName, + ccstr_t i_signature, + voidptr_t i_function, + voidptr_t i_userdata) +{ +_try { + + _throwif(m3Err_moduleNotLinked, !io_module->runtime); + + const bool wildcardModule = (strcmp (i_moduleName, "*") == 0); + + result = m3Err_functionLookupFailed; + + for (u32 i = 0; i < io_module->numFunctions; ++i) + { + const IM3Function f = & io_module->functions [i]; + + if (f->import.moduleUtf8 and f->import.fieldUtf8) + { + if (strcmp (f->import.fieldUtf8, i_functionName) == 0 and + (wildcardModule or strcmp (f->import.moduleUtf8, i_moduleName) == 0)) + { + if (i_signature) { +_ (ValidateSignature (f, i_signature)); + } +_ (CompileRawFunction (io_module, f, i_function, i_userdata)); + } + } + } +} _catch: + return result; +} + +M3Result m3_LinkRawFunctionEx (IM3Module io_module, + const char * const i_moduleName, + const char * const i_functionName, + const char * const i_signature, + M3RawCall i_function, + const void * i_userdata) +{ + return FindAndLinkFunction (io_module, i_moduleName, i_functionName, i_signature, (voidptr_t)i_function, i_userdata); +} + +M3Result m3_LinkRawFunction (IM3Module io_module, + const char * const i_moduleName, + const char * const i_functionName, + const char * const i_signature, + M3RawCall i_function) +{ + return FindAndLinkFunction (io_module, i_moduleName, i_functionName, i_signature, (voidptr_t)i_function, NULL); +} + diff --git a/cpp/m3_bind.h b/cpp/m3_bind.h new file mode 100644 index 0000000..a80317a --- /dev/null +++ b/cpp/m3_bind.h @@ -0,0 +1,20 @@ +// +// m3_bind.h +// +// Created by Steven Massey on 2/27/20. +// Copyright © 2020 Steven Massey. All rights reserved. +// + +#ifndef m3_bind_h +#define m3_bind_h + +#include "m3_env.h" + +d_m3BeginExternC + +u8 ConvertTypeCharToTypeId (char i_code); +M3Result SignatureToFuncType (IM3FuncType * o_functionType, ccstr_t i_signature); + +d_m3EndExternC + +#endif /* m3_bind_h */ diff --git a/cpp/m3_code.c b/cpp/m3_code.c new file mode 100644 index 0000000..af273eb --- /dev/null +++ b/cpp/m3_code.c @@ -0,0 +1,246 @@ +// +// m3_code.c +// +// Created by Steven Massey on 4/19/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#include +#include "m3_code.h" +#include "m3_env.h" + +//--------------------------------------------------------------------------------------------------------------------------------- + + +IM3CodePage NewCodePage (IM3Runtime i_runtime, u32 i_minNumLines) +{ + IM3CodePage page; + + // check multiplication overflow + if (i_minNumLines > UINT_MAX / sizeof (code_t)) { + return NULL; + } + u32 pageSize = sizeof (M3CodePageHeader) + sizeof (code_t) * i_minNumLines; + + // check addition overflow + if (pageSize < sizeof (M3CodePageHeader)) { + return NULL; + } + + pageSize = (pageSize + (d_m3CodePageAlignSize-1)) & ~(d_m3CodePageAlignSize-1); // align + // check alignment overflow + if (pageSize == 0) { + return NULL; + } + + page = (IM3CodePage)m3_Malloc ("M3CodePage", pageSize); + + if (page) + { + page->info.sequence = ++i_runtime->newCodePageSequence; + page->info.numLines = (pageSize - sizeof (M3CodePageHeader)) / sizeof (code_t); + +#if d_m3RecordBacktraces + u32 pageSizeBt = sizeof (M3CodeMappingPage) + sizeof (M3CodeMapEntry) * page->info.numLines; + page->info.mapping = (M3CodeMappingPage *)m3_Malloc ("M3CodeMappingPage", pageSizeBt); + + if (page->info.mapping) + { + page->info.mapping->size = 0; + page->info.mapping->capacity = page->info.numLines; + } + else + { + m3_Free (page); + return NULL; + } + page->info.mapping->basePC = GetPageStartPC(page); +#endif // d_m3RecordBacktraces + + m3log (runtime, "new page: %p; seq: %d; bytes: %d; lines: %d", GetPagePC (page), page->info.sequence, pageSize, page->info.numLines); + } + + return page; +} + + +void FreeCodePages (IM3CodePage * io_list) +{ + IM3CodePage page = * io_list; + + while (page) + { + m3log (code, "free page: %d; %p; util: %3.1f%%", page->info.sequence, page, 100. * page->info.lineIndex / page->info.numLines); + + IM3CodePage next = page->info.next; +#if d_m3RecordBacktraces + m3_Free (page->info.mapping); +#endif // d_m3RecordBacktraces + m3_Free (page); + page = next; + } + + * io_list = NULL; +} + + +u32 NumFreeLines (IM3CodePage i_page) +{ + d_m3Assert (i_page->info.lineIndex <= i_page->info.numLines); + + return i_page->info.numLines - i_page->info.lineIndex; +} + + +void EmitWord_impl (IM3CodePage i_page, void * i_word) +{ d_m3Assert (i_page->info.lineIndex+1 <= i_page->info.numLines); + i_page->code [i_page->info.lineIndex++] = i_word; +} + +void EmitWord32 (IM3CodePage i_page, const u32 i_word) +{ d_m3Assert (i_page->info.lineIndex+1 <= i_page->info.numLines); + memcpy (& i_page->code[i_page->info.lineIndex++], & i_word, sizeof(i_word)); +} + +void EmitWord64 (IM3CodePage i_page, const u64 i_word) +{ +#if M3_SIZEOF_PTR == 4 + d_m3Assert (i_page->info.lineIndex+2 <= i_page->info.numLines); + memcpy (& i_page->code[i_page->info.lineIndex], & i_word, sizeof(i_word)); + i_page->info.lineIndex += 2; +#else + d_m3Assert (i_page->info.lineIndex+1 <= i_page->info.numLines); + memcpy (& i_page->code[i_page->info.lineIndex], & i_word, sizeof(i_word)); + i_page->info.lineIndex += 1; +#endif +} + + +#if d_m3RecordBacktraces +void EmitMappingEntry (IM3CodePage i_page, u32 i_moduleOffset) +{ + M3CodeMappingPage * page = i_page->info.mapping; + d_m3Assert (page->size < page->capacity); + + M3CodeMapEntry * entry = & page->entries[page->size++]; + pc_t pc = GetPagePC (i_page); + + entry->pcOffset = pc - page->basePC; + entry->moduleOffset = i_moduleOffset; +} +#endif // d_m3RecordBacktraces + +pc_t GetPageStartPC (IM3CodePage i_page) +{ + return & i_page->code [0]; +} + + +pc_t GetPagePC (IM3CodePage i_page) +{ + if (i_page) + return & i_page->code [i_page->info.lineIndex]; + else + return NULL; +} + + +void PushCodePage (IM3CodePage * i_list, IM3CodePage i_codePage) +{ + IM3CodePage next = * i_list; + i_codePage->info.next = next; + * i_list = i_codePage; +} + + +IM3CodePage PopCodePage (IM3CodePage * i_list) +{ + IM3CodePage page = * i_list; + * i_list = page->info.next; + page->info.next = NULL; + + return page; +} + + + +u32 FindCodePageEnd (IM3CodePage i_list, IM3CodePage * o_end) +{ + u32 numPages = 0; + * o_end = NULL; + + while (i_list) + { + * o_end = i_list; + ++numPages; + i_list = i_list->info.next; + } + + return numPages; +} + + +u32 CountCodePages (IM3CodePage i_list) +{ + IM3CodePage unused; + return FindCodePageEnd (i_list, & unused); +} + + +IM3CodePage GetEndCodePage (IM3CodePage i_list) +{ + IM3CodePage end; + FindCodePageEnd (i_list, & end); + + return end; +} + +#if d_m3RecordBacktraces +bool ContainsPC (IM3CodePage i_page, pc_t i_pc) +{ + return GetPageStartPC (i_page) <= i_pc && i_pc < GetPagePC (i_page); +} + + +bool MapPCToOffset (IM3CodePage i_page, pc_t i_pc, u32 * o_moduleOffset) +{ + M3CodeMappingPage * mapping = i_page->info.mapping; + + u32 pcOffset = i_pc - mapping->basePC; + + u32 left = 0; + u32 right = mapping->size; + + while (left < right) + { + u32 mid = left + (right - left) / 2; + + if (mapping->entries[mid].pcOffset < pcOffset) + { + left = mid + 1; + } + else if (mapping->entries[mid].pcOffset > pcOffset) + { + right = mid; + } + else + { + *o_moduleOffset = mapping->entries[mid].moduleOffset; + return true; + } + } + + // Getting here means left is now one more than the element we want. + if (left > 0) + { + left--; + *o_moduleOffset = mapping->entries[left].moduleOffset; + return true; + } + else return false; +} +#endif // d_m3RecordBacktraces + +//--------------------------------------------------------------------------------------------------------------------------------- + + diff --git a/cpp/m3_code.h b/cpp/m3_code.h new file mode 100644 index 0000000..25dcb81 --- /dev/null +++ b/cpp/m3_code.h @@ -0,0 +1,80 @@ +// +// m3_code.h +// +// Created by Steven Massey on 4/19/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#ifndef m3_code_h +#define m3_code_h + +#include "m3_core.h" + +d_m3BeginExternC + +typedef struct M3CodePage +{ + M3CodePageHeader info; + code_t code [1]; +} +M3CodePage; + +typedef M3CodePage * IM3CodePage; + + +IM3CodePage NewCodePage (IM3Runtime i_runtime, u32 i_minNumLines); + +void FreeCodePages (IM3CodePage * io_list); + +u32 NumFreeLines (IM3CodePage i_page); +pc_t GetPageStartPC (IM3CodePage i_page); +pc_t GetPagePC (IM3CodePage i_page); +void EmitWord_impl (IM3CodePage i_page, void* i_word); +void EmitWord32 (IM3CodePage i_page, u32 i_word); +void EmitWord64 (IM3CodePage i_page, u64 i_word); +# if d_m3RecordBacktraces +void EmitMappingEntry (IM3CodePage i_page, u32 i_moduleOffset); +# endif // d_m3RecordBacktraces + +void PushCodePage (IM3CodePage * io_list, IM3CodePage i_codePage); +IM3CodePage PopCodePage (IM3CodePage * io_list); + +IM3CodePage GetEndCodePage (IM3CodePage i_list); // i_list = NULL is valid +u32 CountCodePages (IM3CodePage i_list); // i_list = NULL is valid + +# if d_m3RecordBacktraces +bool ContainsPC (IM3CodePage i_page, pc_t i_pc); +bool MapPCToOffset (IM3CodePage i_page, pc_t i_pc, u32 * o_moduleOffset); +# endif // d_m3RecordBacktraces + +# ifdef DEBUG +void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC); +# endif + +#define EmitWord(page, val) EmitWord_impl(page, (void*)(val)) + +//--------------------------------------------------------------------------------------------------------------------------------- + +# if d_m3RecordBacktraces + +typedef struct M3CodeMapEntry +{ + u32 pcOffset; + u32 moduleOffset; +} +M3CodeMapEntry; + +typedef struct M3CodeMappingPage +{ + pc_t basePC; + u32 size; + u32 capacity; + M3CodeMapEntry entries []; +} +M3CodeMappingPage; + +# endif // d_m3RecordBacktraces + +d_m3EndExternC + +#endif // m3_code_h diff --git a/cpp/m3_compile.c b/cpp/m3_compile.c new file mode 100644 index 0000000..9d87937 --- /dev/null +++ b/cpp/m3_compile.c @@ -0,0 +1,2928 @@ +// +// m3_compile.c +// +// Created by Steven Massey on 4/17/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +// Allow using opcodes for compilation process +#define M3_COMPILE_OPCODES + +#include "m3_env.h" +#include "m3_compile.h" +#include "m3_exec.h" +#include "m3_exception.h" +#include "m3_info.h" + +//----- EMIT -------------------------------------------------------------------------------------------------------------- + +static inline +pc_t GetPC (IM3Compilation o) +{ + return GetPagePC (o->page); +} + +static M3_NOINLINE +M3Result EnsureCodePageNumLines (IM3Compilation o, u32 i_numLines) +{ + M3Result result = m3Err_none; + + i_numLines += 2; // room for Bridge + + if (NumFreeLines (o->page) < i_numLines) + { + IM3CodePage page = AcquireCodePageWithCapacity (o->runtime, i_numLines); + + if (page) + { + m3log (emit, "bridging new code page from: %d %p (free slots: %d) to: %d", o->page->info.sequence, GetPC (o), NumFreeLines (o->page), page->info.sequence); + d_m3Assert (NumFreeLines (o->page) >= 2); + + EmitWord (o->page, op_Branch); + EmitWord (o->page, GetPagePC (page)); + + ReleaseCodePage (o->runtime, o->page); + + o->page = page; + } + else result = m3Err_mallocFailedCodePage; + } + + return result; +} + +static M3_NOINLINE +M3Result EmitOp (IM3Compilation o, IM3Operation i_operation) +{ + M3Result result = m3Err_none; d_m3Assert (i_operation or IsStackPolymorphic (o)); + + // it's OK for page to be null; when compile-walking the bytecode without emitting + if (o->page) + { +# if d_m3EnableOpTracing + if (i_operation != op_DumpStack) + o->numEmits++; +# endif + + // have execution jump to a new page if slots are critically low + result = EnsureCodePageNumLines (o, d_m3CodePageFreeLinesThreshold); + + if (not result) + { if (d_m3LogEmit) log_emit (o, i_operation); +# if d_m3RecordBacktraces + EmitMappingEntry (o->page, o->lastOpcodeStart - o->module->wasmStart); +# endif // d_m3RecordBacktraces + EmitWord (o->page, i_operation); + } + } + + return result; +} + +// Push an immediate constant into the M3 codestream +static M3_NOINLINE +void EmitConstant32 (IM3Compilation o, const u32 i_immediate) +{ + if (o->page) + EmitWord32 (o->page, i_immediate); +} + +static M3_NOINLINE +void EmitSlotOffset (IM3Compilation o, const i32 i_offset) +{ + if (o->page) + EmitWord32 (o->page, i_offset); +} + +static M3_NOINLINE +pc_t EmitPointer (IM3Compilation o, const void * const i_pointer) +{ + pc_t ptr = GetPagePC (o->page); + + if (o->page) + EmitWord (o->page, i_pointer); + + return ptr; +} + +static M3_NOINLINE +void * ReservePointer (IM3Compilation o) +{ + pc_t ptr = GetPagePC (o->page); + EmitPointer (o, NULL); + return (void *) ptr; +} + + +//------------------------------------------------------------------------------------------------------------------------- + +#define d_indent " | %s" + +// just want less letters and numbers to stare at down the way in the compiler table +#define i_32 c_m3Type_i32 +#define i_64 c_m3Type_i64 +#define f_32 c_m3Type_f32 +#define f_64 c_m3Type_f64 +#define none c_m3Type_none +#define any (u8)-1 + +#if d_m3HasFloat +# define FPOP(x) x +#else +# define FPOP(x) NULL +#endif + +static const IM3Operation c_preserveSetSlot [] = { NULL, op_PreserveSetSlot_i32, op_PreserveSetSlot_i64, + FPOP(op_PreserveSetSlot_f32), FPOP(op_PreserveSetSlot_f64) }; +static const IM3Operation c_setSetOps [] = { NULL, op_SetSlot_i32, op_SetSlot_i64, + FPOP(op_SetSlot_f32), FPOP(op_SetSlot_f64) }; +static const IM3Operation c_setGlobalOps [] = { NULL, op_SetGlobal_i32, op_SetGlobal_i64, + FPOP(op_SetGlobal_f32), FPOP(op_SetGlobal_f64) }; +static const IM3Operation c_setRegisterOps [] = { NULL, op_SetRegister_i32, op_SetRegister_i64, + FPOP(op_SetRegister_f32), FPOP(op_SetRegister_f64) }; + +static const IM3Operation c_intSelectOps [2] [4] = { { op_Select_i32_rss, op_Select_i32_srs, op_Select_i32_ssr, op_Select_i32_sss }, + { op_Select_i64_rss, op_Select_i64_srs, op_Select_i64_ssr, op_Select_i64_sss } }; +#if d_m3HasFloat +static const IM3Operation c_fpSelectOps [2] [2] [3] = { { { op_Select_f32_sss, op_Select_f32_srs, op_Select_f32_ssr }, // selector in slot + { op_Select_f32_rss, op_Select_f32_rrs, op_Select_f32_rsr } }, // selector in reg + { { op_Select_f64_sss, op_Select_f64_srs, op_Select_f64_ssr }, // selector in slot + { op_Select_f64_rss, op_Select_f64_rrs, op_Select_f64_rsr } } }; // selector in reg +#endif + +// all args & returns are 64-bit aligned, so use 2 slots for a d_m3Use32BitSlots=1 build +static const u16 c_ioSlotCount = sizeof (u64) / sizeof (m3slot_t); + +static +M3Result AcquireCompilationCodePage (IM3Compilation o, IM3CodePage * o_codePage) +{ + M3Result result = m3Err_none; + + IM3CodePage page = AcquireCodePage (o->runtime); + + if (page) + { +# if (d_m3EnableCodePageRefCounting) + { + if (o->function) + { + IM3Function func = o->function; + page->info.usageCount++; + + u32 index = func->numCodePageRefs++; +_ (m3ReallocArray (& func->codePageRefs, IM3CodePage, func->numCodePageRefs, index)); + func->codePageRefs [index] = page; + } + } +# endif + } + else _throw (m3Err_mallocFailedCodePage); + + _catch: + + * o_codePage = page; + + return result; +} + +static inline +void ReleaseCompilationCodePage (IM3Compilation o) +{ + ReleaseCodePage (o->runtime, o->page); +} + +static inline +u16 GetTypeNumSlots (u8 i_type) +{ +# if d_m3Use32BitSlots + return Is64BitType (i_type) ? 2 : 1; +# else + return 1; +# endif +} + +static inline +void AlignSlotToType (u16 * io_slot, u8 i_type) +{ + // align 64-bit words to even slots (if d_m3Use32BitSlots) + u16 numSlots = GetTypeNumSlots (i_type); + + u16 mask = numSlots - 1; + * io_slot = (* io_slot + mask) & ~mask; +} + +static inline +i16 GetStackTopIndex (IM3Compilation o) +{ d_m3Assert (o->stackIndex > o->stackFirstDynamicIndex or IsStackPolymorphic (o)); + return o->stackIndex - 1; +} + + +// Items in the static portion of the stack (args/locals) are hidden from GetStackTypeFromTop () +// In other words, only "real" Wasm stack items can be inspected. This is important when +// returning values, etc. and you need an accurate wasm-view of the stack. +static +u8 GetStackTypeFromTop (IM3Compilation o, u16 i_offset) +{ + u8 type = c_m3Type_none; + + ++i_offset; + if (o->stackIndex >= i_offset) + { + u16 index = o->stackIndex - i_offset; + + if (index >= o->stackFirstDynamicIndex) + type = o->typeStack [index]; + } + + return type; +} + +static inline +u8 GetStackTopType (IM3Compilation o) +{ + return GetStackTypeFromTop (o, 0); +} + +static inline +u8 GetStackTypeFromBottom (IM3Compilation o, u16 i_offset) +{ + u8 type = c_m3Type_none; + + if (i_offset < o->stackIndex) + type = o->typeStack [i_offset]; + + return type; +} + + +static inline bool IsConstantSlot (IM3Compilation o, u16 i_slot) { return (i_slot >= o->slotFirstConstIndex and i_slot < o->slotMaxConstIndex); } +static inline bool IsSlotAllocated (IM3Compilation o, u16 i_slot) { return o->m3Slots [i_slot]; } + +static inline +bool IsStackIndexInRegister (IM3Compilation o, i32 i_stackIndex) +{ d_m3Assert (i_stackIndex < o->stackIndex or IsStackPolymorphic (o)); + if (i_stackIndex >= 0 and i_stackIndex < o->stackIndex) + return (o->wasmStack [i_stackIndex] >= d_m3Reg0SlotAlias); + else + return false; +} + +static inline u16 GetNumBlockValuesOnStack (IM3Compilation o) { return o->stackIndex - o->block.blockStackIndex; } + +static inline bool IsStackTopInRegister (IM3Compilation o) { return IsStackIndexInRegister (o, (i32) GetStackTopIndex (o)); } +static inline bool IsStackTopMinus1InRegister (IM3Compilation o) { return IsStackIndexInRegister (o, (i32) GetStackTopIndex (o) - 1); } +static inline bool IsStackTopMinus2InRegister (IM3Compilation o) { return IsStackIndexInRegister (o, (i32) GetStackTopIndex (o) - 2); } + +static inline bool IsStackTopInSlot (IM3Compilation o) { return not IsStackTopInRegister (o); } + +static inline bool IsValidSlot (u16 i_slot) { return (i_slot < d_m3MaxFunctionSlots); } + +static inline +u16 GetStackTopSlotNumber (IM3Compilation o) +{ + i16 i = GetStackTopIndex (o); + + u16 slot = c_slotUnused; + + if (i >= 0) + slot = o->wasmStack [i]; + + return slot; +} + + +// from bottom +static inline +u16 GetSlotForStackIndex (IM3Compilation o, u16 i_stackIndex) +{ d_m3Assert (i_stackIndex < o->stackIndex or IsStackPolymorphic (o)); + u16 slot = c_slotUnused; + + if (i_stackIndex < o->stackIndex) + slot = o->wasmStack [i_stackIndex]; + + return slot; +} + +static inline +u16 GetExtraSlotForStackIndex (IM3Compilation o, u16 i_stackIndex) +{ + u16 baseSlot = GetSlotForStackIndex (o, i_stackIndex); + + if (baseSlot != c_slotUnused) + { + u16 extraSlot = GetTypeNumSlots (GetStackTypeFromBottom (o, i_stackIndex)) - 1; + baseSlot += extraSlot; + } + + return baseSlot; +} + + +static inline +void TouchSlot (IM3Compilation o, u16 i_slot) +{ + // op_Entry uses this value to track and detect stack overflow + o->maxStackSlots = M3_MAX (o->maxStackSlots, i_slot + 1); +} + +static inline +void MarkSlotAllocated (IM3Compilation o, u16 i_slot) +{ d_m3Assert (o->m3Slots [i_slot] == 0); // shouldn't be already allocated + o->m3Slots [i_slot] = 1; + + o->slotMaxAllocatedIndexPlusOne = M3_MAX (o->slotMaxAllocatedIndexPlusOne, i_slot + 1); + + TouchSlot (o, i_slot); +} + +static inline +void MarkSlotsAllocated (IM3Compilation o, u16 i_slot, u16 i_numSlots) +{ + while (i_numSlots--) + MarkSlotAllocated (o, i_slot++); +} + +static inline +void MarkSlotsAllocatedByType (IM3Compilation o, u16 i_slot, u8 i_type) +{ + u16 numSlots = GetTypeNumSlots (i_type); + MarkSlotsAllocated (o, i_slot, numSlots); +} + + +static +M3Result AllocateSlotsWithinRange (IM3Compilation o, u16 * o_slot, u8 i_type, u16 i_startSlot, u16 i_endSlot) +{ + M3Result result = m3Err_functionStackOverflow; + + u16 numSlots = GetTypeNumSlots (i_type); + u16 searchOffset = numSlots - 1; + + AlignSlotToType (& i_startSlot, i_type); + + // search for 1 or 2 consecutive slots in the execution stack + u16 i = i_startSlot; + while (i + searchOffset < i_endSlot) + { + if (o->m3Slots [i] == 0 and o->m3Slots [i + searchOffset] == 0) + { + MarkSlotsAllocated (o, i, numSlots); + + * o_slot = i; + result = m3Err_none; + break; + } + + // keep 2-slot allocations even-aligned + i += numSlots; + } + + return result; +} + +static inline +M3Result AllocateSlots (IM3Compilation o, u16 * o_slot, u8 i_type) +{ + return AllocateSlotsWithinRange (o, o_slot, i_type, o->slotFirstDynamicIndex, d_m3MaxFunctionSlots); +} + +static inline +M3Result AllocateConstantSlots (IM3Compilation o, u16 * o_slot, u8 i_type) +{ + u16 maxTableIndex = o->slotFirstConstIndex + d_m3MaxConstantTableSize; + return AllocateSlotsWithinRange (o, o_slot, i_type, o->slotFirstConstIndex, M3_MIN(o->slotFirstDynamicIndex, maxTableIndex)); +} + + +// TOQUE: this usage count system could be eliminated. real world code doesn't frequently trigger it. just copy to multiple +// unique slots. +static inline +M3Result IncrementSlotUsageCount (IM3Compilation o, u16 i_slot) +{ d_m3Assert (i_slot < d_m3MaxFunctionSlots); + M3Result result = m3Err_none; d_m3Assert (o->m3Slots [i_slot] > 0); + + // OPTZ (memory): 'm3Slots' could still be fused with 'typeStack' if 4 bits were used to indicate: [0,1,2,many]. The many-case + // would scan 'wasmStack' to determine the actual usage count + if (o->m3Slots [i_slot] < 0xFF) + { + o->m3Slots [i_slot]++; + } + else result = "slot usage count overflow"; + + return result; +} + +static inline +void DeallocateSlot (IM3Compilation o, i16 i_slot, u8 i_type) +{ d_m3Assert (i_slot >= o->slotFirstDynamicIndex); + d_m3Assert (i_slot < o->slotMaxAllocatedIndexPlusOne); + for (u16 i = 0; i < GetTypeNumSlots (i_type); ++i, ++i_slot) + { d_m3Assert (o->m3Slots [i_slot]); + -- o->m3Slots [i_slot]; + } +} + + +static inline +bool IsRegisterTypeAllocated (IM3Compilation o, u8 i_type) +{ + return IsRegisterAllocated (o, IsFpType (i_type)); +} + +static inline +void AllocateRegister (IM3Compilation o, u32 i_register, u16 i_stackIndex) +{ d_m3Assert (not IsRegisterAllocated (o, i_register)); + o->regStackIndexPlusOne [i_register] = i_stackIndex + 1; +} + +static inline +void DeallocateRegister (IM3Compilation o, u32 i_register) +{ d_m3Assert (IsRegisterAllocated (o, i_register)); + o->regStackIndexPlusOne [i_register] = c_m3RegisterUnallocated; +} + +static inline +u16 GetRegisterStackIndex (IM3Compilation o, u32 i_register) +{ d_m3Assert (IsRegisterAllocated (o, i_register)); + return o->regStackIndexPlusOne [i_register] - 1; +} + +u16 GetMaxUsedSlotPlusOne (IM3Compilation o) +{ + while (o->slotMaxAllocatedIndexPlusOne > o->slotFirstDynamicIndex) + { + if (IsSlotAllocated (o, o->slotMaxAllocatedIndexPlusOne - 1)) + break; + + o->slotMaxAllocatedIndexPlusOne--; + } + +# ifdef DEBUG + u16 maxSlot = o->slotMaxAllocatedIndexPlusOne; + while (maxSlot < d_m3MaxFunctionSlots) + { + d_m3Assert (o->m3Slots [maxSlot] == 0); + maxSlot++; + } +# endif + + return o->slotMaxAllocatedIndexPlusOne; +} + +static +M3Result PreserveRegisterIfOccupied (IM3Compilation o, u8 i_registerType) +{ + M3Result result = m3Err_none; + + u32 regSelect = IsFpType (i_registerType); + + if (IsRegisterAllocated (o, regSelect)) + { + u16 stackIndex = GetRegisterStackIndex (o, regSelect); + DeallocateRegister (o, regSelect); + + u8 type = GetStackTypeFromBottom (o, stackIndex); + + // and point to a exec slot + u16 slot = c_slotUnused; +_ (AllocateSlots (o, & slot, type)); + o->wasmStack [stackIndex] = slot; + +_ (EmitOp (o, c_setSetOps [type])); + EmitSlotOffset (o, slot); + } + + _catch: return result; +} + + +// all values must be in slots before entering loop, if, and else blocks +// otherwise they'd end up preserve-copied in the block to probably different locations (if/else) +static inline +M3Result PreserveRegisters (IM3Compilation o) +{ + M3Result result; + +_ (PreserveRegisterIfOccupied (o, c_m3Type_f64)); +_ (PreserveRegisterIfOccupied (o, c_m3Type_i64)); + + _catch: return result; +} + +static +M3Result PreserveNonTopRegisters (IM3Compilation o) +{ + M3Result result = m3Err_none; + + i16 stackTop = GetStackTopIndex (o); + + if (stackTop >= 0) + { + if (IsRegisterAllocated (o, 0)) // r0 + { + if (GetRegisterStackIndex (o, 0) != stackTop) +_ (PreserveRegisterIfOccupied (o, c_m3Type_i64)); + } + + if (IsRegisterAllocated (o, 1)) // fp0 + { + if (GetRegisterStackIndex (o, 1) != stackTop) +_ (PreserveRegisterIfOccupied (o, c_m3Type_f64)); + } + } + + _catch: return result; +} + + +//---------------------------------------------------------------------------------------------------------------------- + +static +M3Result Push (IM3Compilation o, u8 i_type, u16 i_slot) +{ + M3Result result = m3Err_none; + +#if !d_m3HasFloat + if (i_type == c_m3Type_f32 || i_type == c_m3Type_f64) { + return m3Err_unknownOpcode; + } +#endif + + u16 stackIndex = o->stackIndex++; // printf ("push: %d\n", (i32) i); + + if (stackIndex < d_m3MaxFunctionStackHeight) + { + o->wasmStack [stackIndex] = i_slot; + o->typeStack [stackIndex] = i_type; + + if (IsRegisterSlotAlias (i_slot)) + { + u32 regSelect = IsFpRegisterSlotAlias (i_slot); + AllocateRegister (o, regSelect, stackIndex); + } + + if (d_m3LogWasmStack) dump_type_stack (o); + } + else result = m3Err_functionStackOverflow; + + return result; +} + +static inline +M3Result PushRegister (IM3Compilation o, u8 i_type) +{ + M3Result result = m3Err_none; d_m3Assert ((u16) d_m3Reg0SlotAlias > (u16) d_m3MaxFunctionSlots); + u16 slot = IsFpType (i_type) ? d_m3Fp0SlotAlias : d_m3Reg0SlotAlias; d_m3Assert (i_type or IsStackPolymorphic (o)); + +_ (Push (o, i_type, slot)); + + _catch: return result; +} + +static +M3Result Pop (IM3Compilation o) +{ + M3Result result = m3Err_none; + + if (o->stackIndex > o->block.blockStackIndex) + { + o->stackIndex--; // printf ("pop: %d\n", (i32) o->stackIndex); + + u16 slot = o->wasmStack [o->stackIndex]; + u8 type = o->typeStack [o->stackIndex]; + + if (IsRegisterSlotAlias (slot)) + { + u32 regSelect = IsFpRegisterSlotAlias (slot); + DeallocateRegister (o, regSelect); + } + else if (slot >= o->slotFirstDynamicIndex) + { + DeallocateSlot (o, slot, type); + } + } + else if (not IsStackPolymorphic (o)) + result = m3Err_functionStackUnderrun; + + return result; +} + +static +M3Result PopType (IM3Compilation o, u8 i_type) +{ + M3Result result = m3Err_none; + + u8 topType = GetStackTopType (o); + + if (i_type == topType or o->block.isPolymorphic) + { +_ (Pop (o)); + } + else _throw (m3Err_typeMismatch); + + _catch: + return result; +} + +static +M3Result _PushAllocatedSlotAndEmit (IM3Compilation o, u8 i_type, bool i_doEmit) +{ + M3Result result = m3Err_none; + + u16 slot = c_slotUnused; + +_ (AllocateSlots (o, & slot, i_type)); +_ (Push (o, i_type, slot)); + + if (i_doEmit) + EmitSlotOffset (o, slot); + +// printf ("push: %d\n", (u32) slot); + + _catch: return result; +} + +static inline +M3Result PushAllocatedSlotAndEmit (IM3Compilation o, u8 i_type) +{ + return _PushAllocatedSlotAndEmit (o, i_type, true); +} + +static inline +M3Result PushAllocatedSlot (IM3Compilation o, u8 i_type) +{ + return _PushAllocatedSlotAndEmit (o, i_type, false); +} + +static +M3Result PushConst (IM3Compilation o, u64 i_word, u8 i_type) +{ + M3Result result = m3Err_none; + + // Early-exit if we're not emitting + if (!o->page) return result; + + bool matchFound = false; + bool is64BitType = Is64BitType (i_type); + + u16 numRequiredSlots = GetTypeNumSlots (i_type); + u16 numUsedConstSlots = o->slotMaxConstIndex - o->slotFirstConstIndex; + + // search for duplicate matching constant slot to reuse + if (numRequiredSlots == 2 and numUsedConstSlots >= 2) + { + u16 firstConstSlot = o->slotFirstConstIndex; + AlignSlotToType (& firstConstSlot, c_m3Type_i64); + + for (u16 slot = firstConstSlot; slot < o->slotMaxConstIndex - 1; slot += 2) + { + if (IsSlotAllocated (o, slot) and IsSlotAllocated (o, slot + 1)) + { + u64 constant; + memcpy (&constant, &o->constants [slot - o->slotFirstConstIndex], sizeof(constant)); + + if (constant == i_word) + { + matchFound = true; +_ (Push (o, i_type, slot)); + break; + } + } + } + } + else if (numRequiredSlots == 1) + { + for (u16 i = 0; i < numUsedConstSlots; ++i) + { + u16 slot = o->slotFirstConstIndex + i; + + if (IsSlotAllocated (o, slot)) + { + bool matches; + if (is64BitType) { + u64 constant; + memcpy (&constant, &o->constants [i], sizeof(constant)); + matches = (constant == i_word); + } else { + u32 constant; + memcpy (&constant, &o->constants [i], sizeof(constant)); + matches = (constant == i_word); + } + if (matches) + { + matchFound = true; +_ (Push (o, i_type, slot)); + break; + } + } + } + } + + if (not matchFound) + { + u16 slot = c_slotUnused; + result = AllocateConstantSlots (o, & slot, i_type); + + if (result || slot == c_slotUnused) // no more constant table space; use inline constants + { + result = m3Err_none; + + if (is64BitType) { +_ (EmitOp (o, op_Const64)); + EmitWord64 (o->page, i_word); + } else { +_ (EmitOp (o, op_Const32)); + EmitWord32 (o->page, (u32) i_word); + } + +_ (PushAllocatedSlotAndEmit (o, i_type)); + } + else + { + u16 constTableIndex = slot - o->slotFirstConstIndex; + + d_m3Assert(constTableIndex < d_m3MaxConstantTableSize); + + if (is64BitType) { + memcpy (& o->constants [constTableIndex], &i_word, sizeof(i_word)); + } else { + u32 word32 = i_word; + memcpy (& o->constants [constTableIndex], &word32, sizeof(word32)); + } + +_ (Push (o, i_type, slot)); + + o->slotMaxConstIndex = M3_MAX (slot + numRequiredSlots, o->slotMaxConstIndex); + } + } + + _catch: return result; +} + +static inline +M3Result EmitSlotNumOfStackTopAndPop (IM3Compilation o) +{ + // no emit if value is in register + if (IsStackTopInSlot (o)) + EmitSlotOffset (o, GetStackTopSlotNumber (o)); + + return Pop (o); +} + + +// Or, maybe: EmitTrappingOp +M3Result AddTrapRecord (IM3Compilation o) +{ + M3Result result = m3Err_none; + + if (o->function) + { + } + + return result; +} + +static +M3Result UnwindBlockStack (IM3Compilation o) +{ + M3Result result = m3Err_none; + + u32 popCount = 0; + while (o->stackIndex > o->block.blockStackIndex) + { +_ (Pop (o)); + ++popCount; + } + + if (popCount) + { + m3log (compile, "unwound stack top: %d", popCount); + } + + _catch: return result; +} + +static inline +M3Result SetStackPolymorphic (IM3Compilation o) +{ + o->block.isPolymorphic = true; m3log (compile, "stack set polymorphic"); + return UnwindBlockStack (o); +} + +static +void PatchBranches (IM3Compilation o) +{ + pc_t pc = GetPC (o); + + pc_t patches = o->block.patches; + o->block.patches = NULL; + + while (patches) + { m3log (compile, "patching location: %p to pc: %p", patches, pc); + pc_t next = * (pc_t *) patches; + * (pc_t *) patches = pc; + patches = next; + } +} + +//------------------------------------------------------------------------------------------------------------------------- + +static +M3Result CopyStackIndexToSlot (IM3Compilation o, u16 i_destSlot, u16 i_stackIndex) // NoPushPop +{ + M3Result result = m3Err_none; + + IM3Operation op; + + u8 type = GetStackTypeFromBottom (o, i_stackIndex); + bool inRegister = IsStackIndexInRegister (o, i_stackIndex); + + if (inRegister) + { + op = c_setSetOps [type]; + } + else op = Is64BitType (type) ? op_CopySlot_64 : op_CopySlot_32; + +_ (EmitOp (o, op)); + EmitSlotOffset (o, i_destSlot); + + if (not inRegister) + { + u16 srcSlot = GetSlotForStackIndex (o, i_stackIndex); + EmitSlotOffset (o, srcSlot); + } + + _catch: return result; +} + +static +M3Result CopyStackTopToSlot (IM3Compilation o, u16 i_destSlot) // NoPushPop +{ + M3Result result; + + i16 stackTop = GetStackTopIndex (o); +_ (CopyStackIndexToSlot (o, i_destSlot, (u16) stackTop)); + + _catch: return result; +} + + +// a copy-on-write strategy is used with locals. when a get local occurs, it's not copied anywhere. the stack +// entry just has a index pointer to that local memory slot. +// then, when a previously referenced local is set, the current value needs to be preserved for those references + +// TODO: consider getting rid of these specialized operations: PreserveSetSlot & PreserveCopySlot. +// They likely just take up space (which seems to reduce performance) without improving performance. +static +M3Result PreservedCopyTopSlot (IM3Compilation o, u16 i_destSlot, u16 i_preserveSlot) +{ + M3Result result = m3Err_none; d_m3Assert (i_destSlot != i_preserveSlot); + + IM3Operation op; + + u8 type = GetStackTopType (o); + + if (IsStackTopInRegister (o)) + { + op = c_preserveSetSlot [type]; + } + else op = Is64BitType (type) ? op_PreserveCopySlot_64 : op_PreserveCopySlot_32; + +_ (EmitOp (o, op)); + EmitSlotOffset (o, i_destSlot); + + if (IsStackTopInSlot (o)) + EmitSlotOffset (o, GetStackTopSlotNumber (o)); + + EmitSlotOffset (o, i_preserveSlot); + + _catch: return result; +} + +static +M3Result CopyStackTopToRegister (IM3Compilation o, bool i_updateStack) +{ + M3Result result = m3Err_none; + + if (IsStackTopInSlot (o)) + { + u8 type = GetStackTopType (o); + +_ (PreserveRegisterIfOccupied (o, type)); + + IM3Operation op = c_setRegisterOps [type]; + +_ (EmitOp (o, op)); + EmitSlotOffset (o, GetStackTopSlotNumber (o)); + + if (i_updateStack) + { +_ (PopType (o, type)); +_ (PushRegister (o, type)); + } + } + + _catch: return result; +} + + +// if local is unreferenced, o_preservedSlotNumber will be equal to localIndex on return +static +M3Result FindReferencedLocalWithinCurrentBlock (IM3Compilation o, u16 * o_preservedSlotNumber, u32 i_localSlot) +{ + M3Result result = m3Err_none; + + IM3CompilationScope scope = & o->block; + u16 startIndex = scope->blockStackIndex; + + while (scope->opcode == c_waOp_block) + { + scope = scope->outer; + if (not scope) + break; + + startIndex = scope->blockStackIndex; + } + + * o_preservedSlotNumber = (u16) i_localSlot; + + for (u32 i = startIndex; i < o->stackIndex; ++i) + { + if (o->wasmStack [i] == i_localSlot) + { + if (* o_preservedSlotNumber == i_localSlot) + { + u8 type = GetStackTypeFromBottom (o, i); d_m3Assert (type != c_m3Type_none) + +_ (AllocateSlots (o, o_preservedSlotNumber, type)); + } + else +_ (IncrementSlotUsageCount (o, * o_preservedSlotNumber)); + + o->wasmStack [i] = * o_preservedSlotNumber; + } + } + + _catch: return result; +} + +static +M3Result GetBlockScope (IM3Compilation o, IM3CompilationScope * o_scope, u32 i_depth) +{ + M3Result result = m3Err_none; + + IM3CompilationScope scope = & o->block; + + while (i_depth--) + { + scope = scope->outer; + _throwif ("invalid block depth", not scope); + } + + * o_scope = scope; + + _catch: + return result; +} + +static +M3Result CopyStackSlotsR (IM3Compilation o, u16 i_targetSlotStackIndex, u16 i_stackIndex, u16 i_endStackIndex, u16 i_tempSlot) +{ + M3Result result = m3Err_none; + + if (i_stackIndex < i_endStackIndex) + { + u16 srcSlot = GetSlotForStackIndex (o, i_stackIndex); + + u8 type = GetStackTypeFromBottom (o, i_stackIndex); + u16 numSlots = GetTypeNumSlots (type); + u16 extraSlot = numSlots - 1; + + u16 targetSlot = GetSlotForStackIndex (o, i_targetSlotStackIndex); + + u16 preserveIndex = i_stackIndex; + u16 collisionSlot = srcSlot; + + if (targetSlot != srcSlot) + { + // search for collisions + u16 checkIndex = i_stackIndex + 1; + while (checkIndex < i_endStackIndex) + { + u16 otherSlot1 = GetSlotForStackIndex (o, checkIndex); + u16 otherSlot2 = GetExtraSlotForStackIndex (o, checkIndex); + + if (targetSlot == otherSlot1 or + targetSlot == otherSlot2 or + targetSlot + extraSlot == otherSlot1) + { + _throwif (m3Err_functionStackOverflow, i_tempSlot >= d_m3MaxFunctionSlots); + +_ (CopyStackIndexToSlot (o, i_tempSlot, checkIndex)); + o->wasmStack [checkIndex] = i_tempSlot; + i_tempSlot += GetTypeNumSlots (c_m3Type_i64); + TouchSlot (o, i_tempSlot - 1); + + // restore this on the way back down + preserveIndex = checkIndex; + collisionSlot = otherSlot1; + + break; + } + + ++checkIndex; + } + +_ (CopyStackIndexToSlot (o, targetSlot, i_stackIndex)); m3log (compile, " copying slot: %d to slot: %d", srcSlot, targetSlot); + o->wasmStack [i_stackIndex] = targetSlot; + + } + +_ (CopyStackSlotsR (o, i_targetSlotStackIndex + 1, i_stackIndex + 1, i_endStackIndex, i_tempSlot)); + + // restore the stack state + o->wasmStack [i_stackIndex] = srcSlot; + o->wasmStack [preserveIndex] = collisionSlot; + } + + _catch: + return result; +} + +static +M3Result ResolveBlockResults (IM3Compilation o, IM3CompilationScope i_targetBlock, bool i_isBranch) +{ + M3Result result = m3Err_none; if (d_m3LogWasmStack) dump_type_stack (o); + + bool isLoop = (i_targetBlock->opcode == c_waOp_loop and i_isBranch); + + u16 numParams = GetFuncTypeNumParams (i_targetBlock->type); + u16 numResults = GetFuncTypeNumResults (i_targetBlock->type); + + u16 slotRecords = i_targetBlock->exitStackIndex; + + u16 numValues; + + if (not isLoop) + { + numValues = numResults; + slotRecords += numParams; + } + else numValues = numParams; + + u16 blockHeight = GetNumBlockValuesOnStack (o); + + _throwif (m3Err_typeCountMismatch, i_isBranch ? (blockHeight < numValues) : (blockHeight != numValues)); + + if (numValues) + { + u16 endIndex = GetStackTopIndex (o) + 1; + + if (not isLoop and IsFpType (GetStackTopType (o))) + { +_ (CopyStackTopToRegister (o, false)); + --endIndex; + } + + // TODO: tempslot affects maxStackSlots, so can grow unnecess each time. + u16 tempSlot = o->maxStackSlots;// GetMaxUsedSlotPlusOne (o); doesn't work cause can collide with slotRecords + AlignSlotToType (& tempSlot, c_m3Type_i64); + +_ (CopyStackSlotsR (o, slotRecords, endIndex - numValues, endIndex, tempSlot)); + + if (d_m3LogWasmStack) dump_type_stack (o); + } + + _catch: return result; +} + + +static +M3Result ReturnValues (IM3Compilation o, IM3CompilationScope i_functionBlock, bool i_isBranch) +{ + M3Result result = m3Err_none; if (d_m3LogWasmStack) dump_type_stack (o); + + u16 numReturns = GetFuncTypeNumResults (i_functionBlock->type); // could just o->function too... + u16 blockHeight = GetNumBlockValuesOnStack (o); + + if (not IsStackPolymorphic (o)) + _throwif (m3Err_typeCountMismatch, i_isBranch ? (blockHeight < numReturns) : (blockHeight != numReturns)); + + if (numReturns) + { + // return slots like args are 64-bit aligned + u16 returnSlot = numReturns * c_ioSlotCount; + u16 stackTop = GetStackTopIndex (o); + + for (u16 i = 0; i < numReturns; ++i) + { + u8 returnType = GetFuncTypeResultType (i_functionBlock->type, numReturns - 1 - i); + + u8 stackType = GetStackTypeFromTop (o, i); // using FromTop so that only dynamic items are checked + + if (IsStackPolymorphic (o) and stackType == c_m3Type_none) + stackType = returnType; + + _throwif (m3Err_typeMismatch, returnType != stackType); + + if (not IsStackPolymorphic (o)) + { + returnSlot -= c_ioSlotCount; +_ (CopyStackIndexToSlot (o, returnSlot, stackTop--)); + } + } + + if (not i_isBranch) + { + while (numReturns--) +_ (Pop (o)); + } + } + + _catch: return result; +} + + +//------------------------------------------------------------------------------------------------------------------------- + +static +M3Result Compile_Const_i32 (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result; + + i32 value; +_ (ReadLEB_i32 (& value, & o->wasm, o->wasmEnd)); +_ (PushConst (o, value, c_m3Type_i32)); m3log (compile, d_indent " (const i32 = %" PRIi32 ")", get_indention_string (o), value); + _catch: return result; +} + +static +M3Result Compile_Const_i64 (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result; + + i64 value; +_ (ReadLEB_i64 (& value, & o->wasm, o->wasmEnd)); +_ (PushConst (o, value, c_m3Type_i64)); m3log (compile, d_indent " (const i64 = %" PRIi64 ")", get_indention_string (o), value); + _catch: return result; +} + + +#if d_m3ImplementFloat +static +M3Result Compile_Const_f32 (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result; + + union { u32 u; f32 f; } value = { 0 }; + +_ (Read_f32 (& value.f, & o->wasm, o->wasmEnd)); m3log (compile, d_indent " (const f32 = %" PRIf32 ")", get_indention_string (o), value.f); +_ (PushConst (o, value.u, c_m3Type_f32)); + + _catch: return result; +} + +static +M3Result Compile_Const_f64 (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result; + + union { u64 u; f64 f; } value = { 0 }; + +_ (Read_f64 (& value.f, & o->wasm, o->wasmEnd)); m3log (compile, d_indent " (const f64 = %" PRIf64 ")", get_indention_string (o), value.f); +_ (PushConst (o, value.u, c_m3Type_f64)); + + _catch: return result; +} +#endif + +#if d_m3CascadedOpcodes + +static +M3Result Compile_ExtendedOpcode (IM3Compilation o, m3opcode_t i_opcode) +{ +_try { + u8 opcode; +_ (Read_u8 (& opcode, & o->wasm, o->wasmEnd)); m3log (compile, d_indent " (FC: %" PRIi32 ")", get_indention_string (o), opcode); + + i_opcode = (i_opcode << 8) | opcode; + + //printf("Extended opcode: 0x%x\n", i_opcode); + + IM3OpInfo opInfo = GetOpInfo (i_opcode); + _throwif (m3Err_unknownOpcode, not opInfo); + + M3Compiler compiler = opInfo->compiler; + _throwif (m3Err_noCompiler, not compiler); + +_ ((* compiler) (o, i_opcode)); + + o->previousOpcode = i_opcode; + + } _catch: return result; +} +#endif + +static +M3Result Compile_Return (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result = m3Err_none; + + if (not IsStackPolymorphic (o)) + { + IM3CompilationScope functionScope; +_ (GetBlockScope (o, & functionScope, o->block.depth)); + +_ (ReturnValues (o, functionScope, true)); + +_ (EmitOp (o, op_Return)); + +_ (SetStackPolymorphic (o)); + } + + _catch: return result; +} + +static +M3Result ValidateBlockEnd (IM3Compilation o) +{ + M3Result result = m3Err_none; +/* + u16 numResults = GetFuncTypeNumResults (o->block.type); + u16 blockHeight = GetNumBlockValuesOnStack (o); + + if (IsStackPolymorphic (o)) + { + } + else + { + } + + _catch: */ return result; +} + +static +M3Result Compile_End (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result = m3Err_none; //dump_type_stack (o); + + // function end: + if (o->block.depth == 0) + { + ValidateBlockEnd (o); + +// if (not IsStackPolymorphic (o)) + { + if (o->function) + { +_ (ReturnValues (o, & o->block, false)); + } + +_ (EmitOp (o, op_Return)); + } + } + + _catch: return result; +} + + +static +M3Result Compile_SetLocal (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result; + + u32 localIndex; +_ (ReadLEB_u32 (& localIndex, & o->wasm, o->wasmEnd)); // printf ("--- set local: %d \n", localSlot); + + if (localIndex < GetFunctionNumArgsAndLocals (o->function)) + { + u16 localSlot = GetSlotForStackIndex (o, localIndex); + + u16 preserveSlot; +_ (FindReferencedLocalWithinCurrentBlock (o, & preserveSlot, localSlot)); // preserve will be different than local, if referenced + + if (preserveSlot == localSlot) +_ (CopyStackTopToSlot (o, localSlot)) + else +_ (PreservedCopyTopSlot (o, localSlot, preserveSlot)) + + if (i_opcode != c_waOp_teeLocal) +_ (Pop (o)); + } + else _throw ("local index out of bounds"); + + _catch: return result; +} + +static +M3Result Compile_GetLocal (IM3Compilation o, m3opcode_t i_opcode) +{ +_try { + + u32 localIndex; +_ (ReadLEB_u32 (& localIndex, & o->wasm, o->wasmEnd)); + + if (localIndex >= GetFunctionNumArgsAndLocals (o->function)) + _throw ("local index out of bounds"); + + u8 type = GetStackTypeFromBottom (o, localIndex); + u16 slot = GetSlotForStackIndex (o, localIndex); + +_ (Push (o, type, slot)); + + } _catch: return result; +} + +static +M3Result Compile_GetGlobal (IM3Compilation o, M3Global * i_global) +{ + M3Result result; + + IM3Operation op = Is64BitType (i_global->type) ? op_GetGlobal_s64 : op_GetGlobal_s32; +_ (EmitOp (o, op)); + EmitPointer (o, & i_global->i64Value); +_ (PushAllocatedSlotAndEmit (o, i_global->type)); + + _catch: return result; +} + +static +M3Result Compile_SetGlobal (IM3Compilation o, M3Global * i_global) +{ + M3Result result = m3Err_none; + + if (i_global->isMutable) + { + IM3Operation op; + u8 type = GetStackTopType (o); + + if (IsStackTopInRegister (o)) + { + op = c_setGlobalOps [type]; + } + else op = Is64BitType (type) ? op_SetGlobal_s64 : op_SetGlobal_s32; + +_ (EmitOp (o, op)); + EmitPointer (o, & i_global->i64Value); + + if (IsStackTopInSlot (o)) + EmitSlotOffset (o, GetStackTopSlotNumber (o)); + +_ (Pop (o)); + } + else _throw (m3Err_settingImmutableGlobal); + + _catch: return result; +} + +static +M3Result Compile_GetSetGlobal (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result = m3Err_none; + + u32 globalIndex; +_ (ReadLEB_u32 (& globalIndex, & o->wasm, o->wasmEnd)); + + if (globalIndex < o->module->numGlobals) + { + if (o->module->globals) + { + M3Global * global = & o->module->globals [globalIndex]; + +_ ((i_opcode == c_waOp_getGlobal) ? Compile_GetGlobal (o, global) : Compile_SetGlobal (o, global)); + } + else _throw (ErrorCompile (m3Err_globalMemoryNotAllocated, o, "module '%s' is missing global memory", o->module->name)); + } + else _throw (m3Err_globaIndexOutOfBounds); + + _catch: return result; +} + +static +void EmitPatchingBranchPointer (IM3Compilation o, IM3CompilationScope i_scope) +{ + pc_t patch = EmitPointer (o, i_scope->patches); m3log (compile, "branch patch required at: %p", patch); + i_scope->patches = patch; +} + +static +M3Result EmitPatchingBranch (IM3Compilation o, IM3CompilationScope i_scope) +{ + M3Result result = m3Err_none; + +_ (EmitOp (o, op_Branch)); + EmitPatchingBranchPointer (o, i_scope); + + _catch: return result; +} + +static +M3Result Compile_Branch (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result; + + u32 depth; +_ (ReadLEB_u32 (& depth, & o->wasm, o->wasmEnd)); + + IM3CompilationScope scope; +_ (GetBlockScope (o, & scope, depth)); + + // branch target is a loop (continue) + if (scope->opcode == c_waOp_loop) + { + if (i_opcode == c_waOp_branchIf) + { + if (GetFuncTypeNumParams (scope->type)) + { + IM3Operation op = IsStackTopInRegister (o) ? op_BranchIfPrologue_r : op_BranchIfPrologue_s; + +_ (EmitOp (o, op)); +_ (EmitSlotNumOfStackTopAndPop (o)); + + pc_t * jumpTo = (pc_t *) ReservePointer (o); + +_ (ResolveBlockResults (o, scope, /* isBranch: */ true)); + +_ (EmitOp (o, op_ContinueLoop)); + EmitPointer (o, scope->pc); + + * jumpTo = GetPC (o); + } + else + { + // move the condition to a register +_ (CopyStackTopToRegister (o, false)); +_ (PopType (o, c_m3Type_i32)); + +_ (EmitOp (o, op_ContinueLoopIf)); + EmitPointer (o, scope->pc); + } + +// dump_type_stack(o); + } + else // is c_waOp_branch + { + _ (EmitOp (o, op_ContinueLoop)); + EmitPointer (o, scope->pc); + o->block.isPolymorphic = true; + } + } + else // forward branch + { + pc_t * jumpTo = NULL; + + bool isReturn = (scope->depth == 0); + bool targetHasResults = GetFuncTypeNumResults (scope->type); + + if (i_opcode == c_waOp_branchIf) + { + if (targetHasResults or isReturn) + { + IM3Operation op = IsStackTopInRegister (o) ? op_BranchIfPrologue_r : op_BranchIfPrologue_s; + + _ (EmitOp (o, op)); + _ (EmitSlotNumOfStackTopAndPop (o)); // condition + + // this is continuation point, if the branch isn't taken + jumpTo = (pc_t *) ReservePointer (o); + } + else + { + IM3Operation op = IsStackTopInRegister (o) ? op_BranchIf_r : op_BranchIf_s; + + _ (EmitOp (o, op)); + _ (EmitSlotNumOfStackTopAndPop (o)); // condition + + EmitPatchingBranchPointer (o, scope); + goto _catch; + } + } + + if (not IsStackPolymorphic (o)) + { + if (isReturn) + { +_ (ReturnValues (o, scope, true)); +_ (EmitOp (o, op_Return)); + } + else + { +_ (ResolveBlockResults (o, scope, true)); +_ (EmitPatchingBranch (o, scope)); + } + } + + if (jumpTo) + { + * jumpTo = GetPC (o); + } + + if (i_opcode == c_waOp_branch) +_ (SetStackPolymorphic (o)); + } + + _catch: return result; +} + +static +M3Result Compile_BranchTable (IM3Compilation o, m3opcode_t i_opcode) +{ +_try { + u32 targetCount; +_ (ReadLEB_u32 (& targetCount, & o->wasm, o->wasmEnd)); + +_ (PreserveRegisterIfOccupied (o, c_m3Type_i64)); // move branch operand to a slot + u16 slot = GetStackTopSlotNumber (o); +_ (Pop (o)); + + // OPTZ: according to spec: "forward branches that target a control instruction with a non-empty + // result type consume matching operands first and push them back on the operand stack after unwinding" + // So, this move-to-reg is only necessary if the target scopes have a type. + + u32 numCodeLines = targetCount + 4; // 3 => IM3Operation + slot + target_count + default_target +_ (EnsureCodePageNumLines (o, numCodeLines)); + +_ (EmitOp (o, op_BranchTable)); + EmitSlotOffset (o, slot); + EmitConstant32 (o, targetCount); + + IM3CodePage continueOpPage = NULL; + + ++targetCount; // include default + for (u32 i = 0; i < targetCount; ++i) + { + u32 target; +_ (ReadLEB_u32 (& target, & o->wasm, o->wasmEnd)); + + IM3CompilationScope scope; +_ (GetBlockScope (o, & scope, target)); + + // TODO: don't need codepage rigmarole for + // no-param forward-branch targets + +_ (AcquireCompilationCodePage (o, & continueOpPage)); + + pc_t startPC = GetPagePC (continueOpPage); + IM3CodePage savedPage = o->page; + o->page = continueOpPage; + + if (scope->opcode == c_waOp_loop) + { +_ (ResolveBlockResults (o, scope, true)); + +_ (EmitOp (o, op_ContinueLoop)); + EmitPointer (o, scope->pc); + } + else + { + // TODO: this could be fused with equivalent targets + if (not IsStackPolymorphic (o)) + { + if (scope->depth == 0) + { +_ (ReturnValues (o, scope, true)); +_ (EmitOp (o, op_Return)); + } + else + { +_ (ResolveBlockResults (o, scope, true)); + +_ (EmitPatchingBranch (o, scope)); + } + } + } + + ReleaseCompilationCodePage (o); // FIX: continueOpPage can get lost if thrown + o->page = savedPage; + + EmitPointer (o, startPC); + } + +_ (SetStackPolymorphic (o)); + + } + + _catch: return result; +} + +static +M3Result CompileCallArgsAndReturn (IM3Compilation o, u16 * o_stackOffset, IM3FuncType i_type, bool i_isIndirect) +{ +_try { + + u16 topSlot = GetMaxUsedSlotPlusOne (o); + + // force use of at least one stack slot; this is to help ensure + // the m3 stack overflows (and traps) before the native stack can overflow. + // e.g. see Wasm spec test 'runaway' in call.wast + topSlot = M3_MAX (1, topSlot); + + // stack frame is 64-bit aligned + AlignSlotToType (& topSlot, c_m3Type_i64); + + * o_stackOffset = topSlot; + + // wait to pop this here so that topSlot search is correct + if (i_isIndirect) +_ (Pop (o)); + + u16 numArgs = GetFuncTypeNumParams (i_type); + u16 numRets = GetFuncTypeNumResults (i_type); + + u16 argTop = topSlot + (numArgs + numRets) * c_ioSlotCount; + + while (numArgs--) + { +_ (CopyStackTopToSlot (o, argTop -= c_ioSlotCount)); +_ (Pop (o)); + } + + u16 i = 0; + while (numRets--) + { + u8 type = GetFuncTypeResultType (i_type, i++); + +_ (Push (o, type, topSlot)); + MarkSlotsAllocatedByType (o, topSlot, type); + + topSlot += c_ioSlotCount; + } + + } _catch: return result; +} + +static +M3Result Compile_Call (IM3Compilation o, m3opcode_t i_opcode) +{ +_try { + u32 functionIndex; +_ (ReadLEB_u32 (& functionIndex, & o->wasm, o->wasmEnd)); + + IM3Function function = Module_GetFunction (o->module, functionIndex); + + if (function) + { m3log (compile, d_indent " (func= [%d] '%s'; args= %d)", + get_indention_string (o), functionIndex, m3_GetFunctionName (function), function->funcType->numArgs); + if (function->module) + { + u16 slotTop; +_ (CompileCallArgsAndReturn (o, & slotTop, function->funcType, false)); + + IM3Operation op; + const void * operand; + + if (function->compiled) + { + op = op_Call; + operand = function->compiled; + } + else + { + op = op_Compile; + operand = function; + } + +_ (EmitOp (o, op)); + EmitPointer (o, operand); + EmitSlotOffset (o, slotTop); + } + else + { + _throw (ErrorCompile (m3Err_functionImportMissing, o, "'%s.%s'", GetFunctionImportModuleName (function), m3_GetFunctionName (function))); + } + } + else _throw (m3Err_functionLookupFailed); + + } _catch: return result; +} + +static +M3Result Compile_CallIndirect (IM3Compilation o, m3opcode_t i_opcode) +{ +_try { + u32 typeIndex; +_ (ReadLEB_u32 (& typeIndex, & o->wasm, o->wasmEnd)); + + u32 tableIndex; +_ (ReadLEB_u32 (& tableIndex, & o->wasm, o->wasmEnd)); + + _throwif ("function call type index out of range", typeIndex >= o->module->numFuncTypes); + + if (IsStackTopInRegister (o)) +_ (PreserveRegisterIfOccupied (o, c_m3Type_i32)); + + u16 tableIndexSlot = GetStackTopSlotNumber (o); + + u16 execTop; + IM3FuncType type = o->module->funcTypes [typeIndex]; +_ (CompileCallArgsAndReturn (o, & execTop, type, true)); + +_ (EmitOp (o, op_CallIndirect)); + EmitSlotOffset (o, tableIndexSlot); + EmitPointer (o, o->module); + EmitPointer (o, type); // TODO: unify all types in M3Environment + EmitSlotOffset (o, execTop); + +} _catch: + return result; +} + +static +M3Result Compile_Memory_Size (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result; + + i8 reserved; +_ (ReadLEB_i7 (& reserved, & o->wasm, o->wasmEnd)); + +_ (PreserveRegisterIfOccupied (o, c_m3Type_i32)); + +_ (EmitOp (o, op_MemSize)); + +_ (PushRegister (o, c_m3Type_i32)); + + _catch: return result; +} + +static +M3Result Compile_Memory_Grow (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result; + + i8 reserved; +_ (ReadLEB_i7 (& reserved, & o->wasm, o->wasmEnd)); + +_ (CopyStackTopToRegister (o, false)); +_ (PopType (o, c_m3Type_i32)); + +_ (EmitOp (o, op_MemGrow)); + +_ (PushRegister (o, c_m3Type_i32)); + + _catch: return result; +} + +static +M3Result Compile_Memory_CopyFill (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result = m3Err_none; + + u32 sourceMemoryIdx, targetMemoryIdx; + IM3Operation op; + if (i_opcode == c_waOp_memoryCopy) + { +_ (ReadLEB_u32 (& sourceMemoryIdx, & o->wasm, o->wasmEnd)); + op = op_MemCopy; + } + else op = op_MemFill; + +_ (ReadLEB_u32 (& targetMemoryIdx, & o->wasm, o->wasmEnd)); + +_ (CopyStackTopToRegister (o, false)); + +_ (EmitOp (o, op)); +_ (PopType (o, c_m3Type_i32)); +_ (EmitSlotNumOfStackTopAndPop (o)); +_ (EmitSlotNumOfStackTopAndPop (o)); + + _catch: return result; +} + + +static +M3Result ReadBlockType (IM3Compilation o, IM3FuncType * o_blockType) +{ + M3Result result; + + i64 type; +_ (ReadLebSigned (& type, 33, & o->wasm, o->wasmEnd)); + + if (type < 0) + { + u8 valueType; +_ (NormalizeType (&valueType, type)); m3log (compile, d_indent " (type: %s)", get_indention_string (o), c_waTypes [valueType]); + *o_blockType = o->module->environment->retFuncTypes[valueType]; + } + else + { + _throwif("func type out of bounds", type >= o->module->numFuncTypes); + *o_blockType = o->module->funcTypes[type]; m3log (compile, d_indent " (type: %s)", get_indention_string (o), SPrintFuncTypeSignature (*o_blockType)); + } + _catch: return result; +} + +static +M3Result PreserveArgsAndLocals (IM3Compilation o) +{ + M3Result result = m3Err_none; + + if (o->stackIndex > o->stackFirstDynamicIndex) + { + u32 numArgsAndLocals = GetFunctionNumArgsAndLocals (o->function); + + for (u32 i = 0; i < numArgsAndLocals; ++i) + { + u16 slot = GetSlotForStackIndex (o, i); + + u16 preservedSlotNumber; +_ (FindReferencedLocalWithinCurrentBlock (o, & preservedSlotNumber, slot)); + + if (preservedSlotNumber != slot) + { + u8 type = GetStackTypeFromBottom (o, i); d_m3Assert (type != c_m3Type_none) + IM3Operation op = Is64BitType (type) ? op_CopySlot_64 : op_CopySlot_32; + + EmitOp (o, op); + EmitSlotOffset (o, preservedSlotNumber); + EmitSlotOffset (o, slot); + } + } + } + + _catch: + return result; +} + +static +M3Result Compile_LoopOrBlock (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result; + + // TODO: these shouldn't be necessary for non-loop blocks? +_ (PreserveRegisters (o)); +_ (PreserveArgsAndLocals (o)); + + IM3FuncType blockType; +_ (ReadBlockType (o, & blockType)); + + if (i_opcode == c_waOp_loop) + { + u16 numParams = GetFuncTypeNumParams (blockType); + if (numParams) + { + // instantiate constants + u16 numValues = GetNumBlockValuesOnStack (o); // CompileBlock enforces this at comptime + d_m3Assert (numValues >= numParams); + if (numValues >= numParams) + { + u16 stackTop = GetStackTopIndex (o) + 1; + + for (u16 i = stackTop - numParams; i < stackTop; ++i) + { + u16 slot = GetSlotForStackIndex (o, i); + u8 type = GetStackTypeFromBottom (o, i); + + if (IsConstantSlot (o, slot)) + { + u16 newSlot = c_slotUnused; +_ (AllocateSlots (o, & newSlot, type)); +_ (CopyStackIndexToSlot (o, newSlot, i)); + o->wasmStack [i] = newSlot; + } + } + } + } + +_ (EmitOp (o, op_Loop)); + } + else + { + } + +_ (CompileBlock (o, blockType, i_opcode)); + + _catch: return result; +} + +static +M3Result CompileElseBlock (IM3Compilation o, pc_t * o_startPC, IM3FuncType i_blockType) +{ + IM3CodePage savedPage = o->page; +_try { + + IM3CodePage elsePage; +_ (AcquireCompilationCodePage (o, & elsePage)); + + * o_startPC = GetPagePC (elsePage); + + o->page = elsePage; + +_ (CompileBlock (o, i_blockType, c_waOp_else)); + +_ (EmitOp (o, op_Branch)); + EmitPointer (o, GetPagePC (savedPage)); +} _catch: + if(o->page != savedPage) { + ReleaseCompilationCodePage (o); + } + o->page = savedPage; + return result; +} + +static +M3Result Compile_If (IM3Compilation o, m3opcode_t i_opcode) +{ + /* [ op_If ] + [ ] ----> [ ..else.. ] + [ ..if.. ] [ ..block.. ] + [ ..block.. ] [ op_Branch ] + [ end ] <----- [ ] */ + +_try { + +_ (PreserveNonTopRegisters (o)); +_ (PreserveArgsAndLocals (o)); + + IM3Operation op = IsStackTopInRegister (o) ? op_If_r : op_If_s; + +_ (EmitOp (o, op)); +_ (EmitSlotNumOfStackTopAndPop (o)); + + pc_t * pc = (pc_t *) ReservePointer (o); + + IM3FuncType blockType; +_ (ReadBlockType (o, & blockType)); + +// dump_type_stack (o); + + u16 stackIndex = o->stackIndex; + +_ (CompileBlock (o, blockType, i_opcode)); + + if (o->previousOpcode == c_waOp_else) + { + o->stackIndex = stackIndex; +_ (CompileElseBlock (o, pc, blockType)); + } + else + { + // if block produces values and there isn't a defined else + // case, then we need to make one up so that the pass-through + // results end up in the right place + if (GetFuncTypeNumResults (blockType)) + { + // rewind to the if's end to create a fake else block + o->wasm--; + o->stackIndex = stackIndex; + +// dump_type_stack (o); + +_ (CompileElseBlock (o, pc, blockType)); + } + else * pc = GetPC (o); + } + + } _catch: return result; +} + +static +M3Result Compile_Select (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result = m3Err_none; + + u16 slots [3] = { c_slotUnused, c_slotUnused, c_slotUnused }; + + u8 type = GetStackTypeFromTop (o, 1); // get type of selection + + IM3Operation op = NULL; + + if (IsFpType (type)) + { +# if d_m3HasFloat + // not consuming a fp reg, so preserve + if (not IsStackTopMinus1InRegister (o) and + not IsStackTopMinus2InRegister (o)) + { +_ (PreserveRegisterIfOccupied (o, type)); + } + + bool selectorInReg = IsStackTopInRegister (o); + slots [0] = GetStackTopSlotNumber (o); +_ (Pop (o)); + + u32 opIndex = 0; + + for (u32 i = 1; i <= 2; ++i) + { + if (IsStackTopInRegister (o)) + opIndex = i; + else + slots [i] = GetStackTopSlotNumber (o); + +_ (Pop (o)); + } + + op = c_fpSelectOps [type - c_m3Type_f32] [selectorInReg] [opIndex]; +# else + _throw (m3Err_unknownOpcode); +# endif + } + else if (IsIntType (type)) + { + // 'sss' operation doesn't consume a register, so might have to protected its contents + if (not IsStackTopInRegister (o) and + not IsStackTopMinus1InRegister (o) and + not IsStackTopMinus2InRegister (o)) + { +_ (PreserveRegisterIfOccupied (o, type)); + } + + u32 opIndex = 3; // op_Select_*_sss + + for (u32 i = 0; i < 3; ++i) + { + if (IsStackTopInRegister (o)) + opIndex = i; + else + slots [i] = GetStackTopSlotNumber (o); + +_ (Pop (o)); + } + + op = c_intSelectOps [type - c_m3Type_i32] [opIndex]; + } + else if (not IsStackPolymorphic (o)) + _throw (m3Err_functionStackUnderrun); + + EmitOp (o, op); + for (u32 i = 0; i < 3; i++) + { + if (IsValidSlot (slots [i])) + EmitSlotOffset (o, slots [i]); + } +_ (PushRegister (o, type)); + + _catch: return result; +} + +static +M3Result Compile_Drop (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result = Pop (o); if (d_m3LogWasmStack) dump_type_stack (o); + return result; +} + +static +M3Result Compile_Nop (IM3Compilation o, m3opcode_t i_opcode) +{ + return m3Err_none; +} + +static +M3Result Compile_Unreachable (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result; + +_ (AddTrapRecord (o)); + +_ (EmitOp (o, op_Unreachable)); +_ (SetStackPolymorphic (o)); + + _catch: + return result; +} + + +// OPTZ: currently all stack slot indices take up a full word, but +// dual stack source operands could be packed together +static +M3Result Compile_Operator (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result; + + IM3OpInfo opInfo = GetOpInfo (i_opcode); + _throwif (m3Err_unknownOpcode, not opInfo); + + IM3Operation op; + + // This preserve is for for FP compare operations. + // either need additional slot destination operations or the + // easy fix, move _r0 out of the way. + // moving out the way might be the optimal solution most often? + // otherwise, the _r0 reg can get buried down in the stack + // and be idle & wasted for a moment. + if (IsFpType (GetStackTopType (o)) and IsIntType (opInfo->type)) + { +_ (PreserveRegisterIfOccupied (o, opInfo->type)); + } + + if (opInfo->stackOffset == 0) + { + if (IsStackTopInRegister (o)) + { + op = opInfo->operations [0]; // _s + } + else + { +_ (PreserveRegisterIfOccupied (o, opInfo->type)); + op = opInfo->operations [1]; // _r + } + } + else + { + if (IsStackTopInRegister (o)) + { + op = opInfo->operations [0]; // _rs + + if (IsStackTopMinus1InRegister (o)) + { d_m3Assert (i_opcode == c_waOp_store_f32 or i_opcode == c_waOp_store_f64); + op = opInfo->operations [3]; // _rr for fp.store + } + } + else if (IsStackTopMinus1InRegister (o)) + { + op = opInfo->operations [1]; // _sr + + if (not op) // must be commutative, then + op = opInfo->operations [0]; + } + else + { +_ (PreserveRegisterIfOccupied (o, opInfo->type)); // _ss + op = opInfo->operations [2]; + } + } + + if (op) + { +_ (EmitOp (o, op)); + +_ (EmitSlotNumOfStackTopAndPop (o)); + + if (opInfo->stackOffset < 0) +_ (EmitSlotNumOfStackTopAndPop (o)); + + if (opInfo->type != c_m3Type_none) +_ (PushRegister (o, opInfo->type)); + } + else + { +# ifdef DEBUG + result = ErrorCompile ("no operation found for opcode", o, "'%s'", opInfo->name); +# else + result = ErrorCompile ("no operation found for opcode", o, "%x", i_opcode); +# endif + _throw (result); + } + + _catch: return result; +} + +static +M3Result Compile_Convert (IM3Compilation o, m3opcode_t i_opcode) +{ +_try { + IM3OpInfo opInfo = GetOpInfo (i_opcode); + _throwif (m3Err_unknownOpcode, not opInfo); + + bool destInSlot = IsRegisterTypeAllocated (o, opInfo->type); + bool sourceInSlot = IsStackTopInSlot (o); + + IM3Operation op = opInfo->operations [destInSlot * 2 + sourceInSlot]; + +_ (EmitOp (o, op)); +_ (EmitSlotNumOfStackTopAndPop (o)); + + if (destInSlot) +_ (PushAllocatedSlotAndEmit (o, opInfo->type)) + else +_ (PushRegister (o, opInfo->type)) + +} + _catch: return result; +} + +static +M3Result Compile_Load_Store (IM3Compilation o, m3opcode_t i_opcode) +{ +_try { + u32 alignHint, memoryOffset; + +_ (ReadLEB_u32 (& alignHint, & o->wasm, o->wasmEnd)); +_ (ReadLEB_u32 (& memoryOffset, & o->wasm, o->wasmEnd)); + m3log (compile, d_indent " (offset = %d)", get_indention_string (o), memoryOffset); + IM3OpInfo opInfo = GetOpInfo (i_opcode); + _throwif (m3Err_unknownOpcode, not opInfo); + + if (IsFpType (opInfo->type)) +_ (PreserveRegisterIfOccupied (o, c_m3Type_f64)); + +_ (Compile_Operator (o, i_opcode)); + + EmitConstant32 (o, memoryOffset); +} + _catch: return result; +} + + +M3Result CompileRawFunction (IM3Module io_module, IM3Function io_function, const void * i_function, const void * i_userdata) +{ + d_m3Assert (io_module->runtime); + + IM3CodePage page = AcquireCodePageWithCapacity (io_module->runtime, 4); + + if (page) + { + io_function->compiled = GetPagePC (page); + io_function->module = io_module; + + EmitWord (page, op_CallRawFunction); + EmitWord (page, i_function); + EmitWord (page, io_function); + EmitWord (page, i_userdata); + + ReleaseCodePage (io_module->runtime, page); + return m3Err_none; + } + else { + return m3Err_mallocFailedCodePage; + } +} + + + +// d_logOp, d_logOp2 macros aren't actually used by the compiler, just codepage decoding (d_m3LogCodePages = 1) +#define d_logOp(OP) { op_##OP, NULL, NULL, NULL } +#define d_logOp2(OP1,OP2) { op_##OP1, op_##OP2, NULL, NULL } + +#define d_emptyOpList { NULL, NULL, NULL, NULL } +#define d_unaryOpList(TYPE, NAME) { op_##TYPE##_##NAME##_r, op_##TYPE##_##NAME##_s, NULL, NULL } +#define d_binOpList(TYPE, NAME) { op_##TYPE##_##NAME##_rs, op_##TYPE##_##NAME##_sr, op_##TYPE##_##NAME##_ss, NULL } +#define d_storeFpOpList(TYPE, NAME) { op_##TYPE##_##NAME##_rs, op_##TYPE##_##NAME##_sr, op_##TYPE##_##NAME##_ss, op_##TYPE##_##NAME##_rr } +#define d_commutativeBinOpList(TYPE, NAME) { op_##TYPE##_##NAME##_rs, NULL, op_##TYPE##_##NAME##_ss, NULL } +#define d_convertOpList(OP) { op_##OP##_r_r, op_##OP##_r_s, op_##OP##_s_r, op_##OP##_s_s } + + +const M3OpInfo c_operations [] = +{ + M3OP( "unreachable", 0, none, d_logOp (Unreachable), Compile_Unreachable ), // 0x00 + M3OP( "nop", 0, none, d_emptyOpList, Compile_Nop ), // 0x01 . + M3OP( "block", 0, none, d_emptyOpList, Compile_LoopOrBlock ), // 0x02 + M3OP( "loop", 0, none, d_logOp (Loop), Compile_LoopOrBlock ), // 0x03 + M3OP( "if", -1, none, d_emptyOpList, Compile_If ), // 0x04 + M3OP( "else", 0, none, d_emptyOpList, Compile_Nop ), // 0x05 + + M3OP_RESERVED, M3OP_RESERVED, M3OP_RESERVED, M3OP_RESERVED, M3OP_RESERVED, // 0x06...0x0a + + M3OP( "end", 0, none, d_emptyOpList, Compile_End ), // 0x0b + M3OP( "br", 0, none, d_logOp (Branch), Compile_Branch ), // 0x0c + M3OP( "br_if", -1, none, d_logOp2 (BranchIf_r, BranchIf_s), Compile_Branch ), // 0x0d + M3OP( "br_table", -1, none, d_logOp (BranchTable), Compile_BranchTable ), // 0x0e + M3OP( "return", 0, any, d_logOp (Return), Compile_Return ), // 0x0f + M3OP( "call", 0, any, d_logOp (Call), Compile_Call ), // 0x10 + M3OP( "call_indirect", 0, any, d_logOp (CallIndirect), Compile_CallIndirect ), // 0x11 + M3OP( "return_call", 0, any, d_emptyOpList, Compile_Call ), // 0x12 TODO: Optimize + M3OP( "return_call_indirect",0, any, d_emptyOpList, Compile_CallIndirect ), // 0x13 + + M3OP_RESERVED, M3OP_RESERVED, // 0x14... + M3OP_RESERVED, M3OP_RESERVED, M3OP_RESERVED, M3OP_RESERVED, // ...0x19 + + M3OP( "drop", -1, none, d_emptyOpList, Compile_Drop ), // 0x1a + M3OP( "select", -2, any, d_emptyOpList, Compile_Select ), // 0x1b + + M3OP_RESERVED, M3OP_RESERVED, M3OP_RESERVED, M3OP_RESERVED, // 0x1c...0x1f + + M3OP( "local.get", 1, any, d_emptyOpList, Compile_GetLocal ), // 0x20 + M3OP( "local.set", 1, none, d_emptyOpList, Compile_SetLocal ), // 0x21 + M3OP( "local.tee", 0, any, d_emptyOpList, Compile_SetLocal ), // 0x22 + M3OP( "global.get", 1, none, d_emptyOpList, Compile_GetSetGlobal ), // 0x23 + M3OP( "global.set", 1, none, d_emptyOpList, Compile_GetSetGlobal ), // 0x24 + + M3OP_RESERVED, M3OP_RESERVED, M3OP_RESERVED, // 0x25...0x27 + + M3OP( "i32.load", 0, i_32, d_unaryOpList (i32, Load_i32), Compile_Load_Store ), // 0x28 + M3OP( "i64.load", 0, i_64, d_unaryOpList (i64, Load_i64), Compile_Load_Store ), // 0x29 + M3OP_F( "f32.load", 0, f_32, d_unaryOpList (f32, Load_f32), Compile_Load_Store ), // 0x2a + M3OP_F( "f64.load", 0, f_64, d_unaryOpList (f64, Load_f64), Compile_Load_Store ), // 0x2b + + M3OP( "i32.load8_s", 0, i_32, d_unaryOpList (i32, Load_i8), Compile_Load_Store ), // 0x2c + M3OP( "i32.load8_u", 0, i_32, d_unaryOpList (i32, Load_u8), Compile_Load_Store ), // 0x2d + M3OP( "i32.load16_s", 0, i_32, d_unaryOpList (i32, Load_i16), Compile_Load_Store ), // 0x2e + M3OP( "i32.load16_u", 0, i_32, d_unaryOpList (i32, Load_u16), Compile_Load_Store ), // 0x2f + + M3OP( "i64.load8_s", 0, i_64, d_unaryOpList (i64, Load_i8), Compile_Load_Store ), // 0x30 + M3OP( "i64.load8_u", 0, i_64, d_unaryOpList (i64, Load_u8), Compile_Load_Store ), // 0x31 + M3OP( "i64.load16_s", 0, i_64, d_unaryOpList (i64, Load_i16), Compile_Load_Store ), // 0x32 + M3OP( "i64.load16_u", 0, i_64, d_unaryOpList (i64, Load_u16), Compile_Load_Store ), // 0x33 + M3OP( "i64.load32_s", 0, i_64, d_unaryOpList (i64, Load_i32), Compile_Load_Store ), // 0x34 + M3OP( "i64.load32_u", 0, i_64, d_unaryOpList (i64, Load_u32), Compile_Load_Store ), // 0x35 + + M3OP( "i32.store", -2, none, d_binOpList (i32, Store_i32), Compile_Load_Store ), // 0x36 + M3OP( "i64.store", -2, none, d_binOpList (i64, Store_i64), Compile_Load_Store ), // 0x37 + M3OP_F( "f32.store", -2, none, d_storeFpOpList (f32, Store_f32), Compile_Load_Store ), // 0x38 + M3OP_F( "f64.store", -2, none, d_storeFpOpList (f64, Store_f64), Compile_Load_Store ), // 0x39 + + M3OP( "i32.store8", -2, none, d_binOpList (i32, Store_u8), Compile_Load_Store ), // 0x3a + M3OP( "i32.store16", -2, none, d_binOpList (i32, Store_i16), Compile_Load_Store ), // 0x3b + + M3OP( "i64.store8", -2, none, d_binOpList (i64, Store_u8), Compile_Load_Store ), // 0x3c + M3OP( "i64.store16", -2, none, d_binOpList (i64, Store_i16), Compile_Load_Store ), // 0x3d + M3OP( "i64.store32", -2, none, d_binOpList (i64, Store_i32), Compile_Load_Store ), // 0x3e + + M3OP( "memory.size", 1, i_32, d_logOp (MemSize), Compile_Memory_Size ), // 0x3f + M3OP( "memory.grow", 1, i_32, d_logOp (MemGrow), Compile_Memory_Grow ), // 0x40 + + M3OP( "i32.const", 1, i_32, d_logOp (Const32), Compile_Const_i32 ), // 0x41 + M3OP( "i64.const", 1, i_64, d_logOp (Const64), Compile_Const_i64 ), // 0x42 + M3OP_F( "f32.const", 1, f_32, d_emptyOpList, Compile_Const_f32 ), // 0x43 + M3OP_F( "f64.const", 1, f_64, d_emptyOpList, Compile_Const_f64 ), // 0x44 + + M3OP( "i32.eqz", 0, i_32, d_unaryOpList (i32, EqualToZero) , NULL ), // 0x45 + M3OP( "i32.eq", -1, i_32, d_commutativeBinOpList (i32, Equal) , NULL ), // 0x46 + M3OP( "i32.ne", -1, i_32, d_commutativeBinOpList (i32, NotEqual) , NULL ), // 0x47 + M3OP( "i32.lt_s", -1, i_32, d_binOpList (i32, LessThan) , NULL ), // 0x48 + M3OP( "i32.lt_u", -1, i_32, d_binOpList (u32, LessThan) , NULL ), // 0x49 + M3OP( "i32.gt_s", -1, i_32, d_binOpList (i32, GreaterThan) , NULL ), // 0x4a + M3OP( "i32.gt_u", -1, i_32, d_binOpList (u32, GreaterThan) , NULL ), // 0x4b + M3OP( "i32.le_s", -1, i_32, d_binOpList (i32, LessThanOrEqual) , NULL ), // 0x4c + M3OP( "i32.le_u", -1, i_32, d_binOpList (u32, LessThanOrEqual) , NULL ), // 0x4d + M3OP( "i32.ge_s", -1, i_32, d_binOpList (i32, GreaterThanOrEqual) , NULL ), // 0x4e + M3OP( "i32.ge_u", -1, i_32, d_binOpList (u32, GreaterThanOrEqual) , NULL ), // 0x4f + + M3OP( "i64.eqz", 0, i_32, d_unaryOpList (i64, EqualToZero) , NULL ), // 0x50 + M3OP( "i64.eq", -1, i_32, d_commutativeBinOpList (i64, Equal) , NULL ), // 0x51 + M3OP( "i64.ne", -1, i_32, d_commutativeBinOpList (i64, NotEqual) , NULL ), // 0x52 + M3OP( "i64.lt_s", -1, i_32, d_binOpList (i64, LessThan) , NULL ), // 0x53 + M3OP( "i64.lt_u", -1, i_32, d_binOpList (u64, LessThan) , NULL ), // 0x54 + M3OP( "i64.gt_s", -1, i_32, d_binOpList (i64, GreaterThan) , NULL ), // 0x55 + M3OP( "i64.gt_u", -1, i_32, d_binOpList (u64, GreaterThan) , NULL ), // 0x56 + M3OP( "i64.le_s", -1, i_32, d_binOpList (i64, LessThanOrEqual) , NULL ), // 0x57 + M3OP( "i64.le_u", -1, i_32, d_binOpList (u64, LessThanOrEqual) , NULL ), // 0x58 + M3OP( "i64.ge_s", -1, i_32, d_binOpList (i64, GreaterThanOrEqual) , NULL ), // 0x59 + M3OP( "i64.ge_u", -1, i_32, d_binOpList (u64, GreaterThanOrEqual) , NULL ), // 0x5a + + M3OP_F( "f32.eq", -1, i_32, d_commutativeBinOpList (f32, Equal) , NULL ), // 0x5b + M3OP_F( "f32.ne", -1, i_32, d_commutativeBinOpList (f32, NotEqual) , NULL ), // 0x5c + M3OP_F( "f32.lt", -1, i_32, d_binOpList (f32, LessThan) , NULL ), // 0x5d + M3OP_F( "f32.gt", -1, i_32, d_binOpList (f32, GreaterThan) , NULL ), // 0x5e + M3OP_F( "f32.le", -1, i_32, d_binOpList (f32, LessThanOrEqual) , NULL ), // 0x5f + M3OP_F( "f32.ge", -1, i_32, d_binOpList (f32, GreaterThanOrEqual) , NULL ), // 0x60 + + M3OP_F( "f64.eq", -1, i_32, d_commutativeBinOpList (f64, Equal) , NULL ), // 0x61 + M3OP_F( "f64.ne", -1, i_32, d_commutativeBinOpList (f64, NotEqual) , NULL ), // 0x62 + M3OP_F( "f64.lt", -1, i_32, d_binOpList (f64, LessThan) , NULL ), // 0x63 + M3OP_F( "f64.gt", -1, i_32, d_binOpList (f64, GreaterThan) , NULL ), // 0x64 + M3OP_F( "f64.le", -1, i_32, d_binOpList (f64, LessThanOrEqual) , NULL ), // 0x65 + M3OP_F( "f64.ge", -1, i_32, d_binOpList (f64, GreaterThanOrEqual) , NULL ), // 0x66 + + M3OP( "i32.clz", 0, i_32, d_unaryOpList (u32, Clz) , NULL ), // 0x67 + M3OP( "i32.ctz", 0, i_32, d_unaryOpList (u32, Ctz) , NULL ), // 0x68 + M3OP( "i32.popcnt", 0, i_32, d_unaryOpList (u32, Popcnt) , NULL ), // 0x69 + + M3OP( "i32.add", -1, i_32, d_commutativeBinOpList (i32, Add) , NULL ), // 0x6a + M3OP( "i32.sub", -1, i_32, d_binOpList (i32, Subtract) , NULL ), // 0x6b + M3OP( "i32.mul", -1, i_32, d_commutativeBinOpList (i32, Multiply) , NULL ), // 0x6c + M3OP( "i32.div_s", -1, i_32, d_binOpList (i32, Divide) , NULL ), // 0x6d + M3OP( "i32.div_u", -1, i_32, d_binOpList (u32, Divide) , NULL ), // 0x6e + M3OP( "i32.rem_s", -1, i_32, d_binOpList (i32, Remainder) , NULL ), // 0x6f + M3OP( "i32.rem_u", -1, i_32, d_binOpList (u32, Remainder) , NULL ), // 0x70 + M3OP( "i32.and", -1, i_32, d_commutativeBinOpList (u32, And) , NULL ), // 0x71 + M3OP( "i32.or", -1, i_32, d_commutativeBinOpList (u32, Or) , NULL ), // 0x72 + M3OP( "i32.xor", -1, i_32, d_commutativeBinOpList (u32, Xor) , NULL ), // 0x73 + M3OP( "i32.shl", -1, i_32, d_binOpList (u32, ShiftLeft) , NULL ), // 0x74 + M3OP( "i32.shr_s", -1, i_32, d_binOpList (i32, ShiftRight) , NULL ), // 0x75 + M3OP( "i32.shr_u", -1, i_32, d_binOpList (u32, ShiftRight) , NULL ), // 0x76 + M3OP( "i32.rotl", -1, i_32, d_binOpList (u32, Rotl) , NULL ), // 0x77 + M3OP( "i32.rotr", -1, i_32, d_binOpList (u32, Rotr) , NULL ), // 0x78 + + M3OP( "i64.clz", 0, i_64, d_unaryOpList (u64, Clz) , NULL ), // 0x79 + M3OP( "i64.ctz", 0, i_64, d_unaryOpList (u64, Ctz) , NULL ), // 0x7a + M3OP( "i64.popcnt", 0, i_64, d_unaryOpList (u64, Popcnt) , NULL ), // 0x7b + + M3OP( "i64.add", -1, i_64, d_commutativeBinOpList (i64, Add) , NULL ), // 0x7c + M3OP( "i64.sub", -1, i_64, d_binOpList (i64, Subtract) , NULL ), // 0x7d + M3OP( "i64.mul", -1, i_64, d_commutativeBinOpList (i64, Multiply) , NULL ), // 0x7e + M3OP( "i64.div_s", -1, i_64, d_binOpList (i64, Divide) , NULL ), // 0x7f + M3OP( "i64.div_u", -1, i_64, d_binOpList (u64, Divide) , NULL ), // 0x80 + M3OP( "i64.rem_s", -1, i_64, d_binOpList (i64, Remainder) , NULL ), // 0x81 + M3OP( "i64.rem_u", -1, i_64, d_binOpList (u64, Remainder) , NULL ), // 0x82 + M3OP( "i64.and", -1, i_64, d_commutativeBinOpList (u64, And) , NULL ), // 0x83 + M3OP( "i64.or", -1, i_64, d_commutativeBinOpList (u64, Or) , NULL ), // 0x84 + M3OP( "i64.xor", -1, i_64, d_commutativeBinOpList (u64, Xor) , NULL ), // 0x85 + M3OP( "i64.shl", -1, i_64, d_binOpList (u64, ShiftLeft) , NULL ), // 0x86 + M3OP( "i64.shr_s", -1, i_64, d_binOpList (i64, ShiftRight) , NULL ), // 0x87 + M3OP( "i64.shr_u", -1, i_64, d_binOpList (u64, ShiftRight) , NULL ), // 0x88 + M3OP( "i64.rotl", -1, i_64, d_binOpList (u64, Rotl) , NULL ), // 0x89 + M3OP( "i64.rotr", -1, i_64, d_binOpList (u64, Rotr) , NULL ), // 0x8a + + M3OP_F( "f32.abs", 0, f_32, d_unaryOpList(f32, Abs) , NULL ), // 0x8b + M3OP_F( "f32.neg", 0, f_32, d_unaryOpList(f32, Negate) , NULL ), // 0x8c + M3OP_F( "f32.ceil", 0, f_32, d_unaryOpList(f32, Ceil) , NULL ), // 0x8d + M3OP_F( "f32.floor", 0, f_32, d_unaryOpList(f32, Floor) , NULL ), // 0x8e + M3OP_F( "f32.trunc", 0, f_32, d_unaryOpList(f32, Trunc) , NULL ), // 0x8f + M3OP_F( "f32.nearest", 0, f_32, d_unaryOpList(f32, Nearest) , NULL ), // 0x90 + M3OP_F( "f32.sqrt", 0, f_32, d_unaryOpList(f32, Sqrt) , NULL ), // 0x91 + + M3OP_F( "f32.add", -1, f_32, d_commutativeBinOpList (f32, Add) , NULL ), // 0x92 + M3OP_F( "f32.sub", -1, f_32, d_binOpList (f32, Subtract) , NULL ), // 0x93 + M3OP_F( "f32.mul", -1, f_32, d_commutativeBinOpList (f32, Multiply) , NULL ), // 0x94 + M3OP_F( "f32.div", -1, f_32, d_binOpList (f32, Divide) , NULL ), // 0x95 + M3OP_F( "f32.min", -1, f_32, d_commutativeBinOpList (f32, Min) , NULL ), // 0x96 + M3OP_F( "f32.max", -1, f_32, d_commutativeBinOpList (f32, Max) , NULL ), // 0x97 + M3OP_F( "f32.copysign", -1, f_32, d_binOpList (f32, CopySign) , NULL ), // 0x98 + + M3OP_F( "f64.abs", 0, f_64, d_unaryOpList(f64, Abs) , NULL ), // 0x99 + M3OP_F( "f64.neg", 0, f_64, d_unaryOpList(f64, Negate) , NULL ), // 0x9a + M3OP_F( "f64.ceil", 0, f_64, d_unaryOpList(f64, Ceil) , NULL ), // 0x9b + M3OP_F( "f64.floor", 0, f_64, d_unaryOpList(f64, Floor) , NULL ), // 0x9c + M3OP_F( "f64.trunc", 0, f_64, d_unaryOpList(f64, Trunc) , NULL ), // 0x9d + M3OP_F( "f64.nearest", 0, f_64, d_unaryOpList(f64, Nearest) , NULL ), // 0x9e + M3OP_F( "f64.sqrt", 0, f_64, d_unaryOpList(f64, Sqrt) , NULL ), // 0x9f + + M3OP_F( "f64.add", -1, f_64, d_commutativeBinOpList (f64, Add) , NULL ), // 0xa0 + M3OP_F( "f64.sub", -1, f_64, d_binOpList (f64, Subtract) , NULL ), // 0xa1 + M3OP_F( "f64.mul", -1, f_64, d_commutativeBinOpList (f64, Multiply) , NULL ), // 0xa2 + M3OP_F( "f64.div", -1, f_64, d_binOpList (f64, Divide) , NULL ), // 0xa3 + M3OP_F( "f64.min", -1, f_64, d_commutativeBinOpList (f64, Min) , NULL ), // 0xa4 + M3OP_F( "f64.max", -1, f_64, d_commutativeBinOpList (f64, Max) , NULL ), // 0xa5 + M3OP_F( "f64.copysign", -1, f_64, d_binOpList (f64, CopySign) , NULL ), // 0xa6 + + M3OP( "i32.wrap/i64", 0, i_32, d_unaryOpList (i32, Wrap_i64), NULL ), // 0xa7 + M3OP_F( "i32.trunc_s/f32", 0, i_32, d_convertOpList (i32_Trunc_f32), Compile_Convert ), // 0xa8 + M3OP_F( "i32.trunc_u/f32", 0, i_32, d_convertOpList (u32_Trunc_f32), Compile_Convert ), // 0xa9 + M3OP_F( "i32.trunc_s/f64", 0, i_32, d_convertOpList (i32_Trunc_f64), Compile_Convert ), // 0xaa + M3OP_F( "i32.trunc_u/f64", 0, i_32, d_convertOpList (u32_Trunc_f64), Compile_Convert ), // 0xab + + M3OP( "i64.extend_s/i32", 0, i_64, d_unaryOpList (i64, Extend_i32), NULL ), // 0xac + M3OP( "i64.extend_u/i32", 0, i_64, d_unaryOpList (i64, Extend_u32), NULL ), // 0xad + + M3OP_F( "i64.trunc_s/f32", 0, i_64, d_convertOpList (i64_Trunc_f32), Compile_Convert ), // 0xae + M3OP_F( "i64.trunc_u/f32", 0, i_64, d_convertOpList (u64_Trunc_f32), Compile_Convert ), // 0xaf + M3OP_F( "i64.trunc_s/f64", 0, i_64, d_convertOpList (i64_Trunc_f64), Compile_Convert ), // 0xb0 + M3OP_F( "i64.trunc_u/f64", 0, i_64, d_convertOpList (u64_Trunc_f64), Compile_Convert ), // 0xb1 + + M3OP_F( "f32.convert_s/i32",0, f_32, d_convertOpList (f32_Convert_i32), Compile_Convert ), // 0xb2 + M3OP_F( "f32.convert_u/i32",0, f_32, d_convertOpList (f32_Convert_u32), Compile_Convert ), // 0xb3 + M3OP_F( "f32.convert_s/i64",0, f_32, d_convertOpList (f32_Convert_i64), Compile_Convert ), // 0xb4 + M3OP_F( "f32.convert_u/i64",0, f_32, d_convertOpList (f32_Convert_u64), Compile_Convert ), // 0xb5 + + M3OP_F( "f32.demote/f64", 0, f_32, d_unaryOpList (f32, Demote_f64), NULL ), // 0xb6 + + M3OP_F( "f64.convert_s/i32",0, f_64, d_convertOpList (f64_Convert_i32), Compile_Convert ), // 0xb7 + M3OP_F( "f64.convert_u/i32",0, f_64, d_convertOpList (f64_Convert_u32), Compile_Convert ), // 0xb8 + M3OP_F( "f64.convert_s/i64",0, f_64, d_convertOpList (f64_Convert_i64), Compile_Convert ), // 0xb9 + M3OP_F( "f64.convert_u/i64",0, f_64, d_convertOpList (f64_Convert_u64), Compile_Convert ), // 0xba + + M3OP_F( "f64.promote/f32", 0, f_64, d_unaryOpList (f64, Promote_f32), NULL ), // 0xbb + + M3OP_F( "i32.reinterpret/f32",0,i_32, d_convertOpList (i32_Reinterpret_f32), Compile_Convert ), // 0xbc + M3OP_F( "i64.reinterpret/f64",0,i_64, d_convertOpList (i64_Reinterpret_f64), Compile_Convert ), // 0xbd + M3OP_F( "f32.reinterpret/i32",0,f_32, d_convertOpList (f32_Reinterpret_i32), Compile_Convert ), // 0xbe + M3OP_F( "f64.reinterpret/i64",0,f_64, d_convertOpList (f64_Reinterpret_i64), Compile_Convert ), // 0xbf + + M3OP( "i32.extend8_s", 0, i_32, d_unaryOpList (i32, Extend8_s), NULL ), // 0xc0 + M3OP( "i32.extend16_s", 0, i_32, d_unaryOpList (i32, Extend16_s), NULL ), // 0xc1 + M3OP( "i64.extend8_s", 0, i_64, d_unaryOpList (i64, Extend8_s), NULL ), // 0xc2 + M3OP( "i64.extend16_s", 0, i_64, d_unaryOpList (i64, Extend16_s), NULL ), // 0xc3 + M3OP( "i64.extend32_s", 0, i_64, d_unaryOpList (i64, Extend32_s), NULL ), // 0xc4 + +# ifdef DEBUG // for codepage logging. the order doesn't matter: +# define d_m3DebugOp(OP) M3OP (#OP, 0, none, { op_##OP }) + +# if d_m3HasFloat +# define d_m3DebugTypedOp(OP) M3OP (#OP, 0, none, { op_##OP##_i32, op_##OP##_i64, op_##OP##_f32, op_##OP##_f64, }) +# else +# define d_m3DebugTypedOp(OP) M3OP (#OP, 0, none, { op_##OP##_i32, op_##OP##_i64 }) +# endif + + d_m3DebugOp (Compile), d_m3DebugOp (Entry), d_m3DebugOp (End), + d_m3DebugOp (Unsupported), d_m3DebugOp (CallRawFunction), + + d_m3DebugOp (GetGlobal_s32), d_m3DebugOp (GetGlobal_s64), d_m3DebugOp (ContinueLoop), d_m3DebugOp (ContinueLoopIf), + + d_m3DebugOp (CopySlot_32), d_m3DebugOp (PreserveCopySlot_32), d_m3DebugOp (If_s), d_m3DebugOp (BranchIfPrologue_s), + d_m3DebugOp (CopySlot_64), d_m3DebugOp (PreserveCopySlot_64), d_m3DebugOp (If_r), d_m3DebugOp (BranchIfPrologue_r), + + d_m3DebugOp (Select_i32_rss), d_m3DebugOp (Select_i32_srs), d_m3DebugOp (Select_i32_ssr), d_m3DebugOp (Select_i32_sss), + d_m3DebugOp (Select_i64_rss), d_m3DebugOp (Select_i64_srs), d_m3DebugOp (Select_i64_ssr), d_m3DebugOp (Select_i64_sss), + +# if d_m3HasFloat + d_m3DebugOp (Select_f32_sss), d_m3DebugOp (Select_f32_srs), d_m3DebugOp (Select_f32_ssr), + d_m3DebugOp (Select_f32_rss), d_m3DebugOp (Select_f32_rrs), d_m3DebugOp (Select_f32_rsr), + + d_m3DebugOp (Select_f64_sss), d_m3DebugOp (Select_f64_srs), d_m3DebugOp (Select_f64_ssr), + d_m3DebugOp (Select_f64_rss), d_m3DebugOp (Select_f64_rrs), d_m3DebugOp (Select_f64_rsr), +# endif + + d_m3DebugOp (MemFill), d_m3DebugOp (MemCopy), + + d_m3DebugTypedOp (SetGlobal), d_m3DebugOp (SetGlobal_s32), d_m3DebugOp (SetGlobal_s64), + + d_m3DebugTypedOp (SetRegister), d_m3DebugTypedOp (SetSlot), d_m3DebugTypedOp (PreserveSetSlot), +# endif + +# if d_m3CascadedOpcodes + [c_waOp_extended] = M3OP( "0xFC", 0, c_m3Type_unknown, d_emptyOpList, Compile_ExtendedOpcode ), +# endif + +# ifdef DEBUG + M3OP( "termination", 0, c_m3Type_unknown ) // for find_operation_info +# endif +}; + +const M3OpInfo c_operationsFC [] = +{ + M3OP_F( "i32.trunc_s:sat/f32",0, i_32, d_convertOpList (i32_TruncSat_f32), Compile_Convert ), // 0x00 + M3OP_F( "i32.trunc_u:sat/f32",0, i_32, d_convertOpList (u32_TruncSat_f32), Compile_Convert ), // 0x01 + M3OP_F( "i32.trunc_s:sat/f64",0, i_32, d_convertOpList (i32_TruncSat_f64), Compile_Convert ), // 0x02 + M3OP_F( "i32.trunc_u:sat/f64",0, i_32, d_convertOpList (u32_TruncSat_f64), Compile_Convert ), // 0x03 + M3OP_F( "i64.trunc_s:sat/f32",0, i_64, d_convertOpList (i64_TruncSat_f32), Compile_Convert ), // 0x04 + M3OP_F( "i64.trunc_u:sat/f32",0, i_64, d_convertOpList (u64_TruncSat_f32), Compile_Convert ), // 0x05 + M3OP_F( "i64.trunc_s:sat/f64",0, i_64, d_convertOpList (i64_TruncSat_f64), Compile_Convert ), // 0x06 + M3OP_F( "i64.trunc_u:sat/f64",0, i_64, d_convertOpList (u64_TruncSat_f64), Compile_Convert ), // 0x07 + + M3OP_RESERVED, M3OP_RESERVED, + + M3OP( "memory.copy", 0, none, d_emptyOpList, Compile_Memory_CopyFill ), // 0x0a + M3OP( "memory.fill", 0, none, d_emptyOpList, Compile_Memory_CopyFill ), // 0x0b + + +# ifdef DEBUG + M3OP( "termination", 0, c_m3Type_unknown ) // for find_operation_info +# endif +}; + + +IM3OpInfo GetOpInfo (m3opcode_t opcode) +{ + switch (opcode >> 8) { + case 0x00: + if (M3_LIKELY(opcode < M3_COUNT_OF(c_operations))) { + return &c_operations[opcode]; + } + break; + case c_waOp_extended: + opcode &= 0xFF; + if (M3_LIKELY(opcode < M3_COUNT_OF(c_operationsFC))) { + return &c_operationsFC[opcode]; + } + break; + } + return NULL; +} + +M3Result CompileBlockStatements (IM3Compilation o) +{ + M3Result result = m3Err_none; + bool validEnd = false; + + while (o->wasm < o->wasmEnd) + { +# if d_m3EnableOpTracing + if (o->numEmits) + { + EmitOp (o, op_DumpStack); + EmitConstant32 (o, o->numOpcodes); + EmitConstant32 (o, GetMaxUsedSlotPlusOne(o)); + EmitPointer (o, o->function); + + o->numEmits = 0; + } +# endif + m3opcode_t opcode; + o->lastOpcodeStart = o->wasm; +_ (Read_opcode (& opcode, & o->wasm, o->wasmEnd)); log_opcode (o, opcode); + + // Restrict opcodes when evaluating expressions + if (not o->function) { + switch (opcode) { + case c_waOp_i32_const: case c_waOp_i64_const: + case c_waOp_f32_const: case c_waOp_f64_const: + case c_waOp_getGlobal: case c_waOp_end: + break; + default: + _throw(m3Err_restrictedOpcode); + } + } + + IM3OpInfo opinfo = GetOpInfo (opcode); + + if (opinfo == NULL) + _throw (ErrorCompile (m3Err_unknownOpcode, o, "opcode '%x' not available", opcode)); + + if (opinfo->compiler) { +_ ((* opinfo->compiler) (o, opcode)) + } else { +_ (Compile_Operator (o, opcode)); + } + + o->previousOpcode = opcode; + + if (opcode == c_waOp_else) + { + _throwif (m3Err_wasmMalformed, o->block.opcode != c_waOp_if); + validEnd = true; + break; + } + else if (opcode == c_waOp_end) + { + validEnd = true; + break; + } + } + _throwif(m3Err_wasmMalformed, !(validEnd)); + +_catch: + return result; +} + +static +M3Result PushBlockResults (IM3Compilation o) +{ + M3Result result = m3Err_none; + + u16 numResults = GetFuncTypeNumResults (o->block.type); + + for (u16 i = 0; i < numResults; ++i) + { + u8 type = GetFuncTypeResultType (o->block.type, i); + + if (i == numResults - 1 and IsFpType (type)) + { +_ (PushRegister (o, type)); + } + else +_ (PushAllocatedSlot (o, type)); + } + + _catch: return result; +} + + +M3Result CompileBlock (IM3Compilation o, IM3FuncType i_blockType, m3opcode_t i_blockOpcode) +{ + d_m3Assert (not IsRegisterAllocated (o, 0)); + d_m3Assert (not IsRegisterAllocated (o, 1)); + M3CompilationScope outerScope = o->block; + M3CompilationScope * block = & o->block; + + block->outer = & outerScope; + block->pc = GetPagePC (o->page); + block->patches = NULL; + block->type = i_blockType; + block->depth ++; + block->opcode = i_blockOpcode; + + /* + The block stack frame is a little strange but for good reasons. Because blocks need to be restarted to + compile different pathways (if/else), the incoming params must be saved. The parameters are popped + and validated. But, then the stack top is readjusted so they aren't subsequently overwritten. + Next, the result are preallocated to find destination slots. But again these are immediately popped + (deallocated) and the stack top is readjusted to keep these records in pace. This allows branch instructions + to find their result landing pads. Finally, the params are copied from the "dead" records and pushed back + onto the stack as active stack items for the CompileBlockStatements () call. + + [ block ] + [ params ] + ------------------ + [ result ] <---- blockStackIndex + [ slots ] + ------------------ + [ saved param ] + [ records ] + <----- exitStackIndex + */ + +_try { + // validate and dealloc params ---------------------------- + + u16 stackIndex = o->stackIndex; + + u16 numParams = GetFuncTypeNumParams (i_blockType); + + if (i_blockOpcode != c_waOp_else) + { + for (u16 i = 0; i < numParams; ++i) + { + u8 type = GetFuncTypeParamType (i_blockType, numParams - 1 - i); +_ (PopType (o, type)); + } + } + else { + if (IsStackPolymorphic (o) && o->block.blockStackIndex + numParams > o->stackIndex) { + o->stackIndex = o->block.blockStackIndex; + } else { + o->stackIndex -= numParams; + } + } + + u16 paramIndex = o->stackIndex; + block->exitStackIndex = paramIndex; // consume the params at block exit + + // keep copies of param slots in the stack + o->stackIndex = stackIndex; + + // find slots for the results ---------------------------- + PushBlockResults (o); + + stackIndex = o->stackIndex; + + // dealloc but keep record of the result slots in the stack + u16 numResults = GetFuncTypeNumResults (i_blockType); + while (numResults--) + Pop (o); + + block->blockStackIndex = o->stackIndex = stackIndex; + + // push the params back onto the stack ------------------- + for (u16 i = 0; i < numParams; ++i) + { + u8 type = GetFuncTypeParamType (i_blockType, i); + + u16 slot = GetSlotForStackIndex (o, paramIndex + i); + Push (o, type, slot); + + if (slot >= o->slotFirstDynamicIndex && slot != c_slotUnused) + MarkSlotsAllocatedByType (o, slot, type); + } + + //-------------------------------------------------------- + +_ (CompileBlockStatements (o)); + +_ (ValidateBlockEnd (o)); + + if (o->function) // skip for expressions + { + if (not IsStackPolymorphic (o)) +_ (ResolveBlockResults (o, & o->block, /* isBranch: */ false)); + +_ (UnwindBlockStack (o)) + + if (not ((i_blockOpcode == c_waOp_if and numResults) or o->previousOpcode == c_waOp_else)) + { + o->stackIndex = o->block.exitStackIndex; +_ (PushBlockResults (o)); + } + } + + PatchBranches (o); + + o->block = outerScope; + +} _catch: return result; +} + +static +M3Result CompileLocals (IM3Compilation o) +{ + M3Result result; + + u32 numLocals = 0; + u32 numLocalBlocks; +_ (ReadLEB_u32 (& numLocalBlocks, & o->wasm, o->wasmEnd)); + + for (u32 l = 0; l < numLocalBlocks; ++l) + { + u32 varCount; + i8 waType; + u8 localType; + +_ (ReadLEB_u32 (& varCount, & o->wasm, o->wasmEnd)); +_ (ReadLEB_i7 (& waType, & o->wasm, o->wasmEnd)); +_ (NormalizeType (& localType, waType)); + numLocals += varCount; m3log (compile, "pushing locals. count: %d; type: %s", varCount, c_waTypes [localType]); + while (varCount--) +_ (PushAllocatedSlot (o, localType)); + } + + if (o->function) + o->function->numLocals = numLocals; + + _catch: return result; +} + +static +M3Result ReserveConstants (IM3Compilation o) +{ + M3Result result = m3Err_none; + + // in the interest of speed, this blindly scans the Wasm code looking for any byte + // that looks like an const opcode. + u16 numConstantSlots = 0; + + bytes_t wa = o->wasm; + while (wa < o->wasmEnd) + { + u8 code = * wa++; + u16 addSlots = 0; + + if (code == c_waOp_i32_const or code == c_waOp_f32_const) + addSlots = 1; + else if (code == c_waOp_i64_const or code == c_waOp_f64_const) + addSlots = GetTypeNumSlots (c_m3Type_i64); + + if (numConstantSlots + addSlots >= d_m3MaxConstantTableSize) + break; + + numConstantSlots += addSlots; + } + + // if constants overflow their reserved stack space, the compiler simply emits op_Const + // operations as needed. Compiled expressions (global inits) don't pass through this + // ReserveConstants function and thus always produce inline constants. + + AlignSlotToType (& numConstantSlots, c_m3Type_i64); m3log (compile, "reserved constant slots: %d", numConstantSlots); + + o->slotFirstDynamicIndex = o->slotFirstConstIndex + numConstantSlots; + + if (o->slotFirstDynamicIndex >= d_m3MaxFunctionSlots) + _throw (m3Err_functionStackOverflow); + + _catch: + return result; +} + + +M3Result CompileFunction (IM3Function io_function) +{ + if (!io_function->wasm) return "function body is missing"; + + IM3FuncType funcType = io_function->funcType; m3log (compile, "compiling: [%d] %s %s; wasm-size: %d", + io_function->index, m3_GetFunctionName (io_function), SPrintFuncTypeSignature (funcType), (u32) (io_function->wasmEnd - io_function->wasm)); + IM3Runtime runtime = io_function->module->runtime; + + IM3Compilation o = & runtime->compilation; d_m3Assert (d_m3MaxFunctionSlots >= d_m3MaxFunctionStackHeight * (d_m3Use32BitSlots + 1)) // need twice as many slots in 32-bit mode + memset (o, 0x0, sizeof (M3Compilation)); + + o->runtime = runtime; + o->module = io_function->module; + o->function = io_function; + o->wasm = io_function->wasm; + o->wasmEnd = io_function->wasmEnd; + o->block.type = funcType; + +_try { + // skip over code size. the end was already calculated during parse phase + u32 size; +_ (ReadLEB_u32 (& size, & o->wasm, o->wasmEnd)); d_m3Assert (size == (o->wasmEnd - o->wasm)) + +_ (AcquireCompilationCodePage (o, & o->page)); + + pc_t pc = GetPagePC (o->page); + + u16 numRetSlots = GetFunctionNumReturns (o->function) * c_ioSlotCount; + + for (u16 i = 0; i < numRetSlots; ++i) + MarkSlotAllocated (o, i); + + o->function->numRetSlots = o->slotFirstDynamicIndex = numRetSlots; + + u16 numArgs = GetFunctionNumArgs (o->function); + + // push the arg types to the type stack + for (u16 i = 0; i < numArgs; ++i) + { + u8 type = GetFunctionArgType (o->function, i); +_ (PushAllocatedSlot (o, type)); + + // prevent allocator fill-in + o->slotFirstDynamicIndex += c_ioSlotCount; + } + + o->slotMaxAllocatedIndexPlusOne = o->function->numRetAndArgSlots = o->slotFirstLocalIndex = o->slotFirstDynamicIndex; + +_ (CompileLocals (o)); + + u16 maxSlot = GetMaxUsedSlotPlusOne (o); + + o->function->numLocalBytes = (maxSlot - o->slotFirstLocalIndex) * sizeof (m3slot_t); + + o->slotFirstConstIndex = o->slotMaxConstIndex = maxSlot; + + // ReserveConstants initializes o->firstDynamicSlotNumber +_ (ReserveConstants (o)); + + // start tracking the max stack used (Push() also updates this value) so that op_Entry can precisely detect stack overflow + o->maxStackSlots = o->slotMaxAllocatedIndexPlusOne = o->slotFirstDynamicIndex; + + o->block.blockStackIndex = o->stackFirstDynamicIndex = o->stackIndex; m3log (compile, "start stack index: %d", + (u32) o->stackFirstDynamicIndex); +_ (EmitOp (o, op_Entry)); + EmitPointer (o, io_function); + +_ (CompileBlockStatements (o)); + + // TODO: validate opcode sequences + _throwif(m3Err_wasmMalformed, o->previousOpcode != c_waOp_end); + + io_function->compiled = pc; + io_function->maxStackSlots = o->maxStackSlots; + + u16 numConstantSlots = o->slotMaxConstIndex - o->slotFirstConstIndex; m3log (compile, "unique constant slots: %d; unused slots: %d", + numConstantSlots, o->slotFirstDynamicIndex - o->slotMaxConstIndex); + io_function->numConstantBytes = numConstantSlots * sizeof (m3slot_t); + + if (numConstantSlots) + { + io_function->constants = m3_CopyMem (o->constants, io_function->numConstantBytes); + _throwifnull(io_function->constants); + } + +} _catch: + + ReleaseCompilationCodePage (o); + + return result; +} diff --git a/cpp/m3_compile.h b/cpp/m3_compile.h new file mode 100644 index 0000000..d4a6733 --- /dev/null +++ b/cpp/m3_compile.h @@ -0,0 +1,206 @@ +// +// m3_compile.h +// +// Created by Steven Massey on 4/17/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#ifndef m3_compile_h +#define m3_compile_h + +#include "m3_code.h" +#include "m3_exec_defs.h" +#include "m3_function.h" + +d_m3BeginExternC + +enum +{ + c_waOp_block = 0x02, + c_waOp_loop = 0x03, + c_waOp_if = 0x04, + c_waOp_else = 0x05, + c_waOp_end = 0x0b, + c_waOp_branch = 0x0c, + c_waOp_branchTable = 0x0e, + c_waOp_branchIf = 0x0d, + c_waOp_call = 0x10, + c_waOp_getLocal = 0x20, + c_waOp_setLocal = 0x21, + c_waOp_teeLocal = 0x22, + + c_waOp_getGlobal = 0x23, + + c_waOp_store_f32 = 0x38, + c_waOp_store_f64 = 0x39, + + c_waOp_i32_const = 0x41, + c_waOp_i64_const = 0x42, + c_waOp_f32_const = 0x43, + c_waOp_f64_const = 0x44, + + c_waOp_extended = 0xfc, + + c_waOp_memoryCopy = 0xfc0a, + c_waOp_memoryFill = 0xfc0b +}; + + +#define d_FuncRetType(ftype,i) ((ftype)->types[(i)]) +#define d_FuncArgType(ftype,i) ((ftype)->types[(ftype)->numRets + (i)]) + +//----------------------------------------------------------------------------------------------------------------------------------- + +typedef struct M3CompilationScope +{ + struct M3CompilationScope * outer; + + pc_t pc; // used by ContinueLoop's + pc_t patches; + i32 depth; + u16 exitStackIndex; + u16 blockStackIndex; +// u16 topSlot; + IM3FuncType type; + m3opcode_t opcode; + bool isPolymorphic; +} +M3CompilationScope; + +typedef M3CompilationScope * IM3CompilationScope; + +typedef struct +{ + IM3Runtime runtime; + IM3Module module; + + bytes_t wasm; + bytes_t wasmEnd; + bytes_t lastOpcodeStart; + + M3CompilationScope block; + + IM3Function function; + + IM3CodePage page; + +#ifdef DEBUG + u32 numEmits; + u32 numOpcodes; +#endif + + u16 stackFirstDynamicIndex; // args and locals are pushed to the stack so that their slot locations can be tracked. the wasm model itself doesn't + // treat these values as being on the stack, so stackFirstDynamicIndex marks the start of the real Wasm stack + u16 stackIndex; // current stack top + + u16 slotFirstConstIndex; + u16 slotMaxConstIndex; // as const's are encountered during compilation this tracks their location in the "real" stack + + u16 slotFirstLocalIndex; + u16 slotFirstDynamicIndex; // numArgs + numLocals + numReservedConstants. the first mutable slot available to the compiler. + + u16 maxStackSlots; + + m3slot_t constants [d_m3MaxConstantTableSize]; + + // 'wasmStack' holds slot locations + u16 wasmStack [d_m3MaxFunctionStackHeight]; + u8 typeStack [d_m3MaxFunctionStackHeight]; + + // 'm3Slots' contains allocation usage counts + u8 m3Slots [d_m3MaxFunctionSlots]; + + u16 slotMaxAllocatedIndexPlusOne; + + u16 regStackIndexPlusOne [2]; + + m3opcode_t previousOpcode; +} +M3Compilation; + +typedef M3Compilation * IM3Compilation; + +typedef M3Result (* M3Compiler) (IM3Compilation, m3opcode_t); + + +//----------------------------------------------------------------------------------------------------------------------------------- + + +typedef struct M3OpInfo +{ +#ifdef DEBUG + const char * const name; +#endif + + i8 stackOffset; + u8 type; + + // for most operations: + // [0]= top operand in register, [1]= top operand in stack, [2]= both operands in stack + IM3Operation operations [4]; + + M3Compiler compiler; +} +M3OpInfo; + +typedef const M3OpInfo * IM3OpInfo; + +IM3OpInfo GetOpInfo (m3opcode_t opcode); + +// TODO: This helper should be removed, when MultiValue is implemented +static inline +u8 GetSingleRetType(IM3FuncType ftype) { + return (ftype && ftype->numRets) ? ftype->types[0] : (u8)c_m3Type_none; +} + +static const u16 c_m3RegisterUnallocated = 0; +static const u16 c_slotUnused = 0xffff; + +static inline +bool IsRegisterAllocated (IM3Compilation o, u32 i_register) +{ + return (o->regStackIndexPlusOne [i_register] != c_m3RegisterUnallocated); +} + +static inline +bool IsStackPolymorphic (IM3Compilation o) +{ + return o->block.isPolymorphic; +} + +static inline bool IsRegisterSlotAlias (u16 i_slot) { return (i_slot >= d_m3Reg0SlotAlias and i_slot != c_slotUnused); } +static inline bool IsFpRegisterSlotAlias (u16 i_slot) { return (i_slot == d_m3Fp0SlotAlias); } +static inline bool IsIntRegisterSlotAlias (u16 i_slot) { return (i_slot == d_m3Reg0SlotAlias); } + + +#ifdef DEBUG + #define M3OP(...) { __VA_ARGS__ } + #define M3OP_RESERVED { "reserved" } +#else + // Strip-off name + #define M3OP(name, ...) { __VA_ARGS__ } + #define M3OP_RESERVED { 0 } +#endif + +#if d_m3HasFloat + #define M3OP_F M3OP +#elif d_m3NoFloatDynamic + #define M3OP_F(n,o,t,op,...) M3OP(n, o, t, { op_Unsupported, op_Unsupported, op_Unsupported, op_Unsupported }, __VA_ARGS__) +#else + #define M3OP_F(...) { 0 } +#endif + +//----------------------------------------------------------------------------------------------------------------------------------- + +u16 GetMaxUsedSlotPlusOne (IM3Compilation o); + +M3Result CompileBlock (IM3Compilation io, IM3FuncType i_blockType, m3opcode_t i_blockOpcode); + +M3Result CompileBlockStatements (IM3Compilation io); +M3Result CompileFunction (IM3Function io_function); + +M3Result CompileRawFunction (IM3Module io_module, IM3Function io_function, const void * i_function, const void * i_userdata); + +d_m3EndExternC + +#endif // m3_compile_h diff --git a/cpp/m3_config.h b/cpp/m3_config.h new file mode 100644 index 0000000..a7f1532 --- /dev/null +++ b/cpp/m3_config.h @@ -0,0 +1,156 @@ +// +// m3_config.h +// +// Created by Steven Massey on 5/4/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#ifndef m3_config_h +#define m3_config_h + +#include "m3_config_platforms.h" + +// general -------------------------------------------------------------------- + +# ifndef d_m3CodePageAlignSize +# define d_m3CodePageAlignSize 32*1024 +# endif + +# ifndef d_m3MaxFunctionStackHeight +# define d_m3MaxFunctionStackHeight 2000 // max: 32768 +# endif + +# ifndef d_m3MaxLinearMemoryPages +# define d_m3MaxLinearMemoryPages 65536 +# endif + +# ifndef d_m3MaxFunctionSlots +# define d_m3MaxFunctionSlots ((d_m3MaxFunctionStackHeight)*2) +# endif + +# ifndef d_m3MaxConstantTableSize +# define d_m3MaxConstantTableSize 120 +# endif + +# ifndef d_m3MaxDuplicateFunctionImpl +# define d_m3MaxDuplicateFunctionImpl 3 +# endif + +# ifndef d_m3CascadedOpcodes // Cascaded opcodes are slightly faster at the expense of some memory +# define d_m3CascadedOpcodes 1 // Adds ~3Kb to operations table in m3_compile.c +# endif + +# ifndef d_m3VerboseErrorMessages +# define d_m3VerboseErrorMessages 1 +# endif + +# ifndef d_m3FixedHeap +# define d_m3FixedHeap false +//# define d_m3FixedHeap (32*1024) +# endif + +# ifndef d_m3FixedHeapAlign +# define d_m3FixedHeapAlign 16 +# endif + +# ifndef d_m3Use32BitSlots +# define d_m3Use32BitSlots 1 +# endif + +# ifndef d_m3ProfilerSlotMask +# define d_m3ProfilerSlotMask 0xFFFF +# endif + +# ifndef d_m3RecordBacktraces +# define d_m3RecordBacktraces 0 +# endif + +# ifndef d_m3EnableExceptionBreakpoint +# define d_m3EnableExceptionBreakpoint 0 // see m3_exception.h +# endif + + +// profiling and tracing ------------------------------------------------------ + +# ifndef d_m3EnableOpProfiling +# define d_m3EnableOpProfiling 0 // opcode usage counters +# endif + +# ifndef d_m3EnableOpTracing +# define d_m3EnableOpTracing 0 // only works with DEBUG +# endif + +# ifndef d_m3EnableWasiTracing +# define d_m3EnableWasiTracing 0 +# endif + +# ifndef d_m3EnableStrace +# define d_m3EnableStrace 0 // 1 - trace exported function calls + // 2 - trace all calls (structured) + // 3 - all calls + loops + memory operations +# endif + + +// logging -------------------------------------------------------------------- + +# ifndef d_m3LogParse +# define d_m3LogParse 0 // .wasm binary decoding info +# endif + +# ifndef d_m3LogModule +# define d_m3LogModule 0 // wasm module info +# endif + +# ifndef d_m3LogCompile +# define d_m3LogCompile 0 // wasm -> metacode generation phase +# endif + +# ifndef d_m3LogWasmStack +# define d_m3LogWasmStack 0 // dump the wasm stack when pushed or popped +# endif + +# ifndef d_m3LogEmit +# define d_m3LogEmit 0 // metacode generation info +# endif + +# ifndef d_m3LogCodePages +# define d_m3LogCodePages 0 // dump metacode pages when released +# endif + +# ifndef d_m3LogRuntime +# define d_m3LogRuntime 0 // higher-level runtime information +# endif + +# ifndef d_m3LogNativeStack +# define d_m3LogNativeStack 0 // track the memory usage of the C-stack +# endif + +# ifndef d_m3LogHeapOps +# define d_m3LogHeapOps 0 // track heap usage +# endif + +# ifndef d_m3LogTimestamps +# define d_m3LogTimestamps 0 // track timestamps on heap logs +# endif + +// other ---------------------------------------------------------------------- + +# ifndef d_m3HasFloat +# define d_m3HasFloat 1 // implement floating point ops +# endif + +#if !d_m3HasFloat && !defined(d_m3NoFloatDynamic) +# define d_m3NoFloatDynamic 1 // if no floats, do not fail until flops are actually executed +#endif + +# ifndef d_m3SkipStackCheck +# define d_m3SkipStackCheck 0 // skip stack overrun checks +# endif + +# ifndef d_m3SkipMemoryBoundsCheck +# define d_m3SkipMemoryBoundsCheck 0 // skip memory bounds checks +# endif + +#define d_m3EnableCodePageRefCounting 0 // not supported currently + +#endif // m3_config_h diff --git a/cpp/m3_config_platforms.h b/cpp/m3_config_platforms.h new file mode 100644 index 0000000..50b86ac --- /dev/null +++ b/cpp/m3_config_platforms.h @@ -0,0 +1,208 @@ +// +// m3_config_platforms.h +// +// Created by Volodymyr Shymanskyy on 11/20/19. +// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. +// + +#ifndef m3_config_platforms_h +#define m3_config_platforms_h + +#include "wasm3_defs.h" + +/* + * Internal helpers + */ + +# if !defined(__cplusplus) || defined(_MSC_VER) +# define not ! +# define and && +# define or || +# endif + +/* + * Detect/define features + */ + +# if defined(M3_COMPILER_MSVC) +# include +# if UINTPTR_MAX == 0xFFFFFFFF +# define M3_SIZEOF_PTR 4 +# elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFFu +# define M3_SIZEOF_PTR 8 +# else +# error "Pointer size not supported" +# endif +# elif defined(__SIZEOF_POINTER__) +# define M3_SIZEOF_PTR __SIZEOF_POINTER__ +#else +# error "Pointer size not detected" +# endif + +# if defined(M3_BIG_ENDIAN) +# define M3_BSWAP_u8(X) {} +# define M3_BSWAP_u16(X) { (X)=m3_bswap16((X)); } +# define M3_BSWAP_u32(X) { (X)=m3_bswap32((X)); } +# define M3_BSWAP_u64(X) { (X)=m3_bswap64((X)); } +# define M3_BSWAP_i8(X) {} +# define M3_BSWAP_i16(X) M3_BSWAP_u16(X) +# define M3_BSWAP_i32(X) M3_BSWAP_u32(X) +# define M3_BSWAP_i64(X) M3_BSWAP_u64(X) +# define M3_BSWAP_f32(X) { union { f32 f; u32 i; } u; u.f = (X); M3_BSWAP_u32(u.i); (X) = u.f; } +# define M3_BSWAP_f64(X) { union { f64 f; u64 i; } u; u.f = (X); M3_BSWAP_u64(u.i); (X) = u.f; } +# else +# define M3_BSWAP_u8(X) {} +# define M3_BSWAP_u16(x) {} +# define M3_BSWAP_u32(x) {} +# define M3_BSWAP_u64(x) {} +# define M3_BSWAP_i8(X) {} +# define M3_BSWAP_i16(X) {} +# define M3_BSWAP_i32(X) {} +# define M3_BSWAP_i64(X) {} +# define M3_BSWAP_f32(X) {} +# define M3_BSWAP_f64(X) {} +# endif + +# if defined(M3_COMPILER_MSVC) +# define M3_WEAK //__declspec(selectany) +# define M3_NO_UBSAN +# define M3_NOINLINE +# elif defined(__MINGW32__) || defined(__CYGWIN__) +# define M3_WEAK //__attribute__((selectany)) +# define M3_NO_UBSAN +# define M3_NOINLINE __attribute__((noinline)) +# else +# define M3_WEAK __attribute__((weak)) +# define M3_NO_UBSAN //__attribute__((no_sanitize("undefined"))) +// Workaround for Cosmopolitan noinline conflict: https://github.com/jart/cosmopolitan/issues/310 +# if defined(noinline) +# define M3_NOINLINE noinline +# else +# define M3_NOINLINE __attribute__((noinline)) +# endif +# endif + +# if M3_COMPILER_HAS_ATTRIBUTE(musttail) +# define M3_MUSTTAIL __attribute__((musttail)) +# else +# define M3_MUSTTAIL +# endif + +# ifndef M3_MIN +# define M3_MIN(A,B) (((A) < (B)) ? (A) : (B)) +# endif +# ifndef M3_MAX +# define M3_MAX(A,B) (((A) > (B)) ? (A) : (B)) +# endif + +#define M3_INIT(field) memset(&field, 0, sizeof(field)) + +#define M3_COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) + +#if defined(__AVR__) + +#include + +# define PRIu64 "llu" +# define PRIi64 "lli" + +# define d_m3ShortTypesDefined +typedef double f64; +typedef float f32; +typedef uint64_t u64; +typedef int64_t i64; +typedef uint32_t u32; +typedef int32_t i32; +typedef short unsigned u16; +typedef short i16; +typedef uint8_t u8; +typedef int8_t i8; + +#endif + +/* + * Apply settings + */ + +# if defined (M3_COMPILER_MSVC) +# define vectorcall // For MSVC, better not to specify any call convention +# elif defined(__x86_64__) +# define vectorcall +//# elif defined(__riscv) && (__riscv_xlen == 64) +//# define vectorcall +# elif defined(__MINGW32__) +# define vectorcall +# elif defined(WIN32) +# define vectorcall __vectorcall +# elif defined (ESP8266) +# include +# define vectorcall //ICACHE_FLASH_ATTR +# elif defined (ESP32) +# if defined(M3_IN_IRAM) // the interpreter is in IRAM, attribute not needed +# define vectorcall +# else +# include "esp_system.h" +# define vectorcall IRAM_ATTR +# endif +# elif defined (FOMU) +# define vectorcall __attribute__((section(".ramtext"))) +# endif + +#ifndef vectorcall +#define vectorcall +#endif + + +/* + * Device-specific defaults + */ + +# ifndef d_m3MaxFunctionStackHeight +# if defined(ESP8266) || defined(ESP32) || defined(ARDUINO_AMEBA) || defined(TEENSYDUINO) +# define d_m3MaxFunctionStackHeight 256 +# endif +# endif + +# ifndef d_m3FixedHeap +# if defined(ARDUINO_AMEBA) +# define d_m3FixedHeap (128*1024) +# elif defined(BLUE_PILL) || defined(FOMU) +# define d_m3FixedHeap (12*1024) +# elif defined(ARDUINO_ARCH_ARC32) // Arduino 101 +# define d_m3FixedHeap (10*1024) +# endif +# endif + +/* + * Platform-specific defaults + */ + +# if defined(ARDUINO) || defined(PARTICLE) || defined(PLATFORMIO) || defined(__MBED__) || \ + defined(ESP8266) || defined(ESP32) || defined(BLUE_PILL) || defined(WM_W600) || defined(FOMU) +# ifndef d_m3CascadedOpcodes +# define d_m3CascadedOpcodes 0 +# endif +# ifndef d_m3VerboseErrorMessages +# define d_m3VerboseErrorMessages 0 +# endif +# ifndef d_m3MaxConstantTableSize +# define d_m3MaxConstantTableSize 64 +# endif +# ifndef d_m3MaxFunctionStackHeight +# define d_m3MaxFunctionStackHeight 128 +# endif +# ifndef d_m3CodePageAlignSize +# define d_m3CodePageAlignSize 1024 +# endif +# endif + +/* + * Arch-specific defaults + */ +#if defined(__riscv) && (__riscv_xlen == 64) +# ifndef d_m3Use32BitSlots +# define d_m3Use32BitSlots 0 +# endif +#endif + +#endif // m3_config_platforms_h diff --git a/cpp/m3_core.c b/cpp/m3_core.c new file mode 100644 index 0000000..5980ab8 --- /dev/null +++ b/cpp/m3_core.c @@ -0,0 +1,615 @@ +// +// m3_core.c +// +// Created by Steven Massey on 4/15/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#define M3_IMPLEMENT_ERROR_STRINGS +#include "m3_config.h" +#include "wasm3.h" + +#include "m3_core.h" +#include "m3_env.h" + +void m3_Abort(const char* message) { +#ifdef DEBUG + fprintf(stderr, "Error: %s\n", message); +#endif + abort(); +} + +M3_WEAK +M3Result m3_Yield () +{ + return m3Err_none; +} + +#if d_m3LogTimestamps + +#include + +#define SEC_TO_US(sec) ((sec)*1000000) +#define NS_TO_US(ns) ((ns)/1000) + +static uint64_t initial_ts = -1; + +uint64_t m3_GetTimestamp() +{ + if (initial_ts == -1) { + initial_ts = 0; + initial_ts = m3_GetTimestamp(); + } + struct timespec ts; + timespec_get(&ts, TIME_UTC); + uint64_t us = SEC_TO_US((uint64_t)ts.tv_sec) + NS_TO_US((uint64_t)ts.tv_nsec); + return us - initial_ts; +} + +#endif + +#if d_m3FixedHeap + +static u8 fixedHeap[d_m3FixedHeap]; +static u8* fixedHeapPtr = fixedHeap; +static u8* const fixedHeapEnd = fixedHeap + d_m3FixedHeap; +static u8* fixedHeapLast = NULL; + +#if d_m3FixedHeapAlign > 1 +# define HEAP_ALIGN_PTR(P) P = (u8*)(((size_t)(P)+(d_m3FixedHeapAlign-1)) & ~ (d_m3FixedHeapAlign-1)); +#else +# define HEAP_ALIGN_PTR(P) +#endif + +void * m3_Malloc_Impl (size_t i_size) +{ + u8 * ptr = fixedHeapPtr; + + fixedHeapPtr += i_size; + HEAP_ALIGN_PTR(fixedHeapPtr); + + if (fixedHeapPtr >= fixedHeapEnd) + { + return NULL; + } + + memset (ptr, 0x0, i_size); + fixedHeapLast = ptr; + + return ptr; +} + +void m3_Free_Impl (void * i_ptr) +{ + // Handle the last chunk + if (i_ptr && i_ptr == fixedHeapLast) { + fixedHeapPtr = fixedHeapLast; + fixedHeapLast = NULL; + } else { + //printf("== free %p [failed]\n", io_ptr); + } +} + +void * m3_Realloc_Impl (void * i_ptr, size_t i_newSize, size_t i_oldSize) +{ + if (M3_UNLIKELY(i_newSize == i_oldSize)) return i_ptr; + + void * newPtr; + + // Handle the last chunk + if (i_ptr && i_ptr == fixedHeapLast) { + fixedHeapPtr = fixedHeapLast + i_newSize; + HEAP_ALIGN_PTR(fixedHeapPtr); + if (fixedHeapPtr >= fixedHeapEnd) + { + return NULL; + } + newPtr = i_ptr; + } else { + newPtr = m3_Malloc_Impl(i_newSize); + if (!newPtr) { + return NULL; + } + if (i_ptr) { + memcpy(newPtr, i_ptr, i_oldSize); + } + } + + if (i_newSize > i_oldSize) { + memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize); + } + + return newPtr; +} + +#else + +void * m3_Malloc_Impl (size_t i_size) +{ + return calloc (i_size, 1); +} + +void m3_Free_Impl (void * io_ptr) +{ + free (io_ptr); +} + +void * m3_Realloc_Impl (void * i_ptr, size_t i_newSize, size_t i_oldSize) +{ + if (M3_UNLIKELY(i_newSize == i_oldSize)) return i_ptr; + + void * newPtr = realloc (i_ptr, i_newSize); + + if (M3_LIKELY(newPtr)) + { + if (i_newSize > i_oldSize) { + memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize); + } + return newPtr; + } + return NULL; +} + +#endif + +void * m3_CopyMem (const void * i_from, size_t i_size) +{ + void * ptr = m3_Malloc("CopyMem", i_size); + if (ptr) { + memcpy (ptr, i_from, i_size); + } + return ptr; +} + +//-------------------------------------------------------------------------------------------- + +#if d_m3LogNativeStack + +static size_t stack_start; +static size_t stack_end; + +void m3StackCheckInit () +{ + char stack; + stack_end = stack_start = (size_t)&stack; +} + +void m3StackCheck () +{ + char stack; + size_t addr = (size_t)&stack; + + size_t stackEnd = stack_end; + stack_end = M3_MIN (stack_end, addr); + +// if (stackEnd != stack_end) +// printf ("maxStack: %ld\n", m3StackGetMax ()); +} + +int m3StackGetMax () +{ + return stack_start - stack_end; +} + +#endif + +//-------------------------------------------------------------------------------------------- + +M3Result NormalizeType (u8 * o_type, i8 i_convolutedWasmType) +{ + M3Result result = m3Err_none; + + u8 type = -i_convolutedWasmType; + + if (type == 0x40) + type = c_m3Type_none; + else if (type < c_m3Type_i32 or type > c_m3Type_f64) + result = m3Err_invalidTypeId; + + * o_type = type; + + return result; +} + + +bool IsFpType (u8 i_m3Type) +{ + return (i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_f64); +} + + +bool IsIntType (u8 i_m3Type) +{ + return (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_i64); +} + + +bool Is64BitType (u8 i_m3Type) +{ + if (i_m3Type == c_m3Type_i64 or i_m3Type == c_m3Type_f64) + return true; + else if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_none) + return false; + else + return (sizeof (voidptr_t) == 8); // all other cases are pointers +} + +u32 SizeOfType (u8 i_m3Type) +{ + if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32) + return sizeof (i32); + + return sizeof (i64); +} + + +//-- Binary Wasm parsing utils ------------------------------------------------------------------------------------------ + + +M3Result Read_u64 (u64 * o_value, bytes_t * io_bytes, cbytes_t i_end) +{ + const u8 * ptr = * io_bytes; + ptr += sizeof (u64); + + if (ptr <= i_end) + { + memcpy(o_value, * io_bytes, sizeof(u64)); + M3_BSWAP_u64(*o_value); + * io_bytes = ptr; + return m3Err_none; + } + else return m3Err_wasmUnderrun; +} + + +M3Result Read_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end) +{ + const u8 * ptr = * io_bytes; + ptr += sizeof (u32); + + if (ptr <= i_end) + { + memcpy(o_value, * io_bytes, sizeof(u32)); + M3_BSWAP_u32(*o_value); + * io_bytes = ptr; + return m3Err_none; + } + else return m3Err_wasmUnderrun; +} + +#if d_m3ImplementFloat + +M3Result Read_f64 (f64 * o_value, bytes_t * io_bytes, cbytes_t i_end) +{ + const u8 * ptr = * io_bytes; + ptr += sizeof (f64); + + if (ptr <= i_end) + { + memcpy(o_value, * io_bytes, sizeof(f64)); + M3_BSWAP_f64(*o_value); + * io_bytes = ptr; + return m3Err_none; + } + else return m3Err_wasmUnderrun; +} + + +M3Result Read_f32 (f32 * o_value, bytes_t * io_bytes, cbytes_t i_end) +{ + const u8 * ptr = * io_bytes; + ptr += sizeof (f32); + + if (ptr <= i_end) + { + memcpy(o_value, * io_bytes, sizeof(f32)); + M3_BSWAP_f32(*o_value); + * io_bytes = ptr; + return m3Err_none; + } + else return m3Err_wasmUnderrun; +} + +#endif + +M3Result Read_u8 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end) +{ + const u8 * ptr = * io_bytes; + + if (ptr < i_end) + { + * o_value = * ptr; + * io_bytes = ptr + 1; + + return m3Err_none; + } + else return m3Err_wasmUnderrun; +} + +M3Result Read_opcode (m3opcode_t * o_value, bytes_t * io_bytes, cbytes_t i_end) +{ + const u8 * ptr = * io_bytes; + + if (ptr < i_end) + { + m3opcode_t opcode = * ptr++; + +#if d_m3CascadedOpcodes == 0 + if (M3_UNLIKELY(opcode == c_waOp_extended)) + { + if (ptr < i_end) + { + opcode = (opcode << 8) | (* ptr++); + } + else return m3Err_wasmUnderrun; + } +#endif + * o_value = opcode; + * io_bytes = ptr; + + return m3Err_none; + } + else return m3Err_wasmUnderrun; +} + + +M3Result ReadLebUnsigned (u64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_wasmUnderrun; + + u64 value = 0; + + u32 shift = 0; + const u8 * ptr = * io_bytes; + + while (ptr < i_end) + { + u64 byte = * (ptr++); + + value |= ((byte & 0x7f) << shift); + shift += 7; + + if ((byte & 0x80) == 0) + { + result = m3Err_none; + break; + } + + if (shift >= i_maxNumBits) + { + result = m3Err_lebOverflow; + break; + } + } + + * o_value = value; + * io_bytes = ptr; + + return result; +} + + +M3Result ReadLebSigned (i64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_wasmUnderrun; + + i64 value = 0; + + u32 shift = 0; + const u8 * ptr = * io_bytes; + + while (ptr < i_end) + { + u64 byte = * (ptr++); + + value |= ((byte & 0x7f) << shift); + shift += 7; + + if ((byte & 0x80) == 0) + { + result = m3Err_none; + + if ((byte & 0x40) and (shift < 64)) // do sign extension + { + u64 extend = 0; + value |= (~extend << shift); + } + + break; + } + + if (shift >= i_maxNumBits) + { + result = m3Err_lebOverflow; + break; + } + } + + * o_value = value; + * io_bytes = ptr; + + return result; +} + + +M3Result ReadLEB_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end) +{ + u64 value; + M3Result result = ReadLebUnsigned (& value, 32, io_bytes, i_end); + * o_value = (u32) value; + + return result; +} + + +M3Result ReadLEB_u7 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end) +{ + u64 value; + M3Result result = ReadLebUnsigned (& value, 7, io_bytes, i_end); + * o_value = (u8) value; + + return result; +} + + +M3Result ReadLEB_i7 (i8 * o_value, bytes_t * io_bytes, cbytes_t i_end) +{ + i64 value; + M3Result result = ReadLebSigned (& value, 7, io_bytes, i_end); + * o_value = (i8) value; + + return result; +} + + +M3Result ReadLEB_i32 (i32 * o_value, bytes_t * io_bytes, cbytes_t i_end) +{ + i64 value; + M3Result result = ReadLebSigned (& value, 32, io_bytes, i_end); + * o_value = (i32) value; + + return result; +} + + +M3Result ReadLEB_i64 (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end) +{ + i64 value; + M3Result result = ReadLebSigned (& value, 64, io_bytes, i_end); + * o_value = value; + + return result; +} + + +M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end) +{ + *o_utf8 = NULL; + + u32 utf8Length; + M3Result result = ReadLEB_u32 (& utf8Length, io_bytes, i_end); + + if (not result) + { + if (utf8Length <= d_m3MaxSaneUtf8Length) + { + const u8 * ptr = * io_bytes; + const u8 * end = ptr + utf8Length; + + if (end <= i_end) + { + char * utf8 = (char *)m3_Malloc ("UTF8", utf8Length + 1); + + if (utf8) + { + memcpy (utf8, ptr, utf8Length); + utf8 [utf8Length] = 0; + * o_utf8 = utf8; + } + + * io_bytes = end; + } + else result = m3Err_wasmUnderrun; + } + else result = m3Err_missingUTF8; + } + + return result; +} + +#if d_m3RecordBacktraces +u32 FindModuleOffset (IM3Runtime i_runtime, pc_t i_pc) +{ + // walk the code pages + IM3CodePage curr = i_runtime->pagesOpen; + bool pageFound = false; + + while (curr) + { + if (ContainsPC (curr, i_pc)) + { + pageFound = true; + break; + } + curr = curr->info.next; + } + + if (!pageFound) + { + curr = i_runtime->pagesFull; + while (curr) + { + if (ContainsPC (curr, i_pc)) + { + pageFound = true; + break; + } + curr = curr->info.next; + } + } + + if (pageFound) + { + u32 result = 0; + + bool pcFound = MapPCToOffset (curr, i_pc, & result); + d_m3Assert (pcFound); + + return result; + } + else return 0; +} + + +void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc) +{ + // don't try to push any more frames if we've already had an alloc failure + if (M3_UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED)) + return; + + M3BacktraceFrame * newFrame = m3_AllocStruct(M3BacktraceFrame); + + if (!newFrame) + { + io_runtime->backtrace.lastFrame = M3_BACKTRACE_TRUNCATED; + return; + } + + newFrame->moduleOffset = FindModuleOffset (io_runtime, i_pc); + + if (!io_runtime->backtrace.frames || !io_runtime->backtrace.lastFrame) + io_runtime->backtrace.frames = newFrame; + else + io_runtime->backtrace.lastFrame->next = newFrame; + io_runtime->backtrace.lastFrame = newFrame; +} + + +void FillBacktraceFunctionInfo (IM3Runtime io_runtime, IM3Function i_function) +{ + // If we've had an alloc failure then the last frame doesn't refer to the + // frame we want to fill in the function info for. + if (M3_UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED)) + return; + + if (!io_runtime->backtrace.lastFrame) + return; + + io_runtime->backtrace.lastFrame->function = i_function; +} + + +void ClearBacktrace (IM3Runtime io_runtime) +{ + M3BacktraceFrame * currentFrame = io_runtime->backtrace.frames; + while (currentFrame) + { + M3BacktraceFrame * nextFrame = currentFrame->next; + m3_Free (currentFrame); + currentFrame = nextFrame; + } + + io_runtime->backtrace.frames = NULL; + io_runtime->backtrace.lastFrame = NULL; +} +#endif // d_m3RecordBacktraces diff --git a/cpp/m3_core.h b/cpp/m3_core.h new file mode 100644 index 0000000..c470c3b --- /dev/null +++ b/cpp/m3_core.h @@ -0,0 +1,311 @@ +// +// m3_core.h +// +// Created by Steven Massey on 4/15/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#ifndef m3_core_h +#define m3_core_h + +#include +#include +#include +#include +#include + +#include "wasm3.h" +#include "m3_config.h" + +# if defined(__cplusplus) +# define d_m3BeginExternC extern "C" { +# define d_m3EndExternC } +# else +# define d_m3BeginExternC +# define d_m3EndExternC +# endif + +d_m3BeginExternC + +#define d_m3ImplementFloat (d_m3HasFloat || d_m3NoFloatDynamic) + +#if !defined(d_m3ShortTypesDefined) + +typedef uint64_t u64; +typedef int64_t i64; +typedef uint32_t u32; +typedef int32_t i32; +typedef uint16_t u16; +typedef int16_t i16; +typedef uint8_t u8; +typedef int8_t i8; + +#if d_m3ImplementFloat +typedef double f64; +typedef float f32; +#endif + +#endif // d_m3ShortTypesDefined + +#define PRIf32 "f" +#define PRIf64 "lf" + +typedef const void * m3ret_t; +typedef const void * voidptr_t; +typedef const char * cstr_t; +typedef const char * const ccstr_t; +typedef const u8 * bytes_t; +typedef const u8 * const cbytes_t; + +typedef u16 m3opcode_t; + +typedef i64 m3reg_t; + +# if d_m3Use32BitSlots +typedef u32 m3slot_t; +# else +typedef u64 m3slot_t; +# endif + +typedef m3slot_t * m3stack_t; + +typedef +const void * const cvptr_t; + +# if defined (DEBUG) + +# define d_m3Log(CATEGORY, FMT, ...) printf (" %8s | " FMT, #CATEGORY, ##__VA_ARGS__); + +# if d_m3LogParse +# define m3log_parse(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) +# else +# define m3log_parse(...) {} +# endif + +# if d_m3LogCompile +# define m3log_compile(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) +# else +# define m3log_compile(...) {} +# endif + +# if d_m3LogEmit +# define m3log_emit(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) +# else +# define m3log_emit(...) {} +# endif + +# if d_m3LogCodePages +# define m3log_code(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) +# else +# define m3log_code(...) {} +# endif + +# if d_m3LogModule +# define m3log_module(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) +# else +# define m3log_module(...) {} +# endif + +# if d_m3LogRuntime +# define m3log_runtime(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) +# else +# define m3log_runtime(...) {} +# endif + +# define m3log(CATEGORY, FMT, ...) m3log_##CATEGORY (CATEGORY, FMT "\n", ##__VA_ARGS__) +# else +# define d_m3Log(CATEGORY, FMT, ...) {} +# define m3log(CATEGORY, FMT, ...) {} +# endif + + +# if defined(ASSERTS) || (defined(DEBUG) && !defined(NASSERTS)) +# define d_m3Assert(ASS) if (!(ASS)) { printf("Assertion failed at %s:%d : %s\n", __FILE__, __LINE__, #ASS); abort(); } +# else +# define d_m3Assert(ASS) +# endif + +typedef void /*const*/ * code_t; +typedef code_t const * /*__restrict__*/ pc_t; + + +typedef struct M3MemoryHeader +{ + IM3Runtime runtime; + void * maxStack; + size_t length; +} +M3MemoryHeader; + +struct M3CodeMappingPage; + +typedef struct M3CodePageHeader +{ + struct M3CodePage * next; + + u32 lineIndex; + u32 numLines; + u32 sequence; // this is just used for debugging; could be removed + u32 usageCount; + +# if d_m3RecordBacktraces + struct M3CodeMappingPage * mapping; +# endif // d_m3RecordBacktraces +} +M3CodePageHeader; + + +#define d_m3CodePageFreeLinesThreshold 4+2 // max is: select _sss & CallIndirect + 2 for bridge + +#define d_m3MemPageSize 65536 + +#define d_m3Reg0SlotAlias 60000 +#define d_m3Fp0SlotAlias (d_m3Reg0SlotAlias + 2) + +#define d_m3MaxSaneTypesCount 1000000 +#define d_m3MaxSaneFunctionsCount 1000000 +#define d_m3MaxSaneImportsCount 100000 +#define d_m3MaxSaneExportsCount 100000 +#define d_m3MaxSaneGlobalsCount 1000000 +#define d_m3MaxSaneElementSegments 10000000 +#define d_m3MaxSaneDataSegments 100000 +#define d_m3MaxSaneTableSize 10000000 +#define d_m3MaxSaneUtf8Length 10000 +#define d_m3MaxSaneFunctionArgRetCount 1000 // still insane, but whatever + +#define d_externalKind_function 0 +#define d_externalKind_table 1 +#define d_externalKind_memory 2 +#define d_externalKind_global 3 + +static const char * const c_waTypes [] = { "nil", "i32", "i64", "f32", "f64", "unknown" }; +static const char * const c_waCompactTypes [] = { "_", "i", "I", "f", "F", "?" }; + + +# if d_m3VerboseErrorMessages + +M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function, + const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...); + +# define _m3Error(RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ...) \ + m3Error (RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ##__VA_ARGS__) + +# else +# define _m3Error(RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ...) (RESULT) +# endif + +#define ErrorRuntime(RESULT, RUNTIME, FORMAT, ...) _m3Error (RESULT, RUNTIME, NULL, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__) +#define ErrorModule(RESULT, MOD, FORMAT, ...) _m3Error (RESULT, MOD->runtime, MOD, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__) +#define ErrorCompile(RESULT, COMP, FORMAT, ...) _m3Error (RESULT, COMP->runtime, COMP->module, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__) + +#if d_m3LogNativeStack +void m3StackCheckInit (); +void m3StackCheck (); +int m3StackGetMax (); +#else +#define m3StackCheckInit() +#define m3StackCheck() +#define m3StackGetMax() 0 +#endif + +#if d_m3LogTimestamps +#define PRIts "%llu" +uint64_t m3_GetTimestamp (); +#else +#define PRIts "%s" +#define m3_GetTimestamp() "" +#endif + +void m3_Abort (const char* message); +void * m3_Malloc_Impl (size_t i_size); +void * m3_Realloc_Impl (void * i_ptr, size_t i_newSize, size_t i_oldSize); +void m3_Free_Impl (void * i_ptr); +void * m3_CopyMem (const void * i_from, size_t i_size); + +#if d_m3LogHeapOps + +// Tracing format: timestamp;heap:OpCode;name;size(bytes);new items;new ptr;old items;old ptr + +static inline void * m3_AllocStruct_Impl(ccstr_t name, size_t i_size) { + void * result = m3_Malloc_Impl(i_size); + fprintf(stderr, PRIts ";heap:AllocStruct;%s;%zu;;%p;;\n", m3_GetTimestamp(), name, i_size, result); + return result; +} + +static inline void * m3_AllocArray_Impl(ccstr_t name, size_t i_num, size_t i_size) { + void * result = m3_Malloc_Impl(i_size * i_num); + fprintf(stderr, PRIts ";heap:AllocArr;%s;%zu;%zu;%p;;\n", m3_GetTimestamp(), name, i_size, i_num, result); + return result; +} + +static inline void * m3_ReallocArray_Impl(ccstr_t name, void * i_ptr_old, size_t i_num_new, size_t i_num_old, size_t i_size) { + void * result = m3_Realloc_Impl (i_ptr_old, i_size * i_num_new, i_size * i_num_old); + fprintf(stderr, PRIts ";heap:ReallocArr;%s;%zu;%zu;%p;%zu;%p\n", m3_GetTimestamp(), name, i_size, i_num_new, result, i_num_old, i_ptr_old); + return result; +} + +static inline void * m3_Malloc (ccstr_t name, size_t i_size) { + void * result = m3_Malloc_Impl (i_size); + fprintf(stderr, PRIts ";heap:AllocMem;%s;%zu;;%p;;\n", m3_GetTimestamp(), name, i_size, result); + return result; +} +static inline void * m3_Realloc (ccstr_t name, void * i_ptr, size_t i_newSize, size_t i_oldSize) { + void * result = m3_Realloc_Impl (i_ptr, i_newSize, i_oldSize); + fprintf(stderr, PRIts ";heap:ReallocMem;%s;;%zu;%p;%zu;%p\n", m3_GetTimestamp(), name, i_newSize, result, i_oldSize, i_ptr); + return result; +} + +#define m3_AllocStruct(STRUCT) (STRUCT *)m3_AllocStruct_Impl (#STRUCT, sizeof (STRUCT)) +#define m3_AllocArray(STRUCT, NUM) (STRUCT *)m3_AllocArray_Impl (#STRUCT, NUM, sizeof (STRUCT)) +#define m3_ReallocArray(STRUCT, PTR, NEW, OLD) (STRUCT *)m3_ReallocArray_Impl (#STRUCT, (void *)(PTR), (NEW), (OLD), sizeof (STRUCT)) +#define m3_Free(P) do { void* p = (void*)(P); \ + if (p) { fprintf(stderr, PRIts ";heap:FreeMem;;;;%p;\n", m3_GetTimestamp(), p); } \ + m3_Free_Impl (p); (P) = NULL; } while(0) +#else +#define m3_Malloc(NAME, SIZE) m3_Malloc_Impl(SIZE) +#define m3_Realloc(NAME, PTR, NEW, OLD) m3_Realloc_Impl(PTR, NEW, OLD) +#define m3_AllocStruct(STRUCT) (STRUCT *)m3_Malloc_Impl (sizeof (STRUCT)) +#define m3_AllocArray(STRUCT, NUM) (STRUCT *)m3_Malloc_Impl (sizeof (STRUCT) * (NUM)) +#define m3_ReallocArray(STRUCT, PTR, NEW, OLD) (STRUCT *)m3_Realloc_Impl ((void *)(PTR), sizeof (STRUCT) * (NEW), sizeof (STRUCT) * (OLD)) +#define m3_Free(P) do { m3_Free_Impl ((void*)(P)); (P) = NULL; } while(0) +#endif + +M3Result NormalizeType (u8 * o_type, i8 i_convolutedWasmType); + +bool IsIntType (u8 i_wasmType); +bool IsFpType (u8 i_wasmType); +bool Is64BitType (u8 i_m3Type); +u32 SizeOfType (u8 i_m3Type); + +M3Result Read_u64 (u64 * o_value, bytes_t * io_bytes, cbytes_t i_end); +M3Result Read_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end); +#if d_m3ImplementFloat +M3Result Read_f64 (f64 * o_value, bytes_t * io_bytes, cbytes_t i_end); +M3Result Read_f32 (f32 * o_value, bytes_t * io_bytes, cbytes_t i_end); +#endif +M3Result Read_u8 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end); +M3Result Read_opcode (m3opcode_t * o_value, bytes_t * io_bytes, cbytes_t i_end); + +M3Result ReadLebUnsigned (u64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end); +M3Result ReadLebSigned (i64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end); +M3Result ReadLEB_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end); +M3Result ReadLEB_u7 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end); +M3Result ReadLEB_i7 (i8 * o_value, bytes_t * io_bytes, cbytes_t i_end); +M3Result ReadLEB_i32 (i32 * o_value, bytes_t * io_bytes, cbytes_t i_end); +M3Result ReadLEB_i64 (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end); +M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end); + +cstr_t SPrintValue (void * i_value, u8 i_type); +size_t SPrintArg (char * o_string, size_t i_stringBufferSize, voidptr_t i_sp, u8 i_type); + +void ReportError (IM3Runtime io_runtime, IM3Module i_module, IM3Function i_function, ccstr_t i_errorMessage, ccstr_t i_file, u32 i_lineNum); + +# if d_m3RecordBacktraces +void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc); +void FillBacktraceFunctionInfo (IM3Runtime io_runtime, IM3Function i_function); +void ClearBacktrace (IM3Runtime io_runtime); +# endif + +d_m3EndExternC + +#endif // m3_core_h diff --git a/cpp/m3_env.c b/cpp/m3_env.c new file mode 100644 index 0000000..7ea9ece --- /dev/null +++ b/cpp/m3_env.c @@ -0,0 +1,1189 @@ +// +// m3_env.c +// +// Created by Steven Massey on 4/19/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#include +#include + +#include "m3_env.h" +#include "m3_compile.h" +#include "m3_exception.h" +#include "m3_info.h" + + +IM3Environment m3_NewEnvironment () +{ + IM3Environment env = m3_AllocStruct (M3Environment); + + if (env) + { + _try + { + // create FuncTypes for all simple block return ValueTypes + for (u8 t = c_m3Type_none; t <= c_m3Type_f64; t++) + { + IM3FuncType ftype; +_ (AllocFuncType (& ftype, 1)); + + ftype->numArgs = 0; + ftype->numRets = (t == c_m3Type_none) ? 0 : 1; + ftype->types [0] = t; + + Environment_AddFuncType (env, & ftype); + + d_m3Assert (t < 5); + env->retFuncTypes [t] = ftype; + } + } + + _catch: + if (result) + { + m3_FreeEnvironment (env); + env = NULL; + } + } + + return env; +} + + +void Environment_Release (IM3Environment i_environment) +{ + IM3FuncType ftype = i_environment->funcTypes; + + while (ftype) + { + IM3FuncType next = ftype->next; + m3_Free (ftype); + ftype = next; + } + + m3log (runtime, "freeing %d pages from environment", CountCodePages (i_environment->pagesReleased)); + FreeCodePages (& i_environment->pagesReleased); +} + + +void m3_FreeEnvironment (IM3Environment i_environment) +{ + if (i_environment) + { + Environment_Release (i_environment); + m3_Free (i_environment); + } +} + + +void m3_SetCustomSectionHandler (IM3Environment i_environment, M3SectionHandler i_handler) +{ + if (i_environment) i_environment->customSectionHandler = i_handler; +} + + +// returns the same io_funcType or replaces it with an equivalent that's already in the type linked list +void Environment_AddFuncType (IM3Environment i_environment, IM3FuncType * io_funcType) +{ + IM3FuncType addType = * io_funcType; + IM3FuncType newType = i_environment->funcTypes; + + while (newType) + { + if (AreFuncTypesEqual (newType, addType)) + { + m3_Free (addType); + break; + } + + newType = newType->next; + } + + if (newType == NULL) + { + newType = addType; + newType->next = i_environment->funcTypes; + i_environment->funcTypes = newType; + } + + * io_funcType = newType; +} + + +IM3CodePage RemoveCodePageOfCapacity (M3CodePage ** io_list, u32 i_minimumLineCount) +{ + IM3CodePage prev = NULL; + IM3CodePage page = * io_list; + + while (page) + { + if (NumFreeLines (page) >= i_minimumLineCount) + { d_m3Assert (page->info.usageCount == 0); + IM3CodePage next = page->info.next; + if (prev) + prev->info.next = next; // mid-list + else + * io_list = next; // front of list + + break; + } + + prev = page; + page = page->info.next; + } + + return page; +} + + +IM3CodePage Environment_AcquireCodePage (IM3Environment i_environment, u32 i_minimumLineCount) +{ + return RemoveCodePageOfCapacity (& i_environment->pagesReleased, i_minimumLineCount); +} + + +void Environment_ReleaseCodePages (IM3Environment i_environment, IM3CodePage i_codePageList) +{ + IM3CodePage end = i_codePageList; + + while (end) + { + end->info.lineIndex = 0; // reset page +#if d_m3RecordBacktraces + end->info.mapping->size = 0; +#endif // d_m3RecordBacktraces + + IM3CodePage next = end->info.next; + if (not next) + break; + + end = next; + } + + if (end) + { + // push list to front + end->info.next = i_environment->pagesReleased; + i_environment->pagesReleased = i_codePageList; + } +} + + +IM3Runtime m3_NewRuntime (IM3Environment i_environment, u32 i_stackSizeInBytes, void * i_userdata) +{ + IM3Runtime runtime = m3_AllocStruct (M3Runtime); + + if (runtime) + { + m3_ResetErrorInfo(runtime); + + runtime->environment = i_environment; + runtime->userdata = i_userdata; + + runtime->stack = m3_Malloc ("Wasm Stack", i_stackSizeInBytes + 4*sizeof (m3slot_t)); // TODO: more precise stack checks + + if (runtime->stack) + { + runtime->numStackSlots = i_stackSizeInBytes / sizeof (m3slot_t); m3log (runtime, "new stack: %p", runtime->stack); + } + else m3_Free (runtime); + } + + return runtime; +} + +void * m3_GetUserData (IM3Runtime i_runtime) +{ + return i_runtime ? i_runtime->userdata : NULL; +} + + +void * ForEachModule (IM3Runtime i_runtime, ModuleVisitor i_visitor, void * i_info) +{ + void * r = NULL; + + IM3Module module = i_runtime->modules; + + while (module) + { + IM3Module next = module->next; + r = i_visitor (module, i_info); + if (r) + break; + + module = next; + } + + return r; +} + + +void * _FreeModule (IM3Module i_module, void * i_info) +{ + m3_FreeModule (i_module); + return NULL; +} + + +void Runtime_Release (IM3Runtime i_runtime) +{ + ForEachModule (i_runtime, _FreeModule, NULL); d_m3Assert (i_runtime->numActiveCodePages == 0); + + Environment_ReleaseCodePages (i_runtime->environment, i_runtime->pagesOpen); + Environment_ReleaseCodePages (i_runtime->environment, i_runtime->pagesFull); + + m3_Free (i_runtime->stack); + m3_Free (i_runtime->memory.mallocated); +} + + +void m3_FreeRuntime (IM3Runtime i_runtime) +{ + if (i_runtime) + { + m3_PrintProfilerInfo (); + + Runtime_Release (i_runtime); + m3_Free (i_runtime); + } +} + +M3Result EvaluateExpression (IM3Module i_module, void * o_expressed, u8 i_type, bytes_t * io_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_none; + + // OPTZ: use a simplified interpreter for expressions + + // create a temporary runtime context +#if defined(d_m3PreferStaticAlloc) + static M3Runtime runtime; +#else + M3Runtime runtime; +#endif + M3_INIT (runtime); + + runtime.environment = i_module->runtime->environment; + runtime.numStackSlots = i_module->runtime->numStackSlots; + runtime.stack = i_module->runtime->stack; + + m3stack_t stack = (m3stack_t)runtime.stack; + + IM3Runtime savedRuntime = i_module->runtime; + i_module->runtime = & runtime; + + IM3Compilation o = & runtime.compilation; + o->runtime = & runtime; + o->module = i_module; + o->wasm = * io_bytes; + o->wasmEnd = i_end; + o->lastOpcodeStart = o->wasm; + + o->block.depth = -1; // so that root compilation depth = 0 + + // OPTZ: this code page could be erased after use. maybe have 'empty' list in addition to full and open? + o->page = AcquireCodePage (& runtime); // AcquireUnusedCodePage (...) + + if (o->page) + { + IM3FuncType ftype = runtime.environment->retFuncTypes[i_type]; + + pc_t m3code = GetPagePC (o->page); + result = CompileBlock (o, ftype, c_waOp_block); + + if (not result && o->maxStackSlots >= runtime.numStackSlots) { + result = m3Err_trapStackOverflow; + } + + if (not result) + { + m3ret_t r = RunCode (m3code, stack, NULL, d_m3OpDefaultArgs); + + if (r == 0) + { m3log (runtime, "expression result: %s", SPrintValue (stack, i_type)); + if (SizeOfType (i_type) == sizeof (u32)) + { + * (u32 *) o_expressed = * ((u32 *) stack); + } + else + { + * (u64 *) o_expressed = * ((u64 *) stack); + } + } + } + + // TODO: EraseCodePage (...) see OPTZ above + ReleaseCodePage (& runtime, o->page); + } + else result = m3Err_mallocFailedCodePage; + + runtime.stack = NULL; // prevent free(stack) in ReleaseRuntime + Runtime_Release (& runtime); + i_module->runtime = savedRuntime; + + * io_bytes = o->wasm; + + return result; +} + + +M3Result InitMemory (IM3Runtime io_runtime, IM3Module i_module) +{ + M3Result result = m3Err_none; //d_m3Assert (not io_runtime->memory.wasmPages); + + if (not i_module->memoryImported) + { + u32 maxPages = i_module->memoryInfo.maxPages; + io_runtime->memory.maxPages = maxPages ? maxPages : 65536; + + result = ResizeMemory (io_runtime, i_module->memoryInfo.initPages); + } + + return result; +} + + +M3Result ResizeMemory (IM3Runtime io_runtime, u32 i_numPages) +{ + M3Result result = m3Err_none; + + u32 numPagesToAlloc = i_numPages; + + M3Memory * memory = & io_runtime->memory; + +#if 0 // Temporary fix for memory allocation + if (memory->mallocated) { + memory->numPages = i_numPages; + memory->mallocated->end = memory->wasmPages + (memory->numPages * c_m3MemPageSize); + return result; + } + + i_numPagesToAlloc = 256; +#endif + + if (numPagesToAlloc <= memory->maxPages) + { + size_t numPageBytes = numPagesToAlloc * d_m3MemPageSize; + +#if d_m3MaxLinearMemoryPages > 0 + _throwif("linear memory limitation exceeded", numPagesToAlloc > d_m3MaxLinearMemoryPages); +#endif + + // Limit the amount of memory that gets actually allocated + if (io_runtime->memoryLimit) { + numPageBytes = M3_MIN (numPageBytes, io_runtime->memoryLimit); + } + + size_t numBytes = numPageBytes + sizeof (M3MemoryHeader); + + size_t numPreviousBytes = memory->numPages * d_m3MemPageSize; + if (numPreviousBytes) + numPreviousBytes += sizeof (M3MemoryHeader); + + void* newMem = m3_Realloc ("Wasm Linear Memory", memory->mallocated, numBytes, numPreviousBytes); + _throwifnull(newMem); + + memory->mallocated = (M3MemoryHeader*)newMem; + +# if d_m3LogRuntime + M3MemoryHeader * oldMallocated = memory->mallocated; +# endif + + memory->numPages = numPagesToAlloc; + + memory->mallocated->length = numPageBytes; + memory->mallocated->runtime = io_runtime; + + memory->mallocated->maxStack = (m3slot_t *) io_runtime->stack + io_runtime->numStackSlots; + + m3log (runtime, "resized old: %p; mem: %p; length: %zu; pages: %d", oldMallocated, memory->mallocated, memory->mallocated->length, memory->numPages); + } + else result = m3Err_wasmMemoryOverflow; + + _catch: return result; +} + + +M3Result InitGlobals (IM3Module io_module) +{ + M3Result result = m3Err_none; + + if (io_module->numGlobals) + { + // placing the globals in their structs isn't good for cache locality, but i don't really know what the global + // access patterns typically look like yet. + + // io_module->globalMemory = m3Alloc (m3reg_t, io_module->numGlobals); + + // if (io_module->globalMemory) + { + for (u32 i = 0; i < io_module->numGlobals; ++i) + { + M3Global * g = & io_module->globals [i]; m3log (runtime, "initializing global: %d", i); + + if (g->initExpr) + { + bytes_t start = g->initExpr; + + result = EvaluateExpression (io_module, & g->i64Value, g->type, & start, g->initExpr + g->initExprSize); + + if (not result) + { + // io_module->globalMemory [i] = initValue; + } + else break; + } + else + { m3log (runtime, "importing global"); + + } + } + } + // else result = ErrorModule (m3Err_mallocFailed, io_module, "could allocate globals for module: '%s", io_module->name); + } + + return result; +} + + +M3Result InitDataSegments (M3Memory * io_memory, IM3Module io_module) +{ + M3Result result = m3Err_none; + + _throwif ("unallocated linear memory", !(io_memory->mallocated)); + + for (u32 i = 0; i < io_module->numDataSegments; ++i) + { + M3DataSegment * segment = & io_module->dataSegments [i]; + + i32 segmentOffset; + bytes_t start = segment->initExpr; +_ (EvaluateExpression (io_module, & segmentOffset, c_m3Type_i32, & start, segment->initExpr + segment->initExprSize)); + + m3log (runtime, "loading data segment: %d; size: %d; offset: %d", i, segment->size, segmentOffset); + + if (segmentOffset >= 0 && (size_t)(segmentOffset) + segment->size <= io_memory->mallocated->length) + { + u8 * dest = m3MemData (io_memory->mallocated) + segmentOffset; + memcpy (dest, segment->data, segment->size); + } else { + _throw ("data segment out of bounds"); + } + } + + _catch: return result; +} + + +M3Result InitElements (IM3Module io_module) +{ + M3Result result = m3Err_none; + + bytes_t bytes = io_module->elementSection; + cbytes_t end = io_module->elementSectionEnd; + + for (u32 i = 0; i < io_module->numElementSegments; ++i) + { + u32 index; +_ (ReadLEB_u32 (& index, & bytes, end)); + + if (index == 0) + { + i32 offset; +_ (EvaluateExpression (io_module, & offset, c_m3Type_i32, & bytes, end)); + _throwif ("table underflow", offset < 0); + + u32 numElements; +_ (ReadLEB_u32 (& numElements, & bytes, end)); + + size_t endElement = (size_t) numElements + offset; + _throwif ("table overflow", endElement > d_m3MaxSaneTableSize); + + // is there any requirement that elements must be in increasing sequence? + // make sure the table isn't shrunk. + if (endElement > io_module->table0Size) + { + io_module->table0 = m3_ReallocArray (IM3Function, io_module->table0, endElement, io_module->table0Size); + io_module->table0Size = (u32) endElement; + } + _throwifnull(io_module->table0); + + for (u32 e = 0; e < numElements; ++e) + { + u32 functionIndex; +_ (ReadLEB_u32 (& functionIndex, & bytes, end)); + _throwif ("function index out of range", functionIndex >= io_module->numFunctions); + IM3Function function = & io_module->functions [functionIndex]; d_m3Assert (function); //printf ("table: %s\n", m3_GetFunctionName(function)); + io_module->table0 [e + offset] = function; + } + } + else _throw ("element table index must be zero for MVP"); + } + + _catch: return result; +} + +M3Result m3_CompileModule (IM3Module io_module) +{ + M3Result result = m3Err_none; + + for (u32 i = 0; i < io_module->numFunctions; ++i) + { + IM3Function f = & io_module->functions [i]; + if (f->wasm and not f->compiled) + { +_ (CompileFunction (f)); + } + } + + _catch: return result; +} + +M3Result m3_RunStart (IM3Module io_module) +{ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // Execution disabled for fuzzing builds + return m3Err_none; +#endif + + M3Result result = m3Err_none; + i32 startFunctionTmp = -1; + + if (io_module and io_module->startFunction >= 0) + { + IM3Function function = & io_module->functions [io_module->startFunction]; + + if (not function->compiled) + { +_ (CompileFunction (function)); + } + + IM3FuncType ftype = function->funcType; + if (ftype->numArgs != 0 || ftype->numRets != 0) + _throw (m3Err_argumentCountMismatch); + + IM3Module module = function->module; + IM3Runtime runtime = module->runtime; + + startFunctionTmp = io_module->startFunction; + io_module->startFunction = -1; + + result = (M3Result) RunCode (function->compiled, (m3stack_t) runtime->stack, runtime->memory.mallocated, d_m3OpDefaultArgs); + + if (result) + { + io_module->startFunction = startFunctionTmp; + EXCEPTION_PRINT(result); + goto _catch; + } + } + + _catch: return result; +} + +// TODO: deal with main + side-modules loading efforcement +M3Result m3_LoadModule (IM3Runtime io_runtime, IM3Module io_module) +{ + M3Result result = m3Err_none; + + if (M3_UNLIKELY(io_module->runtime)) { + return m3Err_moduleAlreadyLinked; + } + + io_module->runtime = io_runtime; + M3Memory * memory = & io_runtime->memory; + +_ (InitMemory (io_runtime, io_module)); +_ (InitGlobals (io_module)); +_ (InitDataSegments (memory, io_module)); +_ (InitElements (io_module)); + + // Start func might use imported functions, which are not liked here yet, + // so it will be called before a function call is attempted (in m3_FindFunction) + +#ifdef DEBUG + Module_GenerateNames(io_module); +#endif + + io_module->next = io_runtime->modules; + io_runtime->modules = io_module; + return result; // ok + +_catch: + io_module->runtime = NULL; + return result; +} + +IM3Global m3_FindGlobal (IM3Module io_module, + const char * const i_globalName) +{ + // Search exports + for (u32 i = 0; i < io_module->numGlobals; ++i) + { + IM3Global g = & io_module->globals [i]; + if (g->name and strcmp (g->name, i_globalName) == 0) + { + return g; + } + } + + // Search imports + for (u32 i = 0; i < io_module->numGlobals; ++i) + { + IM3Global g = & io_module->globals [i]; + + if (g->import.moduleUtf8 and g->import.fieldUtf8) + { + if (strcmp (g->import.fieldUtf8, i_globalName) == 0) + { + return g; + } + } + } + return NULL; +} + +M3Result m3_GetGlobal (IM3Global i_global, + IM3TaggedValue o_value) +{ + if (not i_global) return m3Err_globalLookupFailed; + + switch (i_global->type) { + case c_m3Type_i32: o_value->value.i32 = i_global->i32Value; break; + case c_m3Type_i64: o_value->value.i64 = i_global->i64Value; break; +# if d_m3HasFloat + case c_m3Type_f32: o_value->value.f32 = i_global->f32Value; break; + case c_m3Type_f64: o_value->value.f64 = i_global->f64Value; break; +# endif + default: return m3Err_invalidTypeId; + } + + o_value->type = (M3ValueType)(i_global->type); + return m3Err_none; +} + +M3Result m3_SetGlobal (IM3Global i_global, + const IM3TaggedValue i_value) +{ + if (not i_global) return m3Err_globalLookupFailed; + // TODO: if (not g->isMutable) return m3Err_globalNotMutable; + + if (i_global->type != i_value->type) return m3Err_globalTypeMismatch; + + switch (i_value->type) { + case c_m3Type_i32: i_global->i32Value = i_value->value.i32; break; + case c_m3Type_i64: i_global->i64Value = i_value->value.i64; break; +# if d_m3HasFloat + case c_m3Type_f32: i_global->f32Value = i_value->value.f32; break; + case c_m3Type_f64: i_global->f64Value = i_value->value.f64; break; +# endif + default: return m3Err_invalidTypeId; + } + + return m3Err_none; +} + +M3ValueType m3_GetGlobalType (IM3Global i_global) +{ + return (i_global) ? (M3ValueType)(i_global->type) : c_m3Type_none; +} + + +void * v_FindFunction (IM3Module i_module, const char * const i_name) +{ + + // Prefer exported functions + for (u32 i = 0; i < i_module->numFunctions; ++i) + { + IM3Function f = & i_module->functions [i]; + if (f->export_name and strcmp (f->export_name, i_name) == 0) + return f; + } + + // Search internal functions + for (u32 i = 0; i < i_module->numFunctions; ++i) + { + IM3Function f = & i_module->functions [i]; + + bool isImported = f->import.moduleUtf8 or f->import.fieldUtf8; + + if (isImported) + continue; + + for (int j = 0; j < f->numNames; j++) + { + if (f->names [j] and strcmp (f->names [j], i_name) == 0) + return f; + } + } + + return NULL; +} + +M3Result m3_FindFunction (IM3Function * o_function, IM3Runtime i_runtime, const char * const i_functionName) +{ + M3Result result = m3Err_none; d_m3Assert (o_function and i_runtime and i_functionName); + + IM3Function function = NULL; + + if (not i_runtime->modules) { + _throw ("no modules loaded"); + } + + function = (IM3Function) ForEachModule (i_runtime, (ModuleVisitor) v_FindFunction, (void *) i_functionName); + + if (function) + { + if (not function->compiled) + { +_ (CompileFunction (function)) + } + } + else _throw (ErrorModule (m3Err_functionLookupFailed, i_runtime->modules, "'%s'", i_functionName)); + + _catch: + if (result) + function = NULL; + + * o_function = function; + + return result; +} + +static +M3Result checkStartFunction(IM3Module i_module) +{ + M3Result result = m3Err_none; d_m3Assert(i_module); + + // Check if start function needs to be called + if (i_module->startFunction >= 0) + { + result = m3_RunStart (i_module); + } + + return result; +} + +uint32_t m3_GetArgCount (IM3Function i_function) +{ + if (i_function) { + IM3FuncType ft = i_function->funcType; + if (ft) { + return ft->numArgs; + } + } + return 0; +} + +uint32_t m3_GetRetCount (IM3Function i_function) +{ + if (i_function) { + IM3FuncType ft = i_function->funcType; + if (ft) { + return ft->numRets; + } + } + return 0; +} + + +M3ValueType m3_GetArgType (IM3Function i_function, uint32_t index) +{ + if (i_function) { + IM3FuncType ft = i_function->funcType; + if (ft and index < ft->numArgs) { + return (M3ValueType)d_FuncArgType(ft, index); + } + } + return c_m3Type_none; +} + +M3ValueType m3_GetRetType (IM3Function i_function, uint32_t index) +{ + if (i_function) { + IM3FuncType ft = i_function->funcType; + if (ft and index < ft->numRets) { + return (M3ValueType) d_FuncRetType (ft, index); + } + } + return c_m3Type_none; +} + + +u8 * GetStackPointerForArgs (IM3Function i_function) +{ + u64 * stack = (u64 *) i_function->module->runtime->stack; + IM3FuncType ftype = i_function->funcType; + + stack += ftype->numRets; + + return (u8 *) stack; +} + + +M3Result m3_CallV (IM3Function i_function, ...) +{ + va_list ap; + va_start(ap, i_function); + M3Result r = m3_CallVL(i_function, ap); + va_end(ap); + return r; +} + +static +void ReportNativeStackUsage () +{ +# if d_m3LogNativeStack + int stackUsed = m3StackGetMax(); + fprintf (stderr, "Native stack used: %d\n", stackUsed); +# endif +} + + +M3Result m3_CallVL (IM3Function i_function, va_list i_args) +{ + IM3Runtime runtime = i_function->module->runtime; + IM3FuncType ftype = i_function->funcType; + M3Result result = m3Err_none; + u8* s = NULL; + + if (!i_function->compiled) { + return m3Err_missingCompiledCode; + } + +# if d_m3RecordBacktraces + ClearBacktrace (runtime); +# endif + + m3StackCheckInit(); + +_ (checkStartFunction(i_function->module)) + + s = GetStackPointerForArgs (i_function); + + for (u32 i = 0; i < ftype->numArgs; ++i) + { + switch (d_FuncArgType(ftype, i)) { + case c_m3Type_i32: *(i32*)(s) = va_arg(i_args, i32); s += 8; break; + case c_m3Type_i64: *(i64*)(s) = va_arg(i_args, i64); s += 8; break; +# if d_m3HasFloat + case c_m3Type_f32: *(f32*)(s) = va_arg(i_args, f64); s += 8; break; // f32 is passed as f64 + case c_m3Type_f64: *(f64*)(s) = va_arg(i_args, f64); s += 8; break; +# endif + default: return "unknown argument type"; + } + } + + result = (M3Result) RunCode (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs); + ReportNativeStackUsage (); + + runtime->lastCalled = result ? NULL : i_function; + + _catch: return result; +} + +M3Result m3_Call (IM3Function i_function, uint32_t i_argc, const void * i_argptrs[]) +{ + IM3Runtime runtime = i_function->module->runtime; + IM3FuncType ftype = i_function->funcType; + M3Result result = m3Err_none; + u8* s = NULL; + + if (i_argc != ftype->numArgs) { + return m3Err_argumentCountMismatch; + } + if (!i_function->compiled) { + return m3Err_missingCompiledCode; + } + +# if d_m3RecordBacktraces + ClearBacktrace (runtime); +# endif + + m3StackCheckInit(); + +_ (checkStartFunction(i_function->module)) + + s = GetStackPointerForArgs (i_function); + + for (u32 i = 0; i < ftype->numArgs; ++i) + { + switch (d_FuncArgType(ftype, i)) { + case c_m3Type_i32: *(i32*)(s) = *(i32*)i_argptrs[i]; s += 8; break; + case c_m3Type_i64: *(i64*)(s) = *(i64*)i_argptrs[i]; s += 8; break; +# if d_m3HasFloat + case c_m3Type_f32: *(f32*)(s) = *(f32*)i_argptrs[i]; s += 8; break; + case c_m3Type_f64: *(f64*)(s) = *(f64*)i_argptrs[i]; s += 8; break; +# endif + default: return "unknown argument type"; + } + } + + result = (M3Result) RunCode (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs); + ReportNativeStackUsage (); + + runtime->lastCalled = result ? NULL : i_function; + + _catch: return result; +} + +M3Result m3_CallArgv (IM3Function i_function, uint32_t i_argc, const char * i_argv[]) +{ + IM3FuncType ftype = i_function->funcType; + IM3Runtime runtime = i_function->module->runtime; + M3Result result = m3Err_none; + u8* s = NULL; + + if (i_argc != ftype->numArgs) { + return m3Err_argumentCountMismatch; + } + if (!i_function->compiled) { + return m3Err_missingCompiledCode; + } + +# if d_m3RecordBacktraces + ClearBacktrace (runtime); +# endif + + m3StackCheckInit(); + +_ (checkStartFunction(i_function->module)) + + s = GetStackPointerForArgs (i_function); + + for (u32 i = 0; i < ftype->numArgs; ++i) + { + switch (d_FuncArgType(ftype, i)) { + case c_m3Type_i32: *(i32*)(s) = strtoul(i_argv[i], NULL, 10); s += 8; break; + case c_m3Type_i64: *(i64*)(s) = strtoull(i_argv[i], NULL, 10); s += 8; break; +# if d_m3HasFloat + case c_m3Type_f32: *(f32*)(s) = strtod(i_argv[i], NULL); s += 8; break; // strtof would be less portable + case c_m3Type_f64: *(f64*)(s) = strtod(i_argv[i], NULL); s += 8; break; +# endif + default: return "unknown argument type"; + } + } + + result = (M3Result) RunCode (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs); + ReportNativeStackUsage (); + + runtime->lastCalled = result ? NULL : i_function; + + _catch: return result; +} + + +//u8 * AlignStackPointerTo64Bits (const u8 * i_stack) +//{ +// uintptr_t ptr = (uintptr_t) i_stack; +// return (u8 *) ((ptr + 7) & ~7); +//} + + +M3Result m3_GetResults (IM3Function i_function, uint32_t i_retc, const void * o_retptrs[]) +{ + IM3FuncType ftype = i_function->funcType; + IM3Runtime runtime = i_function->module->runtime; + + if (i_retc != ftype->numRets) { + return m3Err_argumentCountMismatch; + } + if (i_function != runtime->lastCalled) { + return "function not called"; + } + + u8* s = (u8*) runtime->stack; + + for (u32 i = 0; i < ftype->numRets; ++i) + { + switch (d_FuncRetType(ftype, i)) { + case c_m3Type_i32: *(i32*)o_retptrs[i] = *(i32*)(s); s += 8; break; + case c_m3Type_i64: *(i64*)o_retptrs[i] = *(i64*)(s); s += 8; break; +# if d_m3HasFloat + case c_m3Type_f32: *(f32*)o_retptrs[i] = *(f32*)(s); s += 8; break; + case c_m3Type_f64: *(f64*)o_retptrs[i] = *(f64*)(s); s += 8; break; +# endif + default: return "unknown return type"; + } + } + return m3Err_none; +} + +M3Result m3_GetResultsV (IM3Function i_function, ...) +{ + va_list ap; + va_start(ap, i_function); + M3Result r = m3_GetResultsVL(i_function, ap); + va_end(ap); + return r; +} + +M3Result m3_GetResultsVL (IM3Function i_function, va_list o_rets) +{ + IM3Runtime runtime = i_function->module->runtime; + IM3FuncType ftype = i_function->funcType; + + if (i_function != runtime->lastCalled) { + return "function not called"; + } + + u8* s = (u8*) runtime->stack; + for (u32 i = 0; i < ftype->numRets; ++i) + { + switch (d_FuncRetType(ftype, i)) { + case c_m3Type_i32: *va_arg(o_rets, i32*) = *(i32*)(s); s += 8; break; + case c_m3Type_i64: *va_arg(o_rets, i64*) = *(i64*)(s); s += 8; break; +# if d_m3HasFloat + case c_m3Type_f32: *va_arg(o_rets, f32*) = *(f32*)(s); s += 8; break; + case c_m3Type_f64: *va_arg(o_rets, f64*) = *(f64*)(s); s += 8; break; +# endif + default: return "unknown argument type"; + } + } + return m3Err_none; +} + +void ReleaseCodePageNoTrack (IM3Runtime i_runtime, IM3CodePage i_codePage) +{ + if (i_codePage) + { + IM3CodePage * list; + + bool pageFull = (NumFreeLines (i_codePage) < d_m3CodePageFreeLinesThreshold); + if (pageFull) + list = & i_runtime->pagesFull; + else + list = & i_runtime->pagesOpen; + + PushCodePage (list, i_codePage); m3log (emit, "release page: %d to queue: '%s'", i_codePage->info.sequence, pageFull ? "full" : "open") + } +} + + +IM3CodePage AcquireCodePageWithCapacity (IM3Runtime i_runtime, u32 i_minLineCount) +{ + IM3CodePage page = RemoveCodePageOfCapacity (& i_runtime->pagesOpen, i_minLineCount); + + if (not page) + { + page = Environment_AcquireCodePage (i_runtime->environment, i_minLineCount); + + if (not page) + page = NewCodePage (i_runtime, i_minLineCount); + + if (page) + i_runtime->numCodePages++; + } + + if (page) + { m3log (emit, "acquire page: %d", page->info.sequence); + i_runtime->numActiveCodePages++; + } + + return page; +} + + +IM3CodePage AcquireCodePage (IM3Runtime i_runtime) +{ + return AcquireCodePageWithCapacity (i_runtime, d_m3CodePageFreeLinesThreshold); +} + + +void ReleaseCodePage (IM3Runtime i_runtime, IM3CodePage i_codePage) +{ + if (i_codePage) + { + ReleaseCodePageNoTrack (i_runtime, i_codePage); + i_runtime->numActiveCodePages--; + +# if defined (DEBUG) + u32 numOpen = CountCodePages (i_runtime->pagesOpen); + u32 numFull = CountCodePages (i_runtime->pagesFull); + + m3log (runtime, "runtime: %p; open-pages: %d; full-pages: %d; active: %d; total: %d", i_runtime, numOpen, numFull, i_runtime->numActiveCodePages, i_runtime->numCodePages); + + d_m3Assert (numOpen + numFull + i_runtime->numActiveCodePages == i_runtime->numCodePages); + +# if d_m3LogCodePages + dump_code_page (i_codePage, /* startPC: */ NULL); +# endif +# endif + } +} + + +#if d_m3VerboseErrorMessages +M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function, + const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...) +{ + if (i_runtime) + { + i_runtime->error = (M3ErrorInfo){ .result = i_result, .runtime = i_runtime, .module = i_module, + .function = i_function, .file = i_file, .line = i_lineNum }; + i_runtime->error.message = i_runtime->error_message; + + va_list args; + va_start (args, i_errorMessage); + vsnprintf (i_runtime->error_message, sizeof(i_runtime->error_message), i_errorMessage, args); + va_end (args); + } + + return i_result; +} +#endif + + +void m3_GetErrorInfo (IM3Runtime i_runtime, M3ErrorInfo* o_info) +{ + if (i_runtime) + { + *o_info = i_runtime->error; + m3_ResetErrorInfo (i_runtime); + } +} + + +void m3_ResetErrorInfo (IM3Runtime i_runtime) +{ + if (i_runtime) + { + M3_INIT(i_runtime->error); + i_runtime->error.message = ""; + } +} + +uint8_t * m3_GetMemory (IM3Runtime i_runtime, uint32_t * o_memorySizeInBytes, uint32_t i_memoryIndex) +{ + uint8_t * memory = NULL; d_m3Assert (i_memoryIndex == 0); + + if (i_runtime) + { + u32 size = (u32) i_runtime->memory.mallocated->length; + + if (o_memorySizeInBytes) + * o_memorySizeInBytes = size; + + if (size) + memory = m3MemData (i_runtime->memory.mallocated); + } + + return memory; +} + + +uint32_t m3_GetMemorySize (IM3Runtime i_runtime) +{ + return i_runtime->memory.mallocated->length; +} + + +M3BacktraceInfo * m3_GetBacktrace (IM3Runtime i_runtime) +{ +# if d_m3RecordBacktraces + return & i_runtime->backtrace; +# else + return NULL; +# endif +} + diff --git a/cpp/m3_env.h b/cpp/m3_env.h new file mode 100644 index 0000000..6183998 --- /dev/null +++ b/cpp/m3_env.h @@ -0,0 +1,213 @@ +// +// m3_env.h +// +// Created by Steven Massey on 4/19/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#ifndef m3_env_h +#define m3_env_h + +#include "wasm3.h" +#include "m3_code.h" +#include "m3_compile.h" + +d_m3BeginExternC + + +//--------------------------------------------------------------------------------------------------------------------------------- + +typedef struct M3MemoryInfo +{ + u32 initPages; + u32 maxPages; +} +M3MemoryInfo; + + +typedef struct M3Memory +{ + M3MemoryHeader * mallocated; + + u32 numPages; + u32 maxPages; +} +M3Memory; + +typedef M3Memory * IM3Memory; + + +//--------------------------------------------------------------------------------------------------------------------------------- + +typedef struct M3DataSegment +{ + const u8 * initExpr; // wasm code + const u8 * data; + + u32 initExprSize; + u32 memoryRegion; + u32 size; +} +M3DataSegment; + +//--------------------------------------------------------------------------------------------------------------------------------- + +typedef struct M3Global +{ + M3ImportInfo import; + + union + { + i32 i32Value; + i64 i64Value; +#if d_m3HasFloat + f64 f64Value; + f32 f32Value; +#endif + }; + + cstr_t name; + bytes_t initExpr; // wasm code + u32 initExprSize; + u8 type; + bool imported; + bool isMutable; +} +M3Global; + + +//--------------------------------------------------------------------------------------------------------------------------------- +typedef struct M3Module +{ + struct M3Runtime * runtime; + struct M3Environment * environment; + + bytes_t wasmStart; + bytes_t wasmEnd; + + cstr_t name; + + u32 numFuncTypes; + IM3FuncType * funcTypes; // array of pointers to list of FuncTypes + + u32 numFuncImports; + u32 numFunctions; + u32 allFunctions; // allocated functions count + M3Function * functions; + + i32 startFunction; + + u32 numDataSegments; + M3DataSegment * dataSegments; + + //u32 importedGlobals; + u32 numGlobals; + M3Global * globals; + + u32 numElementSegments; + bytes_t elementSection; + bytes_t elementSectionEnd; + + IM3Function * table0; + u32 table0Size; + + M3MemoryInfo memoryInfo; + bool memoryImported; + + //bool hasWasmCodeCopy; + + struct M3Module * next; +} +M3Module; + +M3Result Module_AddGlobal (IM3Module io_module, IM3Global * o_global, u8 i_type, bool i_mutable, bool i_isImported); + +M3Result Module_PreallocFunctions (IM3Module io_module, u32 i_totalFunctions); +M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo /* can be null */); +IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex); + +void Module_GenerateNames (IM3Module i_module); + +void FreeImportInfo (M3ImportInfo * i_info); + +//--------------------------------------------------------------------------------------------------------------------------------- + +typedef struct M3Environment +{ +// struct M3Runtime * runtimes; + + IM3FuncType funcTypes; // linked list of unique M3FuncType structs that can be compared using pointer-equivalence + + IM3FuncType retFuncTypes [c_m3Type_unknown]; // these 'point' to elements in the linked list above. + // the number of elements must match the basic types as per M3ValueType + M3CodePage * pagesReleased; + + M3SectionHandler customSectionHandler; +} +M3Environment; + +void Environment_Release (IM3Environment i_environment); + +// takes ownership of io_funcType and returns a pointer to the persistent version (could be same or different) +void Environment_AddFuncType (IM3Environment i_environment, IM3FuncType * io_funcType); + +//--------------------------------------------------------------------------------------------------------------------------------- + +typedef struct M3Runtime +{ + M3Compilation compilation; + + IM3Environment environment; + + M3CodePage * pagesOpen; // linked list of code pages with writable space on them + M3CodePage * pagesFull; // linked list of at-capacity pages + + u32 numCodePages; + u32 numActiveCodePages; + + IM3Module modules; // linked list of imported modules + + void * stack; + u32 stackSize; + u32 numStackSlots; + IM3Function lastCalled; // last function that successfully executed + + void * userdata; + + M3Memory memory; + u32 memoryLimit; + +#if d_m3EnableStrace >= 2 + u32 callDepth; +#endif + + M3ErrorInfo error; +#if d_m3VerboseErrorMessages + char error_message[256]; // the actual buffer. M3ErrorInfo can point to this +#endif + +#if d_m3RecordBacktraces + M3BacktraceInfo backtrace; +#endif + + u32 newCodePageSequence; +} +M3Runtime; + +void InitRuntime (IM3Runtime io_runtime, u32 i_stackSizeInBytes); +void Runtime_Release (IM3Runtime io_runtime); + +M3Result ResizeMemory (IM3Runtime io_runtime, u32 i_numPages); + +typedef void * (* ModuleVisitor) (IM3Module i_module, void * i_info); +void * ForEachModule (IM3Runtime i_runtime, ModuleVisitor i_visitor, void * i_info); + +void * v_FindFunction (IM3Module i_module, const char * const i_name); + +IM3CodePage AcquireCodePage (IM3Runtime io_runtime); +IM3CodePage AcquireCodePageWithCapacity (IM3Runtime io_runtime, u32 i_lineCount); +void ReleaseCodePage (IM3Runtime io_runtime, IM3CodePage i_codePage); + +d_m3EndExternC + +#endif // m3_env_h diff --git a/cpp/m3_exception.h b/cpp/m3_exception.h new file mode 100644 index 0000000..258f6f3 --- /dev/null +++ b/cpp/m3_exception.h @@ -0,0 +1,33 @@ +// +// m3_exception.h +// +// Created by Steven Massey on 7/5/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// +// some macros to emulate try/catch + +#ifndef m3_exception_h +#define m3_exception_h + +#include "m3_config.h" + +# if d_m3EnableExceptionBreakpoint + +// declared in m3_info.c +void ExceptionBreakpoint (cstr_t i_exception, cstr_t i_message); + +# define EXCEPTION_PRINT(ERROR) ExceptionBreakpoint (ERROR, (__FILE__ ":" M3_STR(__LINE__))) + +# else +# define EXCEPTION_PRINT(...) +# endif + + +#define _try M3Result result = m3Err_none; +#define _(TRY) { result = TRY; if (M3_UNLIKELY(result)) { EXCEPTION_PRINT (result); goto _catch; } } +#define _throw(ERROR) { result = ERROR; EXCEPTION_PRINT (result); goto _catch; } +#define _throwif(ERROR, COND) if (M3_UNLIKELY(COND)) { _throw(ERROR); } + +#define _throwifnull(PTR) _throwif (m3Err_mallocFailed, !(PTR)) + +#endif // m3_exception_h diff --git a/cpp/m3_exec.c b/cpp/m3_exec.c new file mode 100644 index 0000000..718e447 --- /dev/null +++ b/cpp/m3_exec.c @@ -0,0 +1,8 @@ +// +// m3_exec.c +// +// Created by Steven Massey on 4/17/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +// EMPTY FOR NOW diff --git a/cpp/m3_exec.h b/cpp/m3_exec.h new file mode 100644 index 0000000..47258eb --- /dev/null +++ b/cpp/m3_exec.h @@ -0,0 +1,1500 @@ +// +// m3_exec.h +// +// Created by Steven Massey on 4/17/19. +// Copyright © 2019 Steven Massey. All rights reserved. + + +#ifndef m3_exec_h +#define m3_exec_h + +// TODO: all these functions could move over to the .c at some point. normally, I'd say screw it, +// but it might prove useful to be able to compile m3_exec alone w/ optimizations while the remaining +// code is at debug O0 + + +// About the naming convention of these operations/macros (_rs, _sr_, _ss, _srs, etc.) +//------------------------------------------------------------------------------------------------------ +// - 'r' means register and 's' means slot +// - the first letter is the top of the stack +// +// so, for example, _rs means the first operand (the first thing pushed to the stack) is in a slot +// and the second operand (the top of the stack) is in a register +//------------------------------------------------------------------------------------------------------ + +#ifndef M3_COMPILE_OPCODES +# error "Opcodes should only be included in one compilation unit" +#endif + +#include "m3_math_utils.h" +#include "m3_compile.h" +#include "m3_env.h" +#include "m3_info.h" +#include "m3_exec_defs.h" + +#include + +d_m3BeginExternC + +# define rewrite_op(OP) * ((void **) (_pc-1)) = (void*)(OP) + +# define immediate(TYPE) * ((TYPE *) _pc++) +# define skip_immediate(TYPE) (_pc++) + +# define slot(TYPE) * (TYPE *) (_sp + immediate (i32)) +# define slot_ptr(TYPE) (TYPE *) (_sp + immediate (i32)) + + +# if d_m3EnableOpProfiling + d_m3RetSig profileOp (d_m3OpSig, cstr_t i_operationName); +# define nextOp() M3_MUSTTAIL return profileOp (d_m3OpAllArgs, __FUNCTION__) +# elif d_m3EnableOpTracing + d_m3RetSig debugOp (d_m3OpSig, cstr_t i_operationName); +# define nextOp() M3_MUSTTAIL return debugOp (d_m3OpAllArgs, __FUNCTION__) +# else +# define nextOp() nextOpDirect() +# endif + +#define jumpOp(PC) jumpOpDirect(PC) + +#if d_m3RecordBacktraces + #define pushBacktraceFrame() (PushBacktraceFrame (_mem->runtime, _pc - 1)) + #define fillBacktraceFrame(FUNCTION) (FillBacktraceFunctionInfo (_mem->runtime, function)) + + #define newTrap(err) return (pushBacktraceFrame (), err) + #define forwardTrap(err) return err +#else + #define pushBacktraceFrame() do {} while (0) + #define fillBacktraceFrame(FUNCTION) do {} while (0) + + #define newTrap(err) return err + #define forwardTrap(err) return err +#endif + + +#if d_m3EnableStrace == 1 + // Flat trace + #define d_m3TracePrepare + #define d_m3TracePrint(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) +#elif d_m3EnableStrace >= 2 + // Structured trace + #define d_m3TracePrepare const IM3Runtime trace_rt = m3MemRuntime(_mem); + #define d_m3TracePrint(fmt, ...) fprintf(stderr, "%*s" fmt "\n", (trace_rt->callDepth)*2, "", ##__VA_ARGS__) +#else + #define d_m3TracePrepare + #define d_m3TracePrint(fmt, ...) +#endif + +#if d_m3EnableStrace >= 3 + #define d_m3TraceLoad(TYPE,offset,val) d_m3TracePrint("load." #TYPE " 0x%x = %" PRI##TYPE, offset, val) + #define d_m3TraceStore(TYPE,offset,val) d_m3TracePrint("store." #TYPE " 0x%x , %" PRI##TYPE, offset, val) +#else + #define d_m3TraceLoad(TYPE,offset,val) + #define d_m3TraceStore(TYPE,offset,val) +#endif + +#ifdef DEBUG + #define d_outOfBounds newTrap (ErrorRuntime (m3Err_trapOutOfBoundsMemoryAccess, \ + _mem->runtime, "memory size: %zu; access offset: %zu", \ + _mem->length, operand)) + +# define d_outOfBoundsMemOp(OFFSET, SIZE) newTrap (ErrorRuntime (m3Err_trapOutOfBoundsMemoryAccess, \ + _mem->runtime, "memory size: %zu; access offset: %zu; size: %u", \ + _mem->length, OFFSET, SIZE)) +#else + #define d_outOfBounds newTrap (m3Err_trapOutOfBoundsMemoryAccess) + +# define d_outOfBoundsMemOp(OFFSET, SIZE) newTrap (m3Err_trapOutOfBoundsMemoryAccess) + +#endif + + +d_m3RetSig Call (d_m3OpSig) +{ + m3ret_t possible_trap = m3_Yield (); + if (M3_UNLIKELY(possible_trap)) return possible_trap; + + nextOpDirect(); +} + +// TODO: OK, this needs some explanation here ;0 + +#define d_m3CommutativeOpMacro(RES, REG, TYPE, NAME, OP, ...) \ +d_m3Op(TYPE##_##NAME##_rs) \ +{ \ + TYPE operand = slot (TYPE); \ + OP((RES), operand, ((TYPE) REG), ##__VA_ARGS__); \ + nextOp (); \ +} \ +d_m3Op(TYPE##_##NAME##_ss) \ +{ \ + TYPE operand2 = slot (TYPE); \ + TYPE operand1 = slot (TYPE); \ + OP((RES), operand1, operand2, ##__VA_ARGS__); \ + nextOp (); \ +} + +#define d_m3OpMacro(RES, REG, TYPE, NAME, OP, ...) \ +d_m3Op(TYPE##_##NAME##_sr) \ +{ \ + TYPE operand = slot (TYPE); \ + OP((RES), ((TYPE) REG), operand, ##__VA_ARGS__); \ + nextOp (); \ +} \ +d_m3CommutativeOpMacro(RES, REG, TYPE,NAME, OP, ##__VA_ARGS__) + +// Accept macros +#define d_m3CommutativeOpMacro_i(TYPE, NAME, MACRO, ...) d_m3CommutativeOpMacro ( _r0, _r0, TYPE, NAME, MACRO, ##__VA_ARGS__) +#define d_m3OpMacro_i(TYPE, NAME, MACRO, ...) d_m3OpMacro ( _r0, _r0, TYPE, NAME, MACRO, ##__VA_ARGS__) +#define d_m3CommutativeOpMacro_f(TYPE, NAME, MACRO, ...) d_m3CommutativeOpMacro (_fp0, _fp0, TYPE, NAME, MACRO, ##__VA_ARGS__) +#define d_m3OpMacro_f(TYPE, NAME, MACRO, ...) d_m3OpMacro (_fp0, _fp0, TYPE, NAME, MACRO, ##__VA_ARGS__) + +#define M3_FUNC(RES, A, B, OP) (RES) = OP((A), (B)) // Accept functions: res = OP(a,b) +#define M3_OPER(RES, A, B, OP) (RES) = ((A) OP (B)) // Accept operators: res = a OP b + +#define d_m3CommutativeOpFunc_i(TYPE, NAME, OP) d_m3CommutativeOpMacro_i (TYPE, NAME, M3_FUNC, OP) +#define d_m3OpFunc_i(TYPE, NAME, OP) d_m3OpMacro_i (TYPE, NAME, M3_FUNC, OP) +#define d_m3CommutativeOpFunc_f(TYPE, NAME, OP) d_m3CommutativeOpMacro_f (TYPE, NAME, M3_FUNC, OP) +#define d_m3OpFunc_f(TYPE, NAME, OP) d_m3OpMacro_f (TYPE, NAME, M3_FUNC, OP) + +#define d_m3CommutativeOp_i(TYPE, NAME, OP) d_m3CommutativeOpMacro_i (TYPE, NAME, M3_OPER, OP) +#define d_m3Op_i(TYPE, NAME, OP) d_m3OpMacro_i (TYPE, NAME, M3_OPER, OP) +#define d_m3CommutativeOp_f(TYPE, NAME, OP) d_m3CommutativeOpMacro_f (TYPE, NAME, M3_OPER, OP) +#define d_m3Op_f(TYPE, NAME, OP) d_m3OpMacro_f (TYPE, NAME, M3_OPER, OP) + +// compare needs to be distinct for fp 'cause the result must be _r0 +#define d_m3CompareOp_f(TYPE, NAME, OP) d_m3OpMacro (_r0, _fp0, TYPE, NAME, M3_OPER, OP) +#define d_m3CommutativeCmpOp_f(TYPE, NAME, OP) d_m3CommutativeOpMacro (_r0, _fp0, TYPE, NAME, M3_OPER, OP) + + +//----------------------- + +// signed +d_m3CommutativeOp_i (i32, Equal, ==) d_m3CommutativeOp_i (i64, Equal, ==) +d_m3CommutativeOp_i (i32, NotEqual, !=) d_m3CommutativeOp_i (i64, NotEqual, !=) + +d_m3Op_i (i32, LessThan, < ) d_m3Op_i (i64, LessThan, < ) +d_m3Op_i (i32, GreaterThan, > ) d_m3Op_i (i64, GreaterThan, > ) +d_m3Op_i (i32, LessThanOrEqual, <=) d_m3Op_i (i64, LessThanOrEqual, <=) +d_m3Op_i (i32, GreaterThanOrEqual, >=) d_m3Op_i (i64, GreaterThanOrEqual, >=) + +// unsigned +d_m3Op_i (u32, LessThan, < ) d_m3Op_i (u64, LessThan, < ) +d_m3Op_i (u32, GreaterThan, > ) d_m3Op_i (u64, GreaterThan, > ) +d_m3Op_i (u32, LessThanOrEqual, <=) d_m3Op_i (u64, LessThanOrEqual, <=) +d_m3Op_i (u32, GreaterThanOrEqual, >=) d_m3Op_i (u64, GreaterThanOrEqual, >=) + +#if d_m3HasFloat +d_m3CommutativeCmpOp_f (f32, Equal, ==) d_m3CommutativeCmpOp_f (f64, Equal, ==) +d_m3CommutativeCmpOp_f (f32, NotEqual, !=) d_m3CommutativeCmpOp_f (f64, NotEqual, !=) +d_m3CompareOp_f (f32, LessThan, < ) d_m3CompareOp_f (f64, LessThan, < ) +d_m3CompareOp_f (f32, GreaterThan, > ) d_m3CompareOp_f (f64, GreaterThan, > ) +d_m3CompareOp_f (f32, LessThanOrEqual, <=) d_m3CompareOp_f (f64, LessThanOrEqual, <=) +d_m3CompareOp_f (f32, GreaterThanOrEqual, >=) d_m3CompareOp_f (f64, GreaterThanOrEqual, >=) +#endif + +d_m3CommutativeOp_i (i32, Add, +) d_m3CommutativeOp_i (i64, Add, +) +d_m3CommutativeOp_i (i32, Multiply, *) d_m3CommutativeOp_i (i64, Multiply, *) + +d_m3Op_i (i32, Subtract, -) d_m3Op_i (i64, Subtract, -) + +#define OP_SHL_32(X,N) ((X) << ((u32)(N) % 32)) +#define OP_SHL_64(X,N) ((X) << ((u64)(N) % 64)) +#define OP_SHR_32(X,N) ((X) >> ((u32)(N) % 32)) +#define OP_SHR_64(X,N) ((X) >> ((u64)(N) % 64)) + +d_m3OpFunc_i (u32, ShiftLeft, OP_SHL_32) d_m3OpFunc_i (u64, ShiftLeft, OP_SHL_64) +d_m3OpFunc_i (i32, ShiftRight, OP_SHR_32) d_m3OpFunc_i (i64, ShiftRight, OP_SHR_64) +d_m3OpFunc_i (u32, ShiftRight, OP_SHR_32) d_m3OpFunc_i (u64, ShiftRight, OP_SHR_64) + +d_m3CommutativeOp_i (u32, And, &) +d_m3CommutativeOp_i (u32, Or, |) +d_m3CommutativeOp_i (u32, Xor, ^) + +d_m3CommutativeOp_i (u64, And, &) +d_m3CommutativeOp_i (u64, Or, |) +d_m3CommutativeOp_i (u64, Xor, ^) + +#if d_m3HasFloat +d_m3CommutativeOp_f (f32, Add, +) d_m3CommutativeOp_f (f64, Add, +) +d_m3CommutativeOp_f (f32, Multiply, *) d_m3CommutativeOp_f (f64, Multiply, *) +d_m3Op_f (f32, Subtract, -) d_m3Op_f (f64, Subtract, -) +d_m3Op_f (f32, Divide, /) d_m3Op_f (f64, Divide, /) +#endif + +d_m3OpFunc_i(u32, Rotl, rotl32) +d_m3OpFunc_i(u32, Rotr, rotr32) +d_m3OpFunc_i(u64, Rotl, rotl64) +d_m3OpFunc_i(u64, Rotr, rotr64) + +d_m3OpMacro_i(u32, Divide, OP_DIV_U); +d_m3OpMacro_i(i32, Divide, OP_DIV_S, INT32_MIN); +d_m3OpMacro_i(u64, Divide, OP_DIV_U); +d_m3OpMacro_i(i64, Divide, OP_DIV_S, INT64_MIN); + +d_m3OpMacro_i(u32, Remainder, OP_REM_U); +d_m3OpMacro_i(i32, Remainder, OP_REM_S, INT32_MIN); +d_m3OpMacro_i(u64, Remainder, OP_REM_U); +d_m3OpMacro_i(i64, Remainder, OP_REM_S, INT64_MIN); + +#if d_m3HasFloat +d_m3OpFunc_f(f32, Min, min_f32); +d_m3OpFunc_f(f32, Max, max_f32); +d_m3OpFunc_f(f64, Min, min_f64); +d_m3OpFunc_f(f64, Max, max_f64); + +d_m3OpFunc_f(f32, CopySign, copysignf); +d_m3OpFunc_f(f64, CopySign, copysign); +#endif + +// Unary operations +// Note: This macro follows the principle of d_m3OpMacro + +#define d_m3UnaryMacro(RES, REG, TYPE, NAME, OP, ...) \ +d_m3Op(TYPE##_##NAME##_r) \ +{ \ + OP((RES), (TYPE) REG, ##__VA_ARGS__); \ + nextOp (); \ +} \ +d_m3Op(TYPE##_##NAME##_s) \ +{ \ + TYPE operand = slot (TYPE); \ + OP((RES), operand, ##__VA_ARGS__); \ + nextOp (); \ +} + +#define M3_UNARY(RES, X, OP) (RES) = OP(X) +#define d_m3UnaryOp_i(TYPE, NAME, OPERATION) d_m3UnaryMacro( _r0, _r0, TYPE, NAME, M3_UNARY, OPERATION) +#define d_m3UnaryOp_f(TYPE, NAME, OPERATION) d_m3UnaryMacro(_fp0, _fp0, TYPE, NAME, M3_UNARY, OPERATION) + +#if d_m3HasFloat +d_m3UnaryOp_f (f32, Abs, fabsf); d_m3UnaryOp_f (f64, Abs, fabs); +d_m3UnaryOp_f (f32, Ceil, ceilf); d_m3UnaryOp_f (f64, Ceil, ceil); +d_m3UnaryOp_f (f32, Floor, floorf); d_m3UnaryOp_f (f64, Floor, floor); +d_m3UnaryOp_f (f32, Trunc, truncf); d_m3UnaryOp_f (f64, Trunc, trunc); +d_m3UnaryOp_f (f32, Sqrt, sqrtf); d_m3UnaryOp_f (f64, Sqrt, sqrt); +d_m3UnaryOp_f (f32, Nearest, rintf); d_m3UnaryOp_f (f64, Nearest, rint); +d_m3UnaryOp_f (f32, Negate, -); d_m3UnaryOp_f (f64, Negate, -); +#endif + +#define OP_EQZ(x) ((x) == 0) + +d_m3UnaryOp_i (i32, EqualToZero, OP_EQZ) +d_m3UnaryOp_i (i64, EqualToZero, OP_EQZ) + +// clz(0), ctz(0) results are undefined for rest platforms, fix it +#if (defined(__i386__) || defined(__x86_64__)) && !(defined(__AVX2__) || (defined(__ABM__) && defined(__BMI__))) + #define OP_CLZ_32(x) (M3_UNLIKELY((x) == 0) ? 32 : __builtin_clz(x)) + #define OP_CTZ_32(x) (M3_UNLIKELY((x) == 0) ? 32 : __builtin_ctz(x)) + // for 64-bit instructions branchless approach more preferable + #define OP_CLZ_64(x) (__builtin_clzll((x) | (1LL << 0)) + OP_EQZ(x)) + #define OP_CTZ_64(x) (__builtin_ctzll((x) | (1LL << 63)) + OP_EQZ(x)) +#elif defined(__ppc__) || defined(__ppc64__) +// PowerPC is defined for __builtin_clz(0) and __builtin_ctz(0). +// See (https://github.com/aquynh/capstone/blob/master/MathExtras.h#L99) + #define OP_CLZ_32(x) __builtin_clz(x) + #define OP_CTZ_32(x) __builtin_ctz(x) + #define OP_CLZ_64(x) __builtin_clzll(x) + #define OP_CTZ_64(x) __builtin_ctzll(x) +#else + #define OP_CLZ_32(x) (M3_UNLIKELY((x) == 0) ? 32 : __builtin_clz(x)) + #define OP_CTZ_32(x) (M3_UNLIKELY((x) == 0) ? 32 : __builtin_ctz(x)) + #define OP_CLZ_64(x) (M3_UNLIKELY((x) == 0) ? 64 : __builtin_clzll(x)) + #define OP_CTZ_64(x) (M3_UNLIKELY((x) == 0) ? 64 : __builtin_ctzll(x)) +#endif + +d_m3UnaryOp_i (u32, Clz, OP_CLZ_32) +d_m3UnaryOp_i (u64, Clz, OP_CLZ_64) + +d_m3UnaryOp_i (u32, Ctz, OP_CTZ_32) +d_m3UnaryOp_i (u64, Ctz, OP_CTZ_64) + +d_m3UnaryOp_i (u32, Popcnt, __builtin_popcount) +d_m3UnaryOp_i (u64, Popcnt, __builtin_popcountll) + +#define OP_WRAP_I64(X) ((X) & 0x00000000ffffffff) + +d_m3Op(i32_Wrap_i64_r) +{ + _r0 = OP_WRAP_I64((i64) _r0); + nextOp (); +} + +d_m3Op(i32_Wrap_i64_s) +{ + i64 operand = slot (i64); + _r0 = OP_WRAP_I64(operand); + nextOp (); +} + +// Integer sign extension operations +#define OP_EXTEND8_S_I32(X) ((int32_t)(int8_t)(X)) +#define OP_EXTEND16_S_I32(X) ((int32_t)(int16_t)(X)) +#define OP_EXTEND8_S_I64(X) ((int64_t)(int8_t)(X)) +#define OP_EXTEND16_S_I64(X) ((int64_t)(int16_t)(X)) +#define OP_EXTEND32_S_I64(X) ((int64_t)(int32_t)(X)) + +d_m3UnaryOp_i (i32, Extend8_s, OP_EXTEND8_S_I32) +d_m3UnaryOp_i (i32, Extend16_s, OP_EXTEND16_S_I32) +d_m3UnaryOp_i (i64, Extend8_s, OP_EXTEND8_S_I64) +d_m3UnaryOp_i (i64, Extend16_s, OP_EXTEND16_S_I64) +d_m3UnaryOp_i (i64, Extend32_s, OP_EXTEND32_S_I64) + +#define d_m3TruncMacro(DEST, SRC, TYPE, NAME, FROM, OP, ...) \ +d_m3Op(TYPE##_##NAME##_##FROM##_r_r) \ +{ \ + OP((DEST), (FROM) SRC, ##__VA_ARGS__); \ + nextOp (); \ +} \ +d_m3Op(TYPE##_##NAME##_##FROM##_r_s) \ +{ \ + FROM * stack = slot_ptr (FROM); \ + OP((DEST), (* stack), ##__VA_ARGS__); \ + nextOp (); \ +} \ +d_m3Op(TYPE##_##NAME##_##FROM##_s_r) \ +{ \ + TYPE * dest = slot_ptr (TYPE); \ + OP((* dest), (FROM) SRC, ##__VA_ARGS__); \ + nextOp (); \ +} \ +d_m3Op(TYPE##_##NAME##_##FROM##_s_s) \ +{ \ + FROM * stack = slot_ptr (FROM); \ + TYPE * dest = slot_ptr (TYPE); \ + OP((* dest), (* stack), ##__VA_ARGS__); \ + nextOp (); \ +} + +#if d_m3HasFloat +d_m3TruncMacro(_r0, _fp0, i32, Trunc, f32, OP_I32_TRUNC_F32) +d_m3TruncMacro(_r0, _fp0, u32, Trunc, f32, OP_U32_TRUNC_F32) +d_m3TruncMacro(_r0, _fp0, i32, Trunc, f64, OP_I32_TRUNC_F64) +d_m3TruncMacro(_r0, _fp0, u32, Trunc, f64, OP_U32_TRUNC_F64) + +d_m3TruncMacro(_r0, _fp0, i64, Trunc, f32, OP_I64_TRUNC_F32) +d_m3TruncMacro(_r0, _fp0, u64, Trunc, f32, OP_U64_TRUNC_F32) +d_m3TruncMacro(_r0, _fp0, i64, Trunc, f64, OP_I64_TRUNC_F64) +d_m3TruncMacro(_r0, _fp0, u64, Trunc, f64, OP_U64_TRUNC_F64) + +d_m3TruncMacro(_r0, _fp0, i32, TruncSat, f32, OP_I32_TRUNC_SAT_F32) +d_m3TruncMacro(_r0, _fp0, u32, TruncSat, f32, OP_U32_TRUNC_SAT_F32) +d_m3TruncMacro(_r0, _fp0, i32, TruncSat, f64, OP_I32_TRUNC_SAT_F64) +d_m3TruncMacro(_r0, _fp0, u32, TruncSat, f64, OP_U32_TRUNC_SAT_F64) + +d_m3TruncMacro(_r0, _fp0, i64, TruncSat, f32, OP_I64_TRUNC_SAT_F32) +d_m3TruncMacro(_r0, _fp0, u64, TruncSat, f32, OP_U64_TRUNC_SAT_F32) +d_m3TruncMacro(_r0, _fp0, i64, TruncSat, f64, OP_I64_TRUNC_SAT_F64) +d_m3TruncMacro(_r0, _fp0, u64, TruncSat, f64, OP_U64_TRUNC_SAT_F64) +#endif + +#define d_m3TypeModifyOp(REG_TO, REG_FROM, TO, NAME, FROM) \ +d_m3Op(TO##_##NAME##_##FROM##_r) \ +{ \ + REG_TO = (TO) ((FROM) REG_FROM); \ + nextOp (); \ +} \ + \ +d_m3Op(TO##_##NAME##_##FROM##_s) \ +{ \ + FROM from = slot (FROM); \ + REG_TO = (TO) (from); \ + nextOp (); \ +} + +// Int to int +d_m3TypeModifyOp (_r0, _r0, i64, Extend, i32); +d_m3TypeModifyOp (_r0, _r0, i64, Extend, u32); + +// Float to float +#if d_m3HasFloat +d_m3TypeModifyOp (_fp0, _fp0, f32, Demote, f64); +d_m3TypeModifyOp (_fp0, _fp0, f64, Promote, f32); +#endif + +#define d_m3TypeConvertOp(REG_TO, REG_FROM, TO, NAME, FROM) \ +d_m3Op(TO##_##NAME##_##FROM##_r_r) \ +{ \ + REG_TO = (TO) ((FROM) REG_FROM); \ + nextOp (); \ +} \ + \ +d_m3Op(TO##_##NAME##_##FROM##_s_r) \ +{ \ + slot (TO) = (TO) ((FROM) REG_FROM); \ + nextOp (); \ +} \ + \ +d_m3Op(TO##_##NAME##_##FROM##_r_s) \ +{ \ + FROM from = slot (FROM); \ + REG_TO = (TO) (from); \ + nextOp (); \ +} \ + \ +d_m3Op(TO##_##NAME##_##FROM##_s_s) \ +{ \ + FROM from = slot (FROM); \ + slot (TO) = (TO) (from); \ + nextOp (); \ +} + +// Int to float +#if d_m3HasFloat +d_m3TypeConvertOp (_fp0, _r0, f64, Convert, i32); +d_m3TypeConvertOp (_fp0, _r0, f64, Convert, u32); +d_m3TypeConvertOp (_fp0, _r0, f64, Convert, i64); +d_m3TypeConvertOp (_fp0, _r0, f64, Convert, u64); + +d_m3TypeConvertOp (_fp0, _r0, f32, Convert, i32); +d_m3TypeConvertOp (_fp0, _r0, f32, Convert, u32); +d_m3TypeConvertOp (_fp0, _r0, f32, Convert, i64); +d_m3TypeConvertOp (_fp0, _r0, f32, Convert, u64); +#endif + +#define d_m3ReinterpretOp(REG, TO, SRC, FROM) \ +d_m3Op(TO##_Reinterpret_##FROM##_r_r) \ +{ \ + union { FROM c; TO t; } u; \ + u.c = (FROM) SRC; \ + REG = u.t; \ + nextOp (); \ +} \ + \ +d_m3Op(TO##_Reinterpret_##FROM##_r_s) \ +{ \ + union { FROM c; TO t; } u; \ + u.c = slot (FROM); \ + REG = u.t; \ + nextOp (); \ +} \ + \ +d_m3Op(TO##_Reinterpret_##FROM##_s_r) \ +{ \ + union { FROM c; TO t; } u; \ + u.c = (FROM) SRC; \ + slot (TO) = u.t; \ + nextOp (); \ +} \ + \ +d_m3Op(TO##_Reinterpret_##FROM##_s_s) \ +{ \ + union { FROM c; TO t; } u; \ + u.c = slot (FROM); \ + slot (TO) = u.t; \ + nextOp (); \ +} + +#if d_m3HasFloat +d_m3ReinterpretOp (_r0, i32, _fp0, f32) +d_m3ReinterpretOp (_r0, i64, _fp0, f64) +d_m3ReinterpretOp (_fp0, f32, _r0, i32) +d_m3ReinterpretOp (_fp0, f64, _r0, i64) +#endif + + +d_m3Op (GetGlobal_s32) +{ + u32 * global = immediate (u32 *); + slot (u32) = * global; // printf ("get global: %p %" PRIi64 "\n", global, *global); + + nextOp (); +} + + +d_m3Op (GetGlobal_s64) +{ + u64 * global = immediate (u64 *); + slot (u64) = * global; // printf ("get global: %p %" PRIi64 "\n", global, *global); + + nextOp (); +} + + +d_m3Op (SetGlobal_i32) +{ + u32 * global = immediate (u32 *); + * global = (u32) _r0; // printf ("set global: %p %" PRIi64 "\n", global, _r0); + + nextOp (); +} + + +d_m3Op (SetGlobal_i64) +{ + u64 * global = immediate (u64 *); + * global = (u64) _r0; // printf ("set global: %p %" PRIi64 "\n", global, _r0); + + nextOp (); +} + + +d_m3Op (Call) +{ + pc_t callPC = immediate (pc_t); + i32 stackOffset = immediate (i32); + IM3Memory memory = m3MemInfo (_mem); + + m3stack_t sp = _sp + stackOffset; + + m3ret_t r = Call (callPC, sp, _mem, d_m3OpDefaultArgs); + _mem = memory->mallocated; + + if (M3_LIKELY(not r)) + nextOp (); + else + { + pushBacktraceFrame (); + forwardTrap (r); + } +} + + +d_m3Op (CallIndirect) +{ + u32 tableIndex = slot (u32); + IM3Module module = immediate (IM3Module); + IM3FuncType type = immediate (IM3FuncType); + i32 stackOffset = immediate (i32); + IM3Memory memory = m3MemInfo (_mem); + + m3stack_t sp = _sp + stackOffset; + + m3ret_t r = m3Err_none; + + if (M3_LIKELY(tableIndex < module->table0Size)) + { + IM3Function function = module->table0 [tableIndex]; + + if (M3_LIKELY(function)) + { + if (M3_LIKELY(type == function->funcType)) + { + if (M3_UNLIKELY(not function->compiled)) + r = CompileFunction (function); + + if (M3_LIKELY(not r)) + { + r = Call (function->compiled, sp, _mem, d_m3OpDefaultArgs); + _mem = memory->mallocated; + + if (M3_LIKELY(not r)) + nextOpDirect (); + else + { + pushBacktraceFrame (); + forwardTrap (r); + } + } + } + else r = m3Err_trapIndirectCallTypeMismatch; + } + else r = m3Err_trapTableElementIsNull; + } + else r = m3Err_trapTableIndexOutOfRange; + + if (M3_UNLIKELY(r)) + newTrap (r); + else forwardTrap (r); +} + + +d_m3Op (CallRawFunction) +{ + d_m3TracePrepare + + M3ImportContext ctx; + + M3RawCall call = (M3RawCall) (* _pc++); + ctx.function = immediate (IM3Function); + ctx.userdata = immediate (void *); + u64* const sp = ((u64*)_sp); + IM3Memory memory = m3MemInfo (_mem); + + IM3Runtime runtime = m3MemRuntime(_mem); + +#if d_m3EnableStrace + IM3FuncType ftype = ctx.function->funcType; + + FILE* out = stderr; + char outbuff[1024]; + char* outp = outbuff; + char* oute = outbuff+1024; + + outp += snprintf(outp, oute-outp, "%s!%s(", ctx.function->import.moduleUtf8, ctx.function->import.fieldUtf8); + + const int nArgs = ftype->numArgs; + const int nRets = ftype->numRets; + u64 * args = sp + nRets; + for (int i=0; itypes[nRets + i]; + switch (type) { + case c_m3Type_i32: outp += snprintf(outp, oute-outp, "%" PRIi32, *(i32*)(args+i)); break; + case c_m3Type_i64: outp += snprintf(outp, oute-outp, "%" PRIi64, *(i64*)(args+i)); break; + case c_m3Type_f32: outp += snprintf(outp, oute-outp, "%" PRIf32, *(f32*)(args+i)); break; + case c_m3Type_f64: outp += snprintf(outp, oute-outp, "%" PRIf64, *(f64*)(args+i)); break; + default: outp += snprintf(outp, oute-outp, "", type); break; + } + outp += snprintf(outp, oute-outp, (i < nArgs-1) ? ", " : ")"); + } +# if d_m3EnableStrace >= 2 + outp += snprintf(outp, oute-outp, " { }"); +# endif +#endif + + // m3_Call uses runtime->stack to set-up initial exported function stack. + // Reconfigure the stack to enable recursive invocations of m3_Call. + // I.e. exported/table function can be called from an impoted function. + void* stack_backup = runtime->stack; + runtime->stack = sp; + m3ret_t possible_trap = call (runtime, &ctx, sp, m3MemData(_mem)); + runtime->stack = stack_backup; + +#if d_m3EnableStrace + if (M3_UNLIKELY(possible_trap)) { + d_m3TracePrint("%s -> %s", outbuff, (char*)possible_trap); + } else { + switch (GetSingleRetType(ftype)) { + case c_m3Type_none: d_m3TracePrint("%s", outbuff); break; + case c_m3Type_i32: d_m3TracePrint("%s = %" PRIi32, outbuff, *(i32*)sp); break; + case c_m3Type_i64: d_m3TracePrint("%s = %" PRIi64, outbuff, *(i64*)sp); break; + case c_m3Type_f32: d_m3TracePrint("%s = %" PRIf32, outbuff, *(f32*)sp); break; + case c_m3Type_f64: d_m3TracePrint("%s = %" PRIf64, outbuff, *(f64*)sp); break; + } + } +#endif + + if (M3_UNLIKELY(possible_trap)) { + _mem = memory->mallocated; + pushBacktraceFrame (); + } + forwardTrap (possible_trap); +} + + +d_m3Op (MemSize) +{ + IM3Memory memory = m3MemInfo (_mem); + + _r0 = memory->numPages; + + nextOp (); +} + + +d_m3Op (MemGrow) +{ + IM3Runtime runtime = m3MemRuntime(_mem); + IM3Memory memory = & runtime->memory; + + u32 numPagesToGrow = (u32) _r0; + _r0 = memory->numPages; + + if (M3_LIKELY(numPagesToGrow)) + { + u32 requiredPages = memory->numPages + numPagesToGrow; + + M3Result r = ResizeMemory (runtime, requiredPages); + if (r) + _r0 = -1; + + _mem = memory->mallocated; + } + + nextOp (); +} + + +d_m3Op (MemCopy) +{ + u32 size = (u32) _r0; + u64 source = slot (u32); + u64 destination = slot (u32); + + if (M3_LIKELY(destination + size <= _mem->length)) + { + if (M3_LIKELY(source + size <= _mem->length)) + { + u8 * dst = m3MemData (_mem) + destination; + u8 * src = m3MemData (_mem) + source; + memmove (dst, src, size); + + nextOp (); + } + else d_outOfBoundsMemOp (source, size); + } + else d_outOfBoundsMemOp (destination, size); +} + + +d_m3Op (MemFill) +{ + u32 size = (u32) _r0; + u32 byte = slot (u32); + u64 destination = slot (u32); + + if (M3_LIKELY(destination + size <= _mem->length)) + { + u8 * mem8 = m3MemData (_mem) + destination; + memset (mem8, (u8) byte, size); + nextOp (); + } + else d_outOfBoundsMemOp (destination, size); +} + + +// it's a debate: should the compilation be trigger be the caller or callee page. +// it's a much easier to put it in the caller pager. if it's in the callee, either the entire page +// has be left dangling or it's just a stub that jumps to a newly acquired page. In Gestalt, I opted +// for the stub approach. Stubbing makes it easier to dynamically free the compilation. You can also +// do both. +d_m3Op (Compile) +{ + rewrite_op (op_Call); + + IM3Function function = immediate (IM3Function); + + m3ret_t result = m3Err_none; + + if (M3_UNLIKELY(not function->compiled)) // check to see if function was compiled since this operation was emitted. + result = CompileFunction (function); + + if (not result) + { + // patch up compiled pc and call rewritten op_Call + * ((void**) --_pc) = (void*) (function->compiled); + --_pc; + nextOpDirect (); + } + + newTrap (result); +} + + + +d_m3Op (Entry) +{ + d_m3ClearRegisters + + d_m3TracePrepare + + IM3Function function = immediate (IM3Function); + IM3Memory memory = m3MemInfo (_mem); + +#if d_m3SkipStackCheck + if (true) +#else + if (M3_LIKELY ((void *) (_sp + function->maxStackSlots) < _mem->maxStack)) +#endif + { +#if defined(DEBUG) + function->hits++; +#endif + u8 * stack = (u8 *) ((m3slot_t *) _sp + function->numRetAndArgSlots); + + memset (stack, 0x0, function->numLocalBytes); + stack += function->numLocalBytes; + + if (function->constants) + { + memcpy (stack, function->constants, function->numConstantBytes); + } + +#if d_m3EnableStrace >= 2 + d_m3TracePrint("%s %s {", m3_GetFunctionName(function), SPrintFunctionArgList (function, _sp + function->numRetSlots)); + trace_rt->callDepth++; +#endif + + m3ret_t r = nextOpImpl (); + +#if d_m3EnableStrace >= 2 + trace_rt->callDepth--; + + if (r) { + d_m3TracePrint("} !trap = %s", (char*)r); + } else { + int rettype = GetSingleRetType(function->funcType); + if (rettype != c_m3Type_none) { + char str [128] = { 0 }; + SPrintArg (str, 127, _sp, rettype); + d_m3TracePrint("} = %s", str); + } else { + d_m3TracePrint("}"); + } + } +#endif + + if (M3_UNLIKELY(r)) { + _mem = memory->mallocated; + fillBacktraceFrame (); + } + forwardTrap (r); + } + else newTrap (m3Err_trapStackOverflow); +} + + +d_m3Op (Loop) +{ + d_m3TracePrepare + + // regs are unused coming into a loop anyway + // this reduces code size & stack usage + d_m3ClearRegisters + + m3ret_t r; + + IM3Memory memory = m3MemInfo (_mem); + + do + { +#if d_m3EnableStrace >= 3 + d_m3TracePrint("iter {"); + trace_rt->callDepth++; +#endif + r = nextOpImpl (); + +#if d_m3EnableStrace >= 3 + trace_rt->callDepth--; + d_m3TracePrint("}"); +#endif + // linear memory pointer needs refreshed here because the block it's looping over + // can potentially invoke the grow operation. + _mem = memory->mallocated; + } + while (r == _pc); + + forwardTrap (r); +} + + +d_m3Op (Branch) +{ + jumpOp (* _pc); +} + + +d_m3Op (If_r) +{ + i32 condition = (i32) _r0; + + pc_t elsePC = immediate (pc_t); + + if (condition) + nextOp (); + else + jumpOp (elsePC); +} + + +d_m3Op (If_s) +{ + i32 condition = slot (i32); + + pc_t elsePC = immediate (pc_t); + + if (condition) + nextOp (); + else + jumpOp (elsePC); +} + + +d_m3Op (BranchTable) +{ + u32 branchIndex = slot (u32); // branch index is always in a slot + u32 numTargets = immediate (u32); + + pc_t * branches = (pc_t *) _pc; + + if (branchIndex > numTargets) + branchIndex = numTargets; // the default index + + jumpOp (branches [branchIndex]); +} + + +#define d_m3SetRegisterSetSlot(TYPE, REG) \ +d_m3Op (SetRegister_##TYPE) \ +{ \ + REG = slot (TYPE); \ + nextOp (); \ +} \ + \ +d_m3Op (SetSlot_##TYPE) \ +{ \ + slot (TYPE) = (TYPE) REG; \ + nextOp (); \ +} \ + \ +d_m3Op (PreserveSetSlot_##TYPE) \ +{ \ + TYPE * stack = slot_ptr (TYPE); \ + TYPE * preserve = slot_ptr (TYPE); \ + \ + * preserve = * stack; \ + * stack = (TYPE) REG; \ + \ + nextOp (); \ +} + +d_m3SetRegisterSetSlot (i32, _r0) +d_m3SetRegisterSetSlot (i64, _r0) +#if d_m3HasFloat +d_m3SetRegisterSetSlot (f32, _fp0) +d_m3SetRegisterSetSlot (f64, _fp0) +#endif + +d_m3Op (CopySlot_32) +{ + u32 * dst = slot_ptr (u32); + u32 * src = slot_ptr (u32); + + * dst = * src; + + nextOp (); +} + + +d_m3Op (PreserveCopySlot_32) +{ + u32 * dest = slot_ptr (u32); + u32 * src = slot_ptr (u32); + u32 * preserve = slot_ptr (u32); + + * preserve = * dest; + * dest = * src; + + nextOp (); +} + + +d_m3Op (CopySlot_64) +{ + u64 * dst = slot_ptr (u64); + u64 * src = slot_ptr (u64); + + * dst = * src; // printf ("copy: %p <- %" PRIi64 " <- %p\n", dst, * dst, src); + + nextOp (); +} + + +d_m3Op (PreserveCopySlot_64) +{ + u64 * dest = slot_ptr (u64); + u64 * src = slot_ptr (u64); + u64 * preserve = slot_ptr (u64); + + * preserve = * dest; + * dest = * src; + + nextOp (); +} + + +#if d_m3EnableOpTracing +//-------------------------------------------------------------------------------------------------------- +d_m3Op (DumpStack) +{ + u32 opcodeIndex = immediate (u32); + u32 stackHeight = immediate (u32); + IM3Function function = immediate (IM3Function); + + cstr_t funcName = (function) ? m3_GetFunctionName(function) : ""; + + printf (" %4d ", opcodeIndex); + printf (" %-25s r0: 0x%016" PRIx64 " i:%" PRIi64 " u:%" PRIu64 "\n", funcName, _r0, _r0, _r0); +#if d_m3HasFloat + printf (" fp0: %" PRIf64 "\n", _fp0); +#endif + m3stack_t sp = _sp; + + for (u32 i = 0; i < stackHeight; ++i) + { + cstr_t kind = ""; + + printf ("%p %5s %2d: 0x%" PRIx64 " i:%" PRIi64 "\n", sp, kind, i, (u64) *(sp), (i64) *(sp)); + + ++sp; + } + printf ("---------------------------------------------------------------------------------------------------------\n"); + + nextOpDirect(); +} +#endif + + +#define d_m3Select_i(TYPE, REG) \ +d_m3Op (Select_##TYPE##_rss) \ +{ \ + i32 condition = (i32) _r0; \ + \ + TYPE operand2 = slot (TYPE); \ + TYPE operand1 = slot (TYPE); \ + \ + REG = (condition) ? operand1 : operand2; \ + \ + nextOp (); \ +} \ + \ +d_m3Op (Select_##TYPE##_srs) \ +{ \ + i32 condition = slot (i32); \ + \ + TYPE operand2 = (TYPE) REG; \ + TYPE operand1 = slot (TYPE); \ + \ + REG = (condition) ? operand1 : operand2; \ + \ + nextOp (); \ +} \ + \ +d_m3Op (Select_##TYPE##_ssr) \ +{ \ + i32 condition = slot (i32); \ + \ + TYPE operand2 = slot (TYPE); \ + TYPE operand1 = (TYPE) REG; \ + \ + REG = (condition) ? operand1 : operand2; \ + \ + nextOp (); \ +} \ + \ +d_m3Op (Select_##TYPE##_sss) \ +{ \ + i32 condition = slot (i32); \ + \ + TYPE operand2 = slot (TYPE); \ + TYPE operand1 = slot (TYPE); \ + \ + REG = (condition) ? operand1 : operand2; \ + \ + nextOp (); \ +} + + +d_m3Select_i (i32, _r0) +d_m3Select_i (i64, _r0) + + +#define d_m3Select_f(TYPE, REG, LABEL, SELECTOR) \ +d_m3Op (Select_##TYPE##_##LABEL##ss) \ +{ \ + i32 condition = (i32) SELECTOR; \ + \ + TYPE operand2 = slot (TYPE); \ + TYPE operand1 = slot (TYPE); \ + \ + REG = (condition) ? operand1 : operand2; \ + \ + nextOp (); \ +} \ + \ +d_m3Op (Select_##TYPE##_##LABEL##rs) \ +{ \ + i32 condition = (i32) SELECTOR; \ + \ + TYPE operand2 = (TYPE) REG; \ + TYPE operand1 = slot (TYPE); \ + \ + REG = (condition) ? operand1 : operand2; \ + \ + nextOp (); \ +} \ + \ +d_m3Op (Select_##TYPE##_##LABEL##sr) \ +{ \ + i32 condition = (i32) SELECTOR; \ + \ + TYPE operand2 = slot (TYPE); \ + TYPE operand1 = (TYPE) REG; \ + \ + REG = (condition) ? operand1 : operand2; \ + \ + nextOp (); \ +} + +#if d_m3HasFloat +d_m3Select_f (f32, _fp0, r, _r0) +d_m3Select_f (f32, _fp0, s, slot (i32)) + +d_m3Select_f (f64, _fp0, r, _r0) +d_m3Select_f (f64, _fp0, s, slot (i32)) +#endif + +d_m3Op (Return) +{ + m3StackCheck(); + return m3Err_none; +} + + +d_m3Op (BranchIf_r) +{ + i32 condition = (i32) _r0; + pc_t branch = immediate (pc_t); + + if (condition) + { + jumpOp (branch); + } + else nextOp (); +} + + +d_m3Op (BranchIf_s) +{ + i32 condition = slot (i32); + pc_t branch = immediate (pc_t); + + if (condition) + { + jumpOp (branch); + } + else nextOp (); +} + + +d_m3Op (BranchIfPrologue_r) +{ + i32 condition = (i32) _r0; + pc_t branch = immediate (pc_t); + + if (condition) + { + // this is the "prologue" that ends with + // a plain branch to the actual target + nextOp (); + } + else jumpOp (branch); // jump over the prologue +} + + +d_m3Op (BranchIfPrologue_s) +{ + i32 condition = slot (i32); + pc_t branch = immediate (pc_t); + + if (condition) + { + nextOp (); + } + else jumpOp (branch); +} + + +d_m3Op (ContinueLoop) +{ + m3StackCheck(); + + // TODO: this is where execution can "escape" the M3 code and callback to the client / fiber switch + // OR it can go in the Loop operation. I think it's best to do here. adding code to the loop operation + // has the potential to increase its native-stack usage. (don't forget ContinueLoopIf too.) + + void * loopId = immediate (void *); + return loopId; +} + + +d_m3Op (ContinueLoopIf) +{ + i32 condition = (i32) _r0; + void * loopId = immediate (void *); + + if (condition) + { + return loopId; + } + else nextOp (); +} + + +d_m3Op (Const32) +{ + u32 value = * (u32 *)_pc++; + slot (u32) = value; + nextOp (); +} + + +d_m3Op (Const64) +{ + u64 value = * (u64 *)_pc; + _pc += (M3_SIZEOF_PTR == 4) ? 2 : 1; + slot (u64) = value; + nextOp (); +} + +d_m3Op (Unsupported) +{ + newTrap ("unsupported instruction executed"); +} + +d_m3Op (Unreachable) +{ + m3StackCheck(); + newTrap (m3Err_trapUnreachable); +} + + +d_m3Op (End) +{ + m3StackCheck(); + return m3Err_none; +} + + +d_m3Op (SetGlobal_s32) +{ + u32 * global = immediate (u32 *); + * global = slot (u32); + + nextOp (); +} + + +d_m3Op (SetGlobal_s64) +{ + u64 * global = immediate (u64 *); + * global = slot (u64); + + nextOp (); +} + +#if d_m3HasFloat +d_m3Op (SetGlobal_f32) +{ + f32 * global = immediate (f32 *); + * global = _fp0; + + nextOp (); +} + + +d_m3Op (SetGlobal_f64) +{ + f64 * global = immediate (f64 *); + * global = _fp0; + + nextOp (); +} +#endif + + +#if d_m3SkipMemoryBoundsCheck +# define m3MemCheck(x) true +#else +# define m3MemCheck(x) M3_LIKELY(x) +#endif + +// memcpy here is to support non-aligned access on some platforms. + +#define d_m3Load(REG,DEST_TYPE,SRC_TYPE) \ +d_m3Op(DEST_TYPE##_Load_##SRC_TYPE##_r) \ +{ \ + d_m3TracePrepare \ + u32 offset = immediate (u32); \ + u64 operand = (u32) _r0; \ + operand += offset; \ + \ + if (m3MemCheck( \ + operand + sizeof (SRC_TYPE) <= _mem->length \ + )) { \ + { \ + u8* src8 = m3MemData(_mem) + operand; \ + SRC_TYPE value; \ + memcpy(&value, src8, sizeof(value)); \ + M3_BSWAP_##SRC_TYPE(value); \ + REG = (DEST_TYPE)value; \ + d_m3TraceLoad(DEST_TYPE, operand, REG); \ + } \ + nextOp (); \ + } else d_outOfBounds; \ +} \ +d_m3Op(DEST_TYPE##_Load_##SRC_TYPE##_s) \ +{ \ + d_m3TracePrepare \ + u64 operand = slot (u32); \ + u32 offset = immediate (u32); \ + operand += offset; \ + \ + if (m3MemCheck( \ + operand + sizeof (SRC_TYPE) <= _mem->length \ + )) { \ + { \ + u8* src8 = m3MemData(_mem) + operand; \ + SRC_TYPE value; \ + memcpy(&value, src8, sizeof(value)); \ + M3_BSWAP_##SRC_TYPE(value); \ + REG = (DEST_TYPE)value; \ + d_m3TraceLoad(DEST_TYPE, operand, REG); \ + } \ + nextOp (); \ + } else d_outOfBounds; \ +} + +// printf ("get: %d -> %d\n", operand + offset, (i64) REG); + + +#define d_m3Load_i(DEST_TYPE, SRC_TYPE) d_m3Load(_r0, DEST_TYPE, SRC_TYPE) +#define d_m3Load_f(DEST_TYPE, SRC_TYPE) d_m3Load(_fp0, DEST_TYPE, SRC_TYPE) + +#if d_m3HasFloat +d_m3Load_f (f32, f32); +d_m3Load_f (f64, f64); +#endif + +d_m3Load_i (i32, i8); +d_m3Load_i (i32, u8); +d_m3Load_i (i32, i16); +d_m3Load_i (i32, u16); +d_m3Load_i (i32, i32); + +d_m3Load_i (i64, i8); +d_m3Load_i (i64, u8); +d_m3Load_i (i64, i16); +d_m3Load_i (i64, u16); +d_m3Load_i (i64, i32); +d_m3Load_i (i64, u32); +d_m3Load_i (i64, i64); + +#define d_m3Store(REG, SRC_TYPE, DEST_TYPE) \ +d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_rs) \ +{ \ + d_m3TracePrepare \ + u64 operand = slot (u32); \ + u32 offset = immediate (u32); \ + operand += offset; \ + \ + if (m3MemCheck( \ + operand + sizeof (DEST_TYPE) <= _mem->length \ + )) { \ + { \ + d_m3TraceStore(SRC_TYPE, operand, REG); \ + u8* mem8 = m3MemData(_mem) + operand; \ + DEST_TYPE val = (DEST_TYPE) REG; \ + M3_BSWAP_##DEST_TYPE(val); \ + memcpy(mem8, &val, sizeof(val)); \ + } \ + nextOp (); \ + } else d_outOfBounds; \ +} \ +d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_sr) \ +{ \ + d_m3TracePrepare \ + const SRC_TYPE value = slot (SRC_TYPE); \ + u64 operand = (u32) _r0; \ + u32 offset = immediate (u32); \ + operand += offset; \ + \ + if (m3MemCheck( \ + operand + sizeof (DEST_TYPE) <= _mem->length \ + )) { \ + { \ + d_m3TraceStore(SRC_TYPE, operand, value); \ + u8* mem8 = m3MemData(_mem) + operand; \ + DEST_TYPE val = (DEST_TYPE) value; \ + M3_BSWAP_##DEST_TYPE(val); \ + memcpy(mem8, &val, sizeof(val)); \ + } \ + nextOp (); \ + } else d_outOfBounds; \ +} \ +d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_ss) \ +{ \ + d_m3TracePrepare \ + const SRC_TYPE value = slot (SRC_TYPE); \ + u64 operand = slot (u32); \ + u32 offset = immediate (u32); \ + operand += offset; \ + \ + if (m3MemCheck( \ + operand + sizeof (DEST_TYPE) <= _mem->length \ + )) { \ + { \ + d_m3TraceStore(SRC_TYPE, operand, value); \ + u8* mem8 = m3MemData(_mem) + operand; \ + DEST_TYPE val = (DEST_TYPE) value; \ + M3_BSWAP_##DEST_TYPE(val); \ + memcpy(mem8, &val, sizeof(val)); \ + } \ + nextOp (); \ + } else d_outOfBounds; \ +} + +// both operands can be in regs when storing a float +#define d_m3StoreFp(REG, TYPE) \ +d_m3Op (TYPE##_Store_##TYPE##_rr) \ +{ \ + d_m3TracePrepare \ + u64 operand = (u32) _r0; \ + u32 offset = immediate (u32); \ + operand += offset; \ + \ + if (m3MemCheck( \ + operand + sizeof (TYPE) <= _mem->length \ + )) { \ + { \ + d_m3TraceStore(TYPE, operand, REG); \ + u8* mem8 = m3MemData(_mem) + operand; \ + TYPE val = (TYPE) REG; \ + M3_BSWAP_##TYPE(val); \ + memcpy(mem8, &val, sizeof(val)); \ + } \ + nextOp (); \ + } else d_outOfBounds; \ +} + + +#define d_m3Store_i(SRC_TYPE, DEST_TYPE) d_m3Store(_r0, SRC_TYPE, DEST_TYPE) +#define d_m3Store_f(SRC_TYPE, DEST_TYPE) d_m3Store(_fp0, SRC_TYPE, DEST_TYPE) d_m3StoreFp (_fp0, SRC_TYPE); + +#if d_m3HasFloat +d_m3Store_f (f32, f32) +d_m3Store_f (f64, f64) +#endif + +d_m3Store_i (i32, u8) +d_m3Store_i (i32, i16) +d_m3Store_i (i32, i32) + +d_m3Store_i (i64, u8) +d_m3Store_i (i64, i16) +d_m3Store_i (i64, i32) +d_m3Store_i (i64, i64) + +#undef m3MemCheck + + +//--------------------------------------------------------------------------------------------------------------------- +// debug/profiling +//--------------------------------------------------------------------------------------------------------------------- +#if d_m3EnableOpTracing +d_m3RetSig debugOp (d_m3OpSig, cstr_t i_opcode) +{ + char name [100]; + strcpy (name, strstr (i_opcode, "op_") + 3); + char * bracket = strstr (name, "("); + if (bracket) { + *bracket = 0; + } + + puts (name); + nextOpDirect(); +} +# endif + +# if d_m3EnableOpProfiling +d_m3RetSig profileOp (d_m3OpSig, cstr_t i_operationName) +{ + ProfileHit (i_operationName); + + nextOpDirect(); +} +# endif + +d_m3EndExternC + +#endif // m3_exec_h diff --git a/cpp/m3_exec_defs.h b/cpp/m3_exec_defs.h new file mode 100644 index 0000000..7b6b109 --- /dev/null +++ b/cpp/m3_exec_defs.h @@ -0,0 +1,63 @@ +// +// m3_exec_defs.h +// +// Created by Steven Massey on 5/1/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#ifndef m3_exec_defs_h +#define m3_exec_defs_h + +#include "m3_core.h" + +d_m3BeginExternC + +# define m3MemData(mem) (u8*)(((M3MemoryHeader*)(mem))+1) +# define m3MemRuntime(mem) (((M3MemoryHeader*)(mem))->runtime) +# define m3MemInfo(mem) (&(((M3MemoryHeader*)(mem))->runtime->memory)) + +# define d_m3BaseOpSig pc_t _pc, m3stack_t _sp, M3MemoryHeader * _mem, m3reg_t _r0 +# define d_m3BaseOpArgs _sp, _mem, _r0 +# define d_m3BaseOpAllArgs _pc, _sp, _mem, _r0 +# define d_m3BaseOpDefaultArgs 0 +# define d_m3BaseClearRegisters _r0 = 0; + +# define d_m3ExpOpSig(...) d_m3BaseOpSig, __VA_ARGS__ +# define d_m3ExpOpArgs(...) d_m3BaseOpArgs, __VA_ARGS__ +# define d_m3ExpOpAllArgs(...) d_m3BaseOpAllArgs, __VA_ARGS__ +# define d_m3ExpOpDefaultArgs(...) d_m3BaseOpDefaultArgs, __VA_ARGS__ +# define d_m3ExpClearRegisters(...) d_m3BaseClearRegisters; __VA_ARGS__ + +# if d_m3HasFloat +# define d_m3OpSig d_m3ExpOpSig (f64 _fp0) +# define d_m3OpArgs d_m3ExpOpArgs (_fp0) +# define d_m3OpAllArgs d_m3ExpOpAllArgs (_fp0) +# define d_m3OpDefaultArgs d_m3ExpOpDefaultArgs (0.) +# define d_m3ClearRegisters d_m3ExpClearRegisters (_fp0 = 0.;) +# else +# define d_m3OpSig d_m3BaseOpSig +# define d_m3OpArgs d_m3BaseOpArgs +# define d_m3OpAllArgs d_m3BaseOpAllArgs +# define d_m3OpDefaultArgs d_m3BaseOpDefaultArgs +# define d_m3ClearRegisters d_m3BaseClearRegisters +# endif + +typedef m3ret_t (vectorcall * IM3Operation) (d_m3OpSig); + +#define d_m3RetSig static inline m3ret_t vectorcall +#define d_m3Op(NAME) M3_NO_UBSAN d_m3RetSig op_##NAME (d_m3OpSig) + +#define nextOpImpl() ((IM3Operation)(* _pc))(_pc + 1, d_m3OpArgs) +#define jumpOpImpl(PC) ((IM3Operation)(* PC))( PC + 1, d_m3OpArgs) + +#define nextOpDirect() M3_MUSTTAIL return nextOpImpl() +#define jumpOpDirect(PC) M3_MUSTTAIL return jumpOpImpl((pc_t)(PC)) + +d_m3RetSig RunCode (d_m3OpSig) +{ + nextOpDirect(); +} + +d_m3EndExternC + +#endif // m3_exec_defs_h diff --git a/cpp/m3_function.c b/cpp/m3_function.c new file mode 100644 index 0000000..b100cae --- /dev/null +++ b/cpp/m3_function.c @@ -0,0 +1,233 @@ +// +// m3_function.c +// +// Created by Steven Massey on 4/7/21. +// Copyright © 2021 Steven Massey. All rights reserved. +// + +#include "m3_function.h" +#include "m3_env.h" + + +M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numTypes) +{ + *o_functionType = (IM3FuncType) m3_Malloc ("M3FuncType", sizeof (M3FuncType) + i_numTypes); + return (*o_functionType) ? m3Err_none : m3Err_mallocFailed; +} + + +bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB) +{ + if (i_typeA->numRets == i_typeB->numRets && i_typeA->numArgs == i_typeB->numArgs) + { + return (memcmp (i_typeA->types, i_typeB->types, i_typeA->numRets + i_typeA->numArgs) == 0); + } + + return false; +} + +u16 GetFuncTypeNumParams (const IM3FuncType i_funcType) +{ + return i_funcType ? i_funcType->numArgs : 0; +} + + +u8 GetFuncTypeParamType (const IM3FuncType i_funcType, u16 i_index) +{ + u8 type = c_m3Type_unknown; + + if (i_funcType) + { + if (i_index < i_funcType->numArgs) + { + type = i_funcType->types [i_funcType->numRets + i_index]; + } + } + + return type; +} + + + +u16 GetFuncTypeNumResults (const IM3FuncType i_funcType) +{ + return i_funcType ? i_funcType->numRets : 0; +} + + +u8 GetFuncTypeResultType (const IM3FuncType i_funcType, u16 i_index) +{ + u8 type = c_m3Type_unknown; + + if (i_funcType) + { + if (i_index < i_funcType->numRets) + { + type = i_funcType->types [i_index]; + } + } + + return type; +} + + +//--------------------------------------------------------------------------------------------------------------- + + +void FreeImportInfo (M3ImportInfo * i_info) +{ + m3_Free (i_info->moduleUtf8); + m3_Free (i_info->fieldUtf8); +} + + +void Function_Release (IM3Function i_function) +{ + m3_Free (i_function->constants); + + for (int i = 0; i < i_function->numNames; i++) + { + // name can be an alias of fieldUtf8 + if (i_function->names[i] != i_function->import.fieldUtf8) + { + m3_Free (i_function->names[i]); + } + } + + FreeImportInfo (& i_function->import); + + if (i_function->ownsWasmCode) + m3_Free (i_function->wasm); + + // Function_FreeCompiledCode (func); + +# if (d_m3EnableCodePageRefCounting) + { + m3_Free (i_function->codePageRefs); + i_function->numCodePageRefs = 0; + } +# endif +} + + +void Function_FreeCompiledCode (IM3Function i_function) +{ +# if (d_m3EnableCodePageRefCounting) + { + i_function->compiled = NULL; + + while (i_function->numCodePageRefs--) + { + IM3CodePage page = i_function->codePageRefs [i_function->numCodePageRefs]; + + if (--(page->info.usageCount) == 0) + { +// printf ("free %p\n", page); + } + } + + m3_Free (i_function->codePageRefs); + + Runtime_ReleaseCodePages (i_function->module->runtime); + } +# endif +} + + +cstr_t m3_GetFunctionName (IM3Function i_function) +{ + u16 numNames = 0; + cstr_t *names = GetFunctionNames(i_function, &numNames); + if (numNames > 0) + return names[0]; + else + return ""; +} + + +IM3Module m3_GetFunctionModule (IM3Function i_function) +{ + return i_function ? i_function->module : NULL; +} + + +cstr_t * GetFunctionNames (IM3Function i_function, u16 * o_numNames) +{ + if (!i_function || !o_numNames) + return NULL; + + if (i_function->import.fieldUtf8) + { + *o_numNames = 1; + return &i_function->import.fieldUtf8; + } + else + { + *o_numNames = i_function->numNames; + return i_function->names; + } +} + + +cstr_t GetFunctionImportModuleName (IM3Function i_function) +{ + return (i_function->import.moduleUtf8) ? i_function->import.moduleUtf8 : ""; +} + + +u16 GetFunctionNumArgs (IM3Function i_function) +{ + u16 numArgs = 0; + + if (i_function) + { + if (i_function->funcType) + numArgs = i_function->funcType->numArgs; + } + + return numArgs; +} + +u8 GetFunctionArgType (IM3Function i_function, u32 i_index) +{ + u8 type = c_m3Type_none; + + if (i_index < GetFunctionNumArgs (i_function)) + { + u32 numReturns = i_function->funcType->numRets; + + type = i_function->funcType->types [numReturns + i_index]; + } + + return type; +} + + +u16 GetFunctionNumReturns (IM3Function i_function) +{ + u16 numReturns = 0; + + if (i_function) + { + if (i_function->funcType) + numReturns = i_function->funcType->numRets; + } + + return numReturns; +} + + +u8 GetFunctionReturnType (const IM3Function i_function, u16 i_index) +{ + return i_function ? GetFuncTypeResultType (i_function->funcType, i_index) : c_m3Type_unknown; +} + + +u32 GetFunctionNumArgsAndLocals (IM3Function i_function) +{ + if (i_function) + return i_function->numLocals + GetFunctionNumArgs (i_function); + else + return 0; +} + diff --git a/cpp/m3_function.h b/cpp/m3_function.h new file mode 100644 index 0000000..4a006a2 --- /dev/null +++ b/cpp/m3_function.h @@ -0,0 +1,103 @@ +// +// m3_function.h +// +// Created by Steven Massey on 4/7/21. +// Copyright © 2021 Steven Massey. All rights reserved. +// + +#ifndef m3_function_h +#define m3_function_h + +#include "m3_core.h" + +d_m3BeginExternC + +//--------------------------------------------------------------------------------------------------------------------------------- + +typedef struct M3FuncType +{ + struct M3FuncType * next; + + u16 numRets; + u16 numArgs; + u8 types []; // returns, then args +} +M3FuncType; + +typedef M3FuncType * IM3FuncType; + + +M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numTypes); +bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB); + +u16 GetFuncTypeNumParams (const IM3FuncType i_funcType); +u8 GetFuncTypeParamType (const IM3FuncType i_funcType, u16 i_index); + +u16 GetFuncTypeNumResults (const IM3FuncType i_funcType); +u8 GetFuncTypeResultType (const IM3FuncType i_funcType, u16 i_index); + +//--------------------------------------------------------------------------------------------------------------------------------- + +typedef struct M3Function +{ + struct M3Module * module; + + M3ImportInfo import; + + bytes_t wasm; + bytes_t wasmEnd; + + cstr_t names[d_m3MaxDuplicateFunctionImpl]; + cstr_t export_name; // should be a part of "names" + u16 numNames; // maximum of d_m3MaxDuplicateFunctionImpl + + IM3FuncType funcType; + + pc_t compiled; + +# if (d_m3EnableCodePageRefCounting) + IM3CodePage * codePageRefs; // array of all pages used + u32 numCodePageRefs; +# endif + +# if defined (DEBUG) + u32 hits; + u32 index; +# endif + + u16 maxStackSlots; + + u16 numRetSlots; + u16 numRetAndArgSlots; + + u16 numLocals; // not including args + u16 numLocalBytes; + + bool ownsWasmCode; + + u16 numConstantBytes; + void * constants; +} +M3Function; + +void Function_Release (IM3Function i_function); +void Function_FreeCompiledCode (IM3Function i_function); + +cstr_t GetFunctionImportModuleName (IM3Function i_function); +cstr_t * GetFunctionNames (IM3Function i_function, u16 * o_numNames); +u16 GetFunctionNumArgs (IM3Function i_function); +u8 GetFunctionArgType (IM3Function i_function, u32 i_index); + +u16 GetFunctionNumReturns (IM3Function i_function); +u8 GetFunctionReturnType (const IM3Function i_function, u16 i_index); + +u32 GetFunctionNumArgsAndLocals (IM3Function i_function); + +cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp); + +//--------------------------------------------------------------------------------------------------------------------------------- + + +d_m3EndExternC + +#endif /* m3_function_h */ diff --git a/cpp/m3_info.c b/cpp/m3_info.c new file mode 100644 index 0000000..7153f2c --- /dev/null +++ b/cpp/m3_info.c @@ -0,0 +1,564 @@ +// +// m3_info.c +// +// Created by Steven Massey on 4/27/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#include "m3_env.h" +#include "m3_info.h" +#include "m3_compile.h" + +#if defined(DEBUG) || (d_m3EnableStrace >= 2) + +size_t SPrintArg (char * o_string, size_t i_stringBufferSize, voidptr_t i_sp, u8 i_type) +{ + int len = 0; + + * o_string = 0; + + if (i_type == c_m3Type_i32) + len = snprintf (o_string, i_stringBufferSize, "%" PRIi32, * (i32 *) i_sp); + else if (i_type == c_m3Type_i64) + len = snprintf (o_string, i_stringBufferSize, "%" PRIi64, * (i64 *) i_sp); +#if d_m3HasFloat + else if (i_type == c_m3Type_f32) + len = snprintf (o_string, i_stringBufferSize, "%" PRIf32, * (f32 *) i_sp); + else if (i_type == c_m3Type_f64) + len = snprintf (o_string, i_stringBufferSize, "%" PRIf64, * (f64 *) i_sp); +#endif + + len = M3_MAX (0, len); + + return len; +} + + +cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp) +{ + int ret; + static char string [256]; + + char * s = string; + ccstr_t e = string + sizeof(string) - 1; + + ret = snprintf (s, e-s, "("); + s += M3_MAX (0, ret); + + u64 * argSp = (u64 *) i_sp; + + IM3FuncType funcType = i_function->funcType; + if (funcType) + { + u32 numArgs = funcType->numArgs; + + for (u32 i = 0; i < numArgs; ++i) + { + u8 type = d_FuncArgType(funcType, i); + + ret = snprintf (s, e-s, "%s: ", c_waTypes [type]); + s += M3_MAX (0, ret); + + s += SPrintArg (s, e-s, argSp + i, type); + + if (i != numArgs - 1) { + ret = snprintf (s, e-s, ", "); + s += M3_MAX (0, ret); + } + } + } + else printf ("null signature"); + + ret = snprintf (s, e-s, ")"); + s += M3_MAX (0, ret); + + return string; +} + +#endif + +#ifdef DEBUG + +// a central function you can be breakpoint: +void ExceptionBreakpoint (cstr_t i_exception, cstr_t i_message) +{ + printf ("\nexception: '%s' @ %s\n", i_exception, i_message); + return; +} + + +typedef struct OpInfo +{ + IM3OpInfo info; + m3opcode_t opcode; +} +OpInfo; + +void m3_PrintM3Info () +{ + printf ("\n-- m3 configuration --------------------------------------------\n"); +// printf (" sizeof M3CodePage : %zu bytes (%d slots) \n", sizeof (M3CodePage), c_m3CodePageNumSlots); + printf (" sizeof M3MemPage : %u bytes \n", d_m3MemPageSize); + printf (" sizeof M3Compilation : %zu bytes \n", sizeof (M3Compilation)); + printf (" sizeof M3Function : %zu bytes \n", sizeof (M3Function)); + printf ("----------------------------------------------------------------\n\n"); +} + + +void * v_PrintEnvModuleInfo (IM3Module i_module, u32 * io_index) +{ + printf (" module [%u] name: '%s'; funcs: %d \n", * io_index++, i_module->name, i_module->numFunctions); + + return NULL; +} + + +void m3_PrintRuntimeInfo (IM3Runtime i_runtime) +{ + printf ("\n-- m3 runtime -------------------------------------------------\n"); + + printf (" stack-size: %zu \n\n", i_runtime->numStackSlots * sizeof (m3slot_t)); + + u32 moduleIndex = 0; + ForEachModule (i_runtime, (ModuleVisitor) v_PrintEnvModuleInfo, & moduleIndex); + + printf ("----------------------------------------------------------------\n\n"); +} + + +cstr_t GetTypeName (u8 i_m3Type) +{ + if (i_m3Type < 5) + return c_waTypes [i_m3Type]; + else + return "?"; +} + + +// TODO: these 'static char string []' aren't thread-friendly. though these functions are +// mainly for simple diagnostics during development, it'd be nice if they were fully reliable. + +cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType) +{ + static char string [256]; + + sprintf (string, "("); + + for (u32 i = 0; i < i_funcType->numArgs; ++i) + { + if (i != 0) + strcat (string, ", "); + + strcat (string, GetTypeName (d_FuncArgType(i_funcType, i))); + } + + strcat (string, ") -> "); + + for (u32 i = 0; i < i_funcType->numRets; ++i) + { + if (i != 0) + strcat (string, ", "); + + strcat (string, GetTypeName (d_FuncRetType(i_funcType, i))); + } + + return string; +} + + +cstr_t SPrintValue (void * i_value, u8 i_type) +{ + static char string [100]; + SPrintArg (string, 100, (m3stack_t) i_value, i_type); + return string; +} + +static +OpInfo find_operation_info (IM3Operation i_operation) +{ + OpInfo opInfo = { NULL, 0 }; + + if (!i_operation) return opInfo; + + // TODO: find also extended opcodes + for (u32 i = 0; i <= 0xff; ++i) + { + IM3OpInfo oi = GetOpInfo (i); + + if (oi->type != c_m3Type_unknown) + { + for (u32 o = 0; o < 4; ++o) + { + if (oi->operations [o] == i_operation) + { + opInfo.info = oi; + opInfo.opcode = i; + break; + } + } + } + else break; + } + + return opInfo; +} + + +#undef fetch +#define fetch(TYPE) (* (TYPE *) ((*o_pc)++)) + +#define d_m3Decoder(FUNC) void Decode_##FUNC (char * o_string, u8 i_opcode, IM3Operation i_operation, IM3OpInfo i_opInfo, pc_t * o_pc) + +d_m3Decoder (Call) +{ + void * function = fetch (void *); + i32 stackOffset = fetch (i32); + + sprintf (o_string, "%p; stack-offset: %d", function, stackOffset); +} + + +d_m3Decoder (Entry) +{ + IM3Function function = fetch (IM3Function); + + // only prints out the first registered name for the function + sprintf (o_string, "%s", m3_GetFunctionName(function)); +} + + +d_m3Decoder (f64_Store) +{ + if (i_operation == i_opInfo->operations [0]) + { + u32 operand = fetch (u32); + u32 offset = fetch (u32); + + sprintf (o_string, "offset= slot:%d + immediate:%d", operand, offset); + } + +// sprintf (o_string, "%s", function->name); +} + + +d_m3Decoder (Branch) +{ + void * target = fetch (void *); + sprintf (o_string, "%p", target); +} + +d_m3Decoder (BranchTable) +{ + u32 slot = fetch (u32); + + o_string += sprintf (o_string, "slot: %" PRIu32 "; targets: ", slot); + +// IM3Function function = fetch2 (IM3Function); + + i32 targets = fetch (i32); + + for (i32 i = 0; i < targets; ++i) + { + pc_t addr = fetch (pc_t); + o_string += sprintf (o_string, "%" PRIi32 "=%p, ", i, addr); + } + + pc_t addr = fetch (pc_t); + sprintf (o_string, "def=%p ", addr); +} + + +d_m3Decoder (Const) +{ + u64 value = fetch (u64); i32 offset = fetch (i32); + sprintf (o_string, " slot [%d] = %" PRIu64, offset, value); +} + + +#undef fetch + +void DecodeOperation (char * o_string, u8 i_opcode, IM3Operation i_operation, IM3OpInfo i_opInfo, pc_t * o_pc) +{ + #define d_m3Decode(OPCODE, FUNC) case OPCODE: Decode_##FUNC (o_string, i_opcode, i_operation, i_opInfo, o_pc); break; + + switch (i_opcode) + { +// d_m3Decode (0xc0, Const) + d_m3Decode (0xc5, Entry) + d_m3Decode (c_waOp_call, Call) + d_m3Decode (c_waOp_branch, Branch) + d_m3Decode (c_waOp_branchTable, BranchTable) + d_m3Decode (0x39, f64_Store) + } +} + +// WARNING/TODO: this isn't fully implemented. it blindly assumes each word is a Operation pointer +// and, if an operation happens to missing from the c_operations table it won't be recognized here +void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC) +{ + m3log (code, "code page seq: %d", i_codePage->info.sequence); + + pc_t pc = i_startPC ? i_startPC : GetPageStartPC (i_codePage); + pc_t end = GetPagePC (i_codePage); + + m3log (code, "---------------------------------------------------------------------------------------"); + + while (pc < end) + { + pc_t operationPC = pc; + IM3Operation op = (IM3Operation) (* pc++); + + OpInfo i = find_operation_info (op); + + if (i.info) + { + char infoString [8*1024] = { 0 }; + + DecodeOperation (infoString, i.opcode, op, i.info, & pc); + + m3log (code, "%p | %20s %s", operationPC, i.info->name, infoString); + } + else + m3log (code, "%p | %p", operationPC, op); + + } + + m3log (code, "---------------------------------------------------------------------------------------"); + + m3log (code, "free-lines: %d", i_codePage->info.numLines - i_codePage->info.lineIndex); +} + + +void dump_type_stack (IM3Compilation o) +{ + /* Reminders about how the stack works! :) + -- args & locals remain on the type stack for duration of the function. Denoted with a constant 'A' and 'L' in this dump. + -- the initial stack dumps originate from the CompileLocals () function, so these identifiers won't/can't be + applied until this compilation stage is finished + -- constants are not statically represented in the type stack (like args & constants) since they don't have/need + write counts + + -- the number shown for static args and locals (value in wasmStack [i]) represents the write count for the variable + + -- (does Wasm ever write to an arg? I dunno/don't remember.) + -- the number for the dynamic stack values represents the slot number. + -- if the slot index points to arg, local or constant it's denoted with a lowercase 'a', 'l' or 'c' + + */ + + // for the assert at end of dump: + i32 regAllocated [2] = { (i32) IsRegisterAllocated (o, 0), (i32) IsRegisterAllocated (o, 1) }; + + // display whether r0 or fp0 is allocated. these should then also be reflected somewhere in the stack too. + d_m3Log(stack, "\n"); + d_m3Log(stack, " "); + printf ("%s %s ", regAllocated [0] ? "(r0)" : " ", regAllocated [1] ? "(fp0)" : " "); + printf("\n"); + + for (u32 p = 1; p <= 2; ++p) + { + d_m3Log(stack, " "); + + for (u16 i = 0; i < o->stackIndex; ++i) + { + if (i > 0 and i == o->stackFirstDynamicIndex) + printf ("#"); + + if (i == o->block.blockStackIndex) + printf (">"); + + const char * type = c_waCompactTypes [o->typeStack [i]]; + + const char * location = ""; + + i32 slot = o->wasmStack [i]; + + if (IsRegisterSlotAlias (slot)) + { + bool isFp = IsFpRegisterSlotAlias (slot); + location = isFp ? "/f" : "/r"; + + regAllocated [isFp]--; + slot = -1; + } + else + { + if (slot < o->slotFirstDynamicIndex) + { + if (slot >= o->slotFirstConstIndex) + location = "c"; + else if (slot >= o->function->numRetAndArgSlots) + location = "L"; + else + location = "a"; + } + } + + char item [100]; + + if (slot >= 0) + sprintf (item, "%s%s%d", type, location, slot); + else + sprintf (item, "%s%s", type, location); + + if (p == 1) + { + size_t s = strlen (item); + + sprintf (item, "%d", i); + + while (strlen (item) < s) + strcat (item, " "); + } + + printf ("|%s ", item); + + } + printf ("\n"); + } + +// for (u32 r = 0; r < 2; ++r) +// d_m3Assert (regAllocated [r] == 0); // reg allocation & stack out of sync + + u16 maxSlot = GetMaxUsedSlotPlusOne (o); + + if (maxSlot > o->slotFirstDynamicIndex) + { + d_m3Log (stack, " -"); + + for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i) + printf ("----"); + + printf ("\n"); + + d_m3Log (stack, " slot |"); + for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i) + printf ("%3d|", i); + + printf ("\n"); + d_m3Log (stack, " alloc |"); + + for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i) + { + printf ("%3d|", o->m3Slots [i]); + } + + printf ("\n"); + } + d_m3Log(stack, "\n"); +} + + +static const char * GetOpcodeIndentionString (i32 blockDepth) +{ + blockDepth += 1; + + if (blockDepth < 0) + blockDepth = 0; + + static const char * s_spaces = "......................................................................................."; + const char * indent = s_spaces + strlen (s_spaces); + indent -= (blockDepth * 2); + if (indent < s_spaces) + indent = s_spaces; + + return indent; +} + + +const char * get_indention_string (IM3Compilation o) +{ + return GetOpcodeIndentionString (o->block.depth+4); +} + + +void log_opcode (IM3Compilation o, m3opcode_t i_opcode) +{ + i32 depth = o->block.depth; + if (i_opcode == c_waOp_end or i_opcode == c_waOp_else) + depth--; + + m3log (compile, "%4d | 0x%02x %s %s", o->numOpcodes++, i_opcode, GetOpcodeIndentionString (depth), GetOpInfo(i_opcode)->name); +} + + +void log_emit (IM3Compilation o, IM3Operation i_operation) +{ + OpInfo i = find_operation_info (i_operation); + + d_m3Log(emit, ""); + if (i.info) + { + printf ("%p: %s\n", GetPagePC (o->page), i.info->name); + } + else printf ("not found: %p\n", i_operation); +} + +#endif // DEBUG + + +# if d_m3EnableOpProfiling + +typedef struct M3ProfilerSlot +{ + cstr_t opName; + u64 hitCount; +} +M3ProfilerSlot; + +static M3ProfilerSlot s_opProfilerCounts [d_m3ProfilerSlotMask + 1] = {}; + +void ProfileHit (cstr_t i_operationName) +{ + u64 ptr = (u64) i_operationName; + + M3ProfilerSlot * slot = & s_opProfilerCounts [ptr & d_m3ProfilerSlotMask]; + + if (slot->opName) + { + if (slot->opName != i_operationName) + { + m3_Abort ("profiler slot collision; increase d_m3ProfilerSlotMask"); + } + } + + slot->opName = i_operationName; + slot->hitCount++; +} + + +void m3_PrintProfilerInfo () +{ + M3ProfilerSlot dummy; + M3ProfilerSlot * maxSlot = & dummy; + + do + { + maxSlot->hitCount = 0; + + for (u32 i = 0; i <= d_m3ProfilerSlotMask; ++i) + { + M3ProfilerSlot * slot = & s_opProfilerCounts [i]; + + if (slot->opName) + { + if (slot->hitCount > maxSlot->hitCount) + maxSlot = slot; + } + } + + if (maxSlot->opName) + { + fprintf (stderr, "%13llu %s\n", maxSlot->hitCount, maxSlot->opName); + maxSlot->opName = NULL; + } + } + while (maxSlot->hitCount); +} + +# else + +void m3_PrintProfilerInfo () {} + +# endif + diff --git a/cpp/m3_info.h b/cpp/m3_info.h new file mode 100644 index 0000000..228e93f --- /dev/null +++ b/cpp/m3_info.h @@ -0,0 +1,38 @@ +// +// m3_info.h +// +// Created by Steven Massey on 12/6/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#ifndef m3_info_h +#define m3_info_h + +#include "m3_compile.h" + +d_m3BeginExternC + +void ProfileHit (cstr_t i_operationName); + +#ifdef DEBUG + +void dump_type_stack (IM3Compilation o); +void log_opcode (IM3Compilation o, m3opcode_t i_opcode); +const char * get_indention_string (IM3Compilation o); +void log_emit (IM3Compilation o, IM3Operation i_operation); + +cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType); + +#else // DEBUG + +#define dump_type_stack(...) {} +#define log_opcode(...) {} +#define get_indention_string(...) "" +#define emit_stack_dump(...) {} +#define log_emit(...) {} + +#endif // DEBUG + +d_m3EndExternC + +#endif // m3_info_h diff --git a/cpp/m3_math_utils.h b/cpp/m3_math_utils.h new file mode 100644 index 0000000..8c6ff6d --- /dev/null +++ b/cpp/m3_math_utils.h @@ -0,0 +1,268 @@ +// +// m3_math_utils.h +// +// Created by Volodymyr Shymanksyy on 8/10/19. +// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. +// + +#ifndef m3_math_utils_h +#define m3_math_utils_h + +#include "m3_core.h" + +#include + +#if defined(M3_COMPILER_MSVC) + +#include + +#define __builtin_popcount __popcnt + +static inline +int __builtin_ctz(uint32_t x) { + unsigned long ret; + _BitScanForward(&ret, x); + return (int)ret; +} + +static inline +int __builtin_clz(uint32_t x) { + unsigned long ret; + _BitScanReverse(&ret, x); + return (int)(31 ^ ret); +} + + + +#ifdef _WIN64 + +#define __builtin_popcountll __popcnt64 + +static inline +int __builtin_ctzll(uint64_t value) { + unsigned long ret; + _BitScanForward64(&ret, value); + return (int)ret; +} + +static inline +int __builtin_clzll(uint64_t value) { + unsigned long ret; + _BitScanReverse64(&ret, value); + return (int)(63 ^ ret); +} + +#else // _WIN64 + +#define __builtin_popcountll(x) (__popcnt((x) & 0xFFFFFFFF) + __popcnt((x) >> 32)) + +static inline +int __builtin_ctzll(uint64_t value) { + //if (value == 0) return 64; // Note: ctz(0) result is undefined anyway + uint32_t msh = (uint32_t)(value >> 32); + uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); + if (lsh != 0) return __builtin_ctz(lsh); + return 32 + __builtin_ctz(msh); +} + +static inline +int __builtin_clzll(uint64_t value) { + //if (value == 0) return 64; // Note: clz(0) result is undefined anyway + uint32_t msh = (uint32_t)(value >> 32); + uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); + if (msh != 0) return __builtin_clz(msh); + return 32 + __builtin_clz(lsh); +} + +#endif // _WIN64 + +#endif // defined(M3_COMPILER_MSVC) + + +// TODO: not sure why, signbit is actually defined in math.h +#if (defined(ESP8266) || defined(ESP32)) && !defined(signbit) + #define signbit(__x) \ + ((sizeof(__x) == sizeof(float)) ? __signbitf(__x) : __signbitd(__x)) +#endif + +#if defined(__AVR__) + +static inline +float rintf( float arg ) { + union { float f; uint32_t i; } u; + u.f = arg; + uint32_t ux = u.i & 0x7FFFFFFF; + if (M3_UNLIKELY(ux == 0 || ux > 0x5A000000)) { + return arg; + } + return (float)lrint(arg); +} + +static inline +double rint( double arg ) { + union { double f; uint32_t i[2]; } u; + u.f = arg; + uint32_t ux = u.i[1] & 0x7FFFFFFF; + if (M3_UNLIKELY((ux == 0 && u.i[0] == 0) || ux > 0x433FFFFF)) { + return arg; + } + return (double)lrint(arg); +} + +//TODO +static inline +uint64_t strtoull(const char* str, char** endptr, int base) { + return 0; +} + +#endif + +/* + * Rotr, Rotl + */ + +static inline +u32 rotl32(u32 n, unsigned c) { + const unsigned mask = CHAR_BIT * sizeof(n) - 1; + c &= mask & 31; + return (n << c) | (n >> ((-c) & mask)); +} + +static inline +u32 rotr32(u32 n, unsigned c) { + const unsigned mask = CHAR_BIT * sizeof(n) - 1; + c &= mask & 31; + return (n >> c) | (n << ((-c) & mask)); +} + +static inline +u64 rotl64(u64 n, unsigned c) { + const unsigned mask = CHAR_BIT * sizeof(n) - 1; + c &= mask & 63; + return (n << c) | (n >> ((-c) & mask)); +} + +static inline +u64 rotr64(u64 n, unsigned c) { + const unsigned mask = CHAR_BIT * sizeof(n) - 1; + c &= mask & 63; + return (n >> c) | (n << ((-c) & mask)); +} + +/* + * Integer Div, Rem + */ + +#define OP_DIV_U(RES, A, B) \ + if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ + RES = A / B; + +#define OP_REM_U(RES, A, B) \ + if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ + RES = A % B; + +// 2's complement detection +#if (INT_MIN != -INT_MAX) + + #define OP_DIV_S(RES, A, B, TYPE_MIN) \ + if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ + if (M3_UNLIKELY(B == -1 and A == TYPE_MIN)) { \ + newTrap (m3Err_trapIntegerOverflow); \ + } \ + RES = A / B; + + #define OP_REM_S(RES, A, B, TYPE_MIN) \ + if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ + if (M3_UNLIKELY(B == -1 and A == TYPE_MIN)) RES = 0; \ + else RES = A % B; + +#else + + #define OP_DIV_S(RES, A, B, TYPE_MIN) OP_DIV_U(RES, A, B) + #define OP_REM_S(RES, A, B, TYPE_MIN) OP_REM_U(RES, A, B) + +#endif + +/* + * Trunc + */ + +#define OP_TRUNC(RES, A, TYPE, RMIN, RMAX) \ + if (M3_UNLIKELY(isnan(A))) { \ + newTrap (m3Err_trapIntegerConversion); \ + } \ + if (M3_UNLIKELY(A <= RMIN or A >= RMAX)) { \ + newTrap (m3Err_trapIntegerOverflow); \ + } \ + RES = (TYPE)A; + + +#define OP_I32_TRUNC_F32(RES, A) OP_TRUNC(RES, A, i32, -2147483904.0f, 2147483648.0f) +#define OP_U32_TRUNC_F32(RES, A) OP_TRUNC(RES, A, u32, -1.0f, 4294967296.0f) +#define OP_I32_TRUNC_F64(RES, A) OP_TRUNC(RES, A, i32, -2147483649.0 , 2147483648.0 ) +#define OP_U32_TRUNC_F64(RES, A) OP_TRUNC(RES, A, u32, -1.0 , 4294967296.0 ) + +#define OP_I64_TRUNC_F32(RES, A) OP_TRUNC(RES, A, i64, -9223373136366403584.0f, 9223372036854775808.0f) +#define OP_U64_TRUNC_F32(RES, A) OP_TRUNC(RES, A, u64, -1.0f, 18446744073709551616.0f) +#define OP_I64_TRUNC_F64(RES, A) OP_TRUNC(RES, A, i64, -9223372036854777856.0 , 9223372036854775808.0 ) +#define OP_U64_TRUNC_F64(RES, A) OP_TRUNC(RES, A, u64, -1.0 , 18446744073709551616.0 ) + +#define OP_TRUNC_SAT(RES, A, TYPE, RMIN, RMAX, IMIN, IMAX) \ + if (M3_UNLIKELY(isnan(A))) { \ + RES = 0; \ + } else if (M3_UNLIKELY(A <= RMIN)) { \ + RES = IMIN; \ + } else if (M3_UNLIKELY(A >= RMAX)) { \ + RES = IMAX; \ + } else { \ + RES = (TYPE)A; \ + } + +#define OP_I32_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, i32, -2147483904.0f, 2147483648.0f, INT32_MIN, INT32_MAX) +#define OP_U32_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, u32, -1.0f, 4294967296.0f, 0UL, UINT32_MAX) +#define OP_I32_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, i32, -2147483649.0 , 2147483648.0, INT32_MIN, INT32_MAX) +#define OP_U32_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, u32, -1.0 , 4294967296.0, 0UL, UINT32_MAX) + +#define OP_I64_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, i64, -9223373136366403584.0f, 9223372036854775808.0f, INT64_MIN, INT64_MAX) +#define OP_U64_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, u64, -1.0f, 18446744073709551616.0f, 0ULL, UINT64_MAX) +#define OP_I64_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, i64, -9223372036854777856.0 , 9223372036854775808.0, INT64_MIN, INT64_MAX) +#define OP_U64_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, u64, -1.0 , 18446744073709551616.0, 0ULL, UINT64_MAX) + +/* + * Min, Max + */ + +#if d_m3HasFloat + +#include + +static inline +f32 min_f32(f32 a, f32 b) { + if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN; + if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? a : b; + return a > b ? b : a; +} + +static inline +f32 max_f32(f32 a, f32 b) { + if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN; + if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? b : a; + return a > b ? a : b; +} + +static inline +f64 min_f64(f64 a, f64 b) { + if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN; + if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? a : b; + return a > b ? b : a; +} + +static inline +f64 max_f64(f64 a, f64 b) { + if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN; + if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? b : a; + return a > b ? a : b; +} +#endif + +#endif // m3_math_utils_h diff --git a/cpp/m3_module.c b/cpp/m3_module.c new file mode 100644 index 0000000..5742db6 --- /dev/null +++ b/cpp/m3_module.c @@ -0,0 +1,171 @@ +// +// m3_module.c +// +// Created by Steven Massey on 5/7/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#include "m3_env.h" +#include "m3_exception.h" + + +void Module_FreeFunctions (IM3Module i_module) +{ + for (u32 i = 0; i < i_module->numFunctions; ++i) + { + IM3Function func = & i_module->functions [i]; + Function_Release (func); + } +} + + +void m3_FreeModule (IM3Module i_module) +{ + if (i_module) + { + m3log (module, "freeing module: %s (funcs: %d; segments: %d)", + i_module->name, i_module->numFunctions, i_module->numDataSegments); + + Module_FreeFunctions (i_module); + + m3_Free (i_module->functions); + //m3_Free (i_module->imports); + m3_Free (i_module->funcTypes); + m3_Free (i_module->dataSegments); + m3_Free (i_module->table0); + + for (u32 i = 0; i < i_module->numGlobals; ++i) + { + m3_Free (i_module->globals[i].name); + FreeImportInfo(&(i_module->globals[i].import)); + } + m3_Free (i_module->globals); + + m3_Free (i_module); + } +} + + +M3Result Module_AddGlobal (IM3Module io_module, IM3Global * o_global, u8 i_type, bool i_mutable, bool i_isImported) +{ +_try { + u32 index = io_module->numGlobals++; + io_module->globals = m3_ReallocArray (M3Global, io_module->globals, io_module->numGlobals, index); + _throwifnull (io_module->globals); + M3Global * global = & io_module->globals [index]; + + global->type = i_type; + global->imported = i_isImported; + global->isMutable = i_mutable; + + if (o_global) + * o_global = global; + +} _catch: + return result; +} + +M3Result Module_PreallocFunctions (IM3Module io_module, u32 i_totalFunctions) +{ +_try { + if (i_totalFunctions > io_module->allFunctions) { + io_module->functions = m3_ReallocArray (M3Function, io_module->functions, i_totalFunctions, io_module->allFunctions); + io_module->allFunctions = i_totalFunctions; + _throwifnull (io_module->functions); + } +} _catch: + return result; +} + +M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo) +{ +_try { + + u32 index = io_module->numFunctions++; +_ (Module_PreallocFunctions(io_module, io_module->numFunctions)); + + _throwif ("type sig index out of bounds", i_typeIndex >= io_module->numFuncTypes); + + IM3FuncType ft = io_module->funcTypes [i_typeIndex]; + + IM3Function func = Module_GetFunction (io_module, index); + func->funcType = ft; + +# ifdef DEBUG + func->index = index; +# endif + + if (i_importInfo and func->numNames == 0) + { + func->import = * i_importInfo; + func->names[0] = i_importInfo->fieldUtf8; + func->numNames = 1; + } + + m3log (module, " added function: %3d; sig: %d", index, i_typeIndex); + +} _catch: + return result; +} + +#ifdef DEBUG +void Module_GenerateNames (IM3Module i_module) +{ + for (u32 i = 0; i < i_module->numFunctions; ++i) + { + IM3Function func = & i_module->functions [i]; + + if (func->numNames == 0) + { + char* buff = m3_AllocArray(char, 16); + snprintf(buff, 16, "$func%d", i); + func->names[0] = buff; + func->numNames = 1; + } + } + for (u32 i = 0; i < i_module->numGlobals; ++i) + { + IM3Global global = & i_module->globals [i]; + + if (global->name == NULL) + { + char* buff = m3_AllocArray(char, 16); + snprintf(buff, 16, "$global%d", i); + global->name = buff; + } + } +} +#endif + +IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex) +{ + IM3Function func = NULL; + + if (i_functionIndex < i_module->numFunctions) + { + func = & i_module->functions [i_functionIndex]; + //func->module = i_module; + } + + return func; +} + + +const char* m3_GetModuleName (IM3Module i_module) +{ + if (!i_module || !i_module->name) + return ".unnamed"; + + return i_module->name; +} + +void m3_SetModuleName (IM3Module i_module, const char* name) +{ + if (i_module) i_module->name = name; +} + +IM3Runtime m3_GetModuleRuntime (IM3Module i_module) +{ + return i_module ? i_module->runtime : NULL; +} + diff --git a/cpp/m3_parse.c b/cpp/m3_parse.c new file mode 100644 index 0000000..bdc3ad5 --- /dev/null +++ b/cpp/m3_parse.c @@ -0,0 +1,650 @@ +// +// m3_parse.c +// +// Created by Steven Massey on 4/19/19. +// Copyright © 2019 Steven Massey. All rights reserved. +// + +#include "m3_env.h" +#include "m3_compile.h" +#include "m3_exception.h" +#include "m3_info.h" + + +M3Result ParseType_Table (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_none; + + return result; +} + + +M3Result ParseType_Memory (M3MemoryInfo * o_memory, bytes_t * io_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_none; + + u8 flag; + +_ (ReadLEB_u7 (& flag, io_bytes, i_end)); // really a u1 +_ (ReadLEB_u32 (& o_memory->initPages, io_bytes, i_end)); + + o_memory->maxPages = 0; + if (flag) +_ (ReadLEB_u32 (& o_memory->maxPages, io_bytes, i_end)); + + _catch: return result; +} + + +M3Result ParseSection_Type (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) +{ + IM3FuncType ftype = NULL; + +_try { + u32 numTypes; +_ (ReadLEB_u32 (& numTypes, & i_bytes, i_end)); m3log (parse, "** Type [%d]", numTypes); + + _throwif("too many types", numTypes > d_m3MaxSaneTypesCount); + + if (numTypes) + { + // table of IM3FuncType (that point to the actual M3FuncType struct in the Environment) + io_module->funcTypes = m3_AllocArray (IM3FuncType, numTypes); + _throwifnull (io_module->funcTypes); + io_module->numFuncTypes = numTypes; + + for (u32 i = 0; i < numTypes; ++i) + { + i8 form; +_ (ReadLEB_i7 (& form, & i_bytes, i_end)); + _throwif (m3Err_wasmMalformed, form != -32); // for Wasm MVP + + u32 numArgs; +_ (ReadLEB_u32 (& numArgs, & i_bytes, i_end)); + + _throwif (m3Err_tooManyArgsRets, numArgs > d_m3MaxSaneFunctionArgRetCount); +#if defined(M3_COMPILER_MSVC) + u8 argTypes [d_m3MaxSaneFunctionArgRetCount]; +#else + u8 argTypes[numArgs+1]; // make ubsan happy +#endif + for (u32 a = 0; a < numArgs; ++a) + { + i8 wasmType; + u8 argType; +_ (ReadLEB_i7 (& wasmType, & i_bytes, i_end)); +_ (NormalizeType (& argType, wasmType)); + + argTypes[a] = argType; + } + + u32 numRets; +_ (ReadLEB_u32 (& numRets, & i_bytes, i_end)); + _throwif (m3Err_tooManyArgsRets, (u64)(numRets) + numArgs > d_m3MaxSaneFunctionArgRetCount); + +_ (AllocFuncType (& ftype, numRets + numArgs)); + ftype->numArgs = numArgs; + ftype->numRets = numRets; + + for (u32 r = 0; r < numRets; ++r) + { + i8 wasmType; + u8 retType; +_ (ReadLEB_i7 (& wasmType, & i_bytes, i_end)); +_ (NormalizeType (& retType, wasmType)); + + ftype->types[r] = retType; + } + memcpy (ftype->types + numRets, argTypes, numArgs); m3log (parse, " type %2d: %s", i, SPrintFuncTypeSignature (ftype)); + + Environment_AddFuncType (io_module->environment, & ftype); + io_module->funcTypes [i] = ftype; + ftype = NULL; // ownership transferred to environment + } + } + +} _catch: + + if (result) + { + m3_Free (ftype); + // FIX: M3FuncTypes in the table are leaked + m3_Free (io_module->funcTypes); + io_module->numFuncTypes = 0; + } + + return result; +} + + +M3Result ParseSection_Function (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_none; + + u32 numFunctions; +_ (ReadLEB_u32 (& numFunctions, & i_bytes, i_end)); m3log (parse, "** Function [%d]", numFunctions); + + _throwif("too many functions", numFunctions > d_m3MaxSaneFunctionsCount); + +_ (Module_PreallocFunctions(io_module, io_module->numFunctions + numFunctions)); + + for (u32 i = 0; i < numFunctions; ++i) + { + u32 funcTypeIndex; +_ (ReadLEB_u32 (& funcTypeIndex, & i_bytes, i_end)); + +_ (Module_AddFunction (io_module, funcTypeIndex, NULL /* import info */)); + } + + _catch: return result; +} + + +M3Result ParseSection_Import (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_none; + + M3ImportInfo import = { NULL, NULL }, clearImport = { NULL, NULL }; + + u32 numImports; +_ (ReadLEB_u32 (& numImports, & i_bytes, i_end)); m3log (parse, "** Import [%d]", numImports); + + _throwif("too many imports", numImports > d_m3MaxSaneImportsCount); + + // Most imports are functions, so we won't waste much space anyway (if any) +_ (Module_PreallocFunctions(io_module, numImports)); + + for (u32 i = 0; i < numImports; ++i) + { + u8 importKind; + +_ (Read_utf8 (& import.moduleUtf8, & i_bytes, i_end)); +_ (Read_utf8 (& import.fieldUtf8, & i_bytes, i_end)); +_ (Read_u8 (& importKind, & i_bytes, i_end)); m3log (parse, " kind: %d '%s.%s' ", + (u32) importKind, import.moduleUtf8, import.fieldUtf8); + switch (importKind) + { + case d_externalKind_function: + { + u32 typeIndex; +_ (ReadLEB_u32 (& typeIndex, & i_bytes, i_end)) + +_ (Module_AddFunction (io_module, typeIndex, & import)) + import = clearImport; + + io_module->numFuncImports++; + } + break; + + case d_externalKind_table: +// result = ParseType_Table (& i_bytes, i_end); + break; + + case d_externalKind_memory: + { +_ (ParseType_Memory (& io_module->memoryInfo, & i_bytes, i_end)); + io_module->memoryImported = true; + } + break; + + case d_externalKind_global: + { + i8 waType; + u8 type, isMutable; + +_ (ReadLEB_i7 (& waType, & i_bytes, i_end)); +_ (NormalizeType (& type, waType)); +_ (ReadLEB_u7 (& isMutable, & i_bytes, i_end)); m3log (parse, " global: %s mutable=%d", c_waTypes [type], (u32) isMutable); + + IM3Global global; +_ (Module_AddGlobal (io_module, & global, type, isMutable, true /* isImport */)); + global->import = import; + import = clearImport; + } + break; + + default: + _throw (m3Err_wasmMalformed); + } + + FreeImportInfo (& import); + } + + _catch: + + FreeImportInfo (& import); + + return result; +} + + +M3Result ParseSection_Export (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_none; + const char * utf8 = NULL; + + u32 numExports; +_ (ReadLEB_u32 (& numExports, & i_bytes, i_end)); m3log (parse, "** Export [%d]", numExports); + + _throwif("too many exports", numExports > d_m3MaxSaneExportsCount); + + for (u32 i = 0; i < numExports; ++i) + { + u8 exportKind; + u32 index; + +_ (Read_utf8 (& utf8, & i_bytes, i_end)); +_ (Read_u8 (& exportKind, & i_bytes, i_end)); +_ (ReadLEB_u32 (& index, & i_bytes, i_end)); m3log (parse, " index: %3d; kind: %d; export: '%s'; ", index, (u32) exportKind, utf8); + + if (exportKind == d_externalKind_function) + { + _throwif(m3Err_wasmMalformed, index >= io_module->numFunctions); + IM3Function func = &(io_module->functions [index]); + if (func->numNames < d_m3MaxDuplicateFunctionImpl) + { + func->names[func->numNames++] = utf8; + func->export_name = utf8; + utf8 = NULL; // ownership transferred to M3Function + } + } + else if (exportKind == d_externalKind_global) + { + _throwif(m3Err_wasmMalformed, index >= io_module->numGlobals); + IM3Global global = &(io_module->globals [index]); + m3_Free (global->name); + global->name = utf8; + utf8 = NULL; // ownership transferred to M3Global + } + + m3_Free (utf8); + } + +_catch: + m3_Free (utf8); + return result; +} + + +M3Result ParseSection_Start (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_none; + + u32 startFuncIndex; +_ (ReadLEB_u32 (& startFuncIndex, & i_bytes, i_end)); m3log (parse, "** Start Function: %d", startFuncIndex); + + if (startFuncIndex < io_module->numFunctions) + { + io_module->startFunction = startFuncIndex; + } + else result = "start function index out of bounds"; + + _catch: return result; +} + + +M3Result Parse_InitExpr (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_none; + + // this doesn't generate code pages. just walks the wasm bytecode to find the end + +#if defined(d_m3PreferStaticAlloc) + static M3Compilation compilation; +#else + M3Compilation compilation; +#endif + compilation = (M3Compilation){ .runtime = NULL, .module = io_module, .wasm = * io_bytes, .wasmEnd = i_end }; + + result = CompileBlockStatements (& compilation); + + * io_bytes = compilation.wasm; + + return result; +} + + +M3Result ParseSection_Element (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_none; + + u32 numSegments; +_ (ReadLEB_u32 (& numSegments, & i_bytes, i_end)); m3log (parse, "** Element [%d]", numSegments); + + _throwif ("too many element segments", numSegments > d_m3MaxSaneElementSegments); + + io_module->elementSection = i_bytes; + io_module->elementSectionEnd = i_end; + io_module->numElementSegments = numSegments; + + _catch: return result; +} + + +M3Result ParseSection_Code (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) +{ + M3Result result; + + u32 numFunctions; +_ (ReadLEB_u32 (& numFunctions, & i_bytes, i_end)); m3log (parse, "** Code [%d]", numFunctions); + + if (numFunctions != io_module->numFunctions - io_module->numFuncImports) + { + _throw ("mismatched function count in code section"); + } + + for (u32 f = 0; f < numFunctions; ++f) + { + const u8 * start = i_bytes; + + u32 size; +_ (ReadLEB_u32 (& size, & i_bytes, i_end)); + + if (size) + { + const u8 * ptr = i_bytes; + i_bytes += size; + + if (i_bytes <= i_end) + { + /* + u32 numLocalBlocks; +_ (ReadLEB_u32 (& numLocalBlocks, & ptr, i_end)); m3log (parse, " code size: %-4d", size); + + u32 numLocals = 0; + + for (u32 l = 0; l < numLocalBlocks; ++l) + { + u32 varCount; + i8 wasmType; + u8 normalType; + +_ (ReadLEB_u32 (& varCount, & ptr, i_end)); +_ (ReadLEB_i7 (& wasmType, & ptr, i_end)); +_ (NormalizeType (& normalType, wasmType)); + + numLocals += varCount; m3log (parse, " %2d locals; type: '%s'", varCount, c_waTypes [normalType]); + } + */ + + IM3Function func = Module_GetFunction (io_module, f + io_module->numFuncImports); + + func->module = io_module; + func->wasm = start; + func->wasmEnd = i_bytes; + //func->ownsWasmCode = io_module->hasWasmCodeCopy; +// func->numLocals = numLocals; + } + else _throw (m3Err_wasmSectionOverrun); + } + } + + _catch: + + if (not result and i_bytes != i_end) + result = m3Err_wasmSectionUnderrun; + + return result; +} + + +M3Result ParseSection_Data (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_none; + + u32 numDataSegments; +_ (ReadLEB_u32 (& numDataSegments, & i_bytes, i_end)); m3log (parse, "** Data [%d]", numDataSegments); + + _throwif("too many data segments", numDataSegments > d_m3MaxSaneDataSegments); + + io_module->dataSegments = m3_AllocArray (M3DataSegment, numDataSegments); + _throwifnull(io_module->dataSegments); + io_module->numDataSegments = numDataSegments; + + for (u32 i = 0; i < numDataSegments; ++i) + { + M3DataSegment * segment = & io_module->dataSegments [i]; + +_ (ReadLEB_u32 (& segment->memoryRegion, & i_bytes, i_end)); + + segment->initExpr = i_bytes; +_ (Parse_InitExpr (io_module, & i_bytes, i_end)); + segment->initExprSize = (u32) (i_bytes - segment->initExpr); + + _throwif (m3Err_wasmMissingInitExpr, segment->initExprSize <= 1); + +_ (ReadLEB_u32 (& segment->size, & i_bytes, i_end)); + segment->data = i_bytes; m3log (parse, " segment [%u] memory: %u; expr-size: %d; size: %d", + i, segment->memoryRegion, segment->initExprSize, segment->size); + i_bytes += segment->size; + + _throwif("data segment underflow", i_bytes > i_end); + } + + _catch: + + return result; +} + + +M3Result ParseSection_Memory (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_none; + + // TODO: MVP; assert no memory imported + + u32 numMemories; +_ (ReadLEB_u32 (& numMemories, & i_bytes, i_end)); m3log (parse, "** Memory [%d]", numMemories); + + _throwif (m3Err_tooManyMemorySections, numMemories != 1); + + ParseType_Memory (& io_module->memoryInfo, & i_bytes, i_end); + + _catch: return result; +} + + +M3Result ParseSection_Global (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) +{ + M3Result result = m3Err_none; + + u32 numGlobals; +_ (ReadLEB_u32 (& numGlobals, & i_bytes, i_end)); m3log (parse, "** Global [%d]", numGlobals); + + _throwif("too many globals", numGlobals > d_m3MaxSaneGlobalsCount); + + for (u32 i = 0; i < numGlobals; ++i) + { + i8 waType; + u8 type, isMutable; + +_ (ReadLEB_i7 (& waType, & i_bytes, i_end)); +_ (NormalizeType (& type, waType)); +_ (ReadLEB_u7 (& isMutable, & i_bytes, i_end)); m3log (parse, " global: [%d] %s mutable: %d", i, c_waTypes [type], (u32) isMutable); + + IM3Global global; +_ (Module_AddGlobal (io_module, & global, type, isMutable, false /* isImport */)); + + global->initExpr = i_bytes; +_ (Parse_InitExpr (io_module, & i_bytes, i_end)); + global->initExprSize = (u32) (i_bytes - global->initExpr); + + _throwif (m3Err_wasmMissingInitExpr, global->initExprSize <= 1); + } + + _catch: return result; +} + + +M3Result ParseSection_Name (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) +{ + M3Result result; + + cstr_t name; + + while (i_bytes < i_end) + { + u8 nameType; + u32 payloadLength; + +_ (ReadLEB_u7 (& nameType, & i_bytes, i_end)); +_ (ReadLEB_u32 (& payloadLength, & i_bytes, i_end)); + + bytes_t start = i_bytes; + if (nameType == 1) + { + u32 numNames; +_ (ReadLEB_u32 (& numNames, & i_bytes, i_end)); + + _throwif("too many names", numNames > d_m3MaxSaneFunctionsCount); + + for (u32 i = 0; i < numNames; ++i) + { + u32 index; +_ (ReadLEB_u32 (& index, & i_bytes, i_end)); +_ (Read_utf8 (& name, & i_bytes, i_end)); + + if (index < io_module->numFunctions) + { + IM3Function func = &(io_module->functions [index]); + if (func->numNames == 0) + { + func->names[0] = name; m3log (parse, " naming function%5d: %s", index, name); + func->numNames = 1; + name = NULL; // transfer ownership + } +// else m3log (parse, "prenamed: %s", io_module->functions [index].name); + } + + m3_Free (name); + } + } + + i_bytes = start + payloadLength; + } + + _catch: return result; +} + + +M3Result ParseSection_Custom (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) +{ + M3Result result; + + cstr_t name; +_ (Read_utf8 (& name, & i_bytes, i_end)); + m3log (parse, "** Custom: '%s'", name); + if (strcmp (name, "name") == 0) { +_ (ParseSection_Name(io_module, i_bytes, i_end)); + } else if (io_module->environment->customSectionHandler) { +_ (io_module->environment->customSectionHandler(io_module, name, i_bytes, i_end)); + } + + m3_Free (name); + + _catch: return result; +} + + +M3Result ParseModuleSection (M3Module * o_module, u8 i_sectionType, bytes_t i_bytes, u32 i_numBytes) +{ + M3Result result = m3Err_none; + + typedef M3Result (* M3Parser) (M3Module *, bytes_t, cbytes_t); + + static M3Parser s_parsers [] = + { + ParseSection_Custom, // 0 + ParseSection_Type, // 1 + ParseSection_Import, // 2 + ParseSection_Function, // 3 + NULL, // 4: TODO Table + ParseSection_Memory, // 5 + ParseSection_Global, // 6 + ParseSection_Export, // 7 + ParseSection_Start, // 8 + ParseSection_Element, // 9 + ParseSection_Code, // 10 + ParseSection_Data, // 11 + NULL, // 12: TODO DataCount + }; + + M3Parser parser = NULL; + + if (i_sectionType <= 12) + parser = s_parsers [i_sectionType]; + + if (parser) + { + cbytes_t end = i_bytes + i_numBytes; + result = parser (o_module, i_bytes, end); + } + else + { + m3log (parse, " skipped section type: %d", (u32) i_sectionType); + } + + return result; +} + + +M3Result m3_ParseModule (IM3Environment i_environment, IM3Module * o_module, cbytes_t i_bytes, u32 i_numBytes) +{ + IM3Module module; m3log (parse, "load module: %d bytes", i_numBytes); +_try { + module = m3_AllocStruct (M3Module); + _throwifnull (module); + module->name = ".unnamed"; m3log (parse, "load module: %d bytes", i_numBytes); + module->startFunction = -1; + //module->hasWasmCodeCopy = false; + module->environment = i_environment; + + const u8 * pos = i_bytes; + const u8 * end = pos + i_numBytes; + + module->wasmStart = pos; + module->wasmEnd = end; + + u32 magic, version; +_ (Read_u32 (& magic, & pos, end)); +_ (Read_u32 (& version, & pos, end)); + + _throwif (m3Err_wasmMalformed, magic != 0x6d736100); + _throwif (m3Err_incompatibleWasmVersion, version != 1); + + static const u8 sectionsOrder[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 10, 11, 0 }; // 0 is a placeholder + u8 expectedSection = 0; + + while (pos < end) + { + u8 section; +_ (ReadLEB_u7 (& section, & pos, end)); + + if (section != 0) { + // Ensure sections appear only once and in order + while (sectionsOrder[expectedSection++] != section) { + _throwif(m3Err_misorderedWasmSection, expectedSection >= 12); + } + } + + u32 sectionLength; +_ (ReadLEB_u32 (& sectionLength, & pos, end)); + _throwif(m3Err_wasmMalformed, pos + sectionLength > end); + +_ (ParseModuleSection (module, section, pos, sectionLength)); + + pos += sectionLength; + } + +} _catch: + + if (result) + { + m3_FreeModule (module); + module = NULL; + } + + * o_module = module; + + return result; +} diff --git a/cpp/react-native-webassembly.cpp b/cpp/react-native-webassembly.cpp new file mode 100644 index 0000000..f739322 --- /dev/null +++ b/cpp/react-native-webassembly.cpp @@ -0,0 +1,130 @@ +#include "react-native-webassembly.h" + +#include +#include +#include +#include +#include +#include + +#include "wasm3_cpp.h" + +int sum(int a, int b) +{ + return a + b; +} + +void * ext_memcpy (void* dst, const void* arg, int32_t size) +{ + return memcpy(dst, arg, (size_t) size); +} + +char* double_to_c_string(double value) +{ + std::stringstream stream; + stream << std::fixed << std::setprecision(16) << value; + std::string s = stream.str(); + return s.data(); +} + +typedef void (*t_v_i)(int32_t); +typedef void (*t_v_ii)(int32_t, int32_t); + +std::map RUNTIMES; + +namespace webassembly { + double instantiate(RNWebassemblyInstantiateParams* a) { + + std::string iid = a->iid; + uint32_t stackSizeInBytes = (uint32_t)a->stackSizeInBytes; + + /* Wasm module can also be loaded from an array */ + try { + wasm3::environment env; + wasm3::runtime runtime = env.new_runtime(stackSizeInBytes); + + wasm3::module mod = env.parse_module(a->bufferSource, a->bufferSourceLength); + + runtime.load(mod); + + std::vector::value_type *rawFunctions = a->rawFunctions->data(); + + for (int i = 0; i < a->rawFunctions->size(); ++i) { + std::string name = rawFunctions[i]; + + /* TODO: These require implementation. */ + + /* v(i) */ + t_v_i v_i = [](int32_t p) { + throw std::runtime_error(std::string("v(i)")); + }; + + /* v(ii) */ + t_v_ii v_ii = [](int32_t p, int32_t q) { + throw std::runtime_error(std::string("v(ii)")); + }; + + // HACK: How to propagate instantiation back to runtime? + try { mod.link("*", name.data(), v_i); continue; } catch (wasm3::error &e) {} + try { mod.link("*", name.data(), v_ii); continue; } catch (wasm3::error &e) {} + + throw std::runtime_error(std::string("Unsupported signature for " + name + ".")); + } + + mod.compile(); + + RUNTIMES.insert(std::make_pair(iid, runtime)); + + return 0; + } catch(wasm3::error &e) { + return 1; + } + } + + double invoke(RNWebassemblyInvokeParams* a) { + + std::string iid = a->iid; + std::string func = a->func; + std::vector* args = a->args; + std::vector* res = a->res; + + std::map::iterator it = RUNTIMES.find(iid); + + if (it == RUNTIMES.end()) + throw std::runtime_error("Unable to invoke."); + + wasm3::runtime runtime = it->second; + wasm3::function fn = runtime.find_function(func.data()); + + std::vector vec; + + for (uint32_t i = 0; i < fn.GetArgCount(); i += 1) { + vec.push_back((*args)[i]); + } + + if (fn.GetRetCount() > 1) throw std::runtime_error("Unable to invoke."); + + if (fn.GetRetCount() == 0) { + fn.call_argv(vec); + return 0; + } + + M3ValueType type = fn.GetRetType(0); + + if (type == c_m3Type_i32) { + res->push_back(std::to_string(fn.call_argv(vec))); + return 0; + } else if (type == c_m3Type_i64) { + res->push_back(std::to_string(fn.call_argv(vec))); + return 0; + } else if (type == c_m3Type_f32) { + res->push_back(std::string(double_to_c_string(fn.call_argv(vec)))); + return 0; + } else if (type == c_m3Type_f64) { + res->push_back(std::string(double_to_c_string(fn.call_argv(vec)))); + return 0; + } + + throw std::runtime_error("Failed to invoke."); + } +} diff --git a/cpp/react-native-webassembly.h b/cpp/react-native-webassembly.h new file mode 100644 index 0000000..aea27c9 --- /dev/null +++ b/cpp/react-native-webassembly.h @@ -0,0 +1,29 @@ +#ifndef WEBASSEMBLY_H +#define WEBASSEMBLY_H + +#include +#include +#include + +struct RNWebassemblyInstantiateParams { + std::string iid; + uint8_t* bufferSource; + size_t bufferSourceLength; + double stackSizeInBytes; + + std::vector* rawFunctions; +}; + +struct RNWebassemblyInvokeParams { + std::string iid; + std::string func; + std::vector* args; + std::vector* res; +}; + +namespace webassembly { + double instantiate(RNWebassemblyInstantiateParams* a); + double invoke(RNWebassemblyInvokeParams* a); +} + +#endif /* WEBASSEMBLY_H */ diff --git a/cpp/wasm3.h b/cpp/wasm3.h new file mode 100644 index 0000000..0d8554f --- /dev/null +++ b/cpp/wasm3.h @@ -0,0 +1,372 @@ +// +// Wasm3, high performance WebAssembly interpreter +// +// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy. +// All rights reserved. +// + +#ifndef wasm3_h +#define wasm3_h + +#define M3_VERSION_MAJOR 0 +#define M3_VERSION_MINOR 5 +#define M3_VERSION_REV 0 +#define M3_VERSION "0.5.0" + +#include +#include +#include +#include + +#include "wasm3_defs.h" + +// Constants +#define M3_BACKTRACE_TRUNCATED (IM3BacktraceFrame)(SIZE_MAX) + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef const char * M3Result; + +struct M3Environment; typedef struct M3Environment * IM3Environment; +struct M3Runtime; typedef struct M3Runtime * IM3Runtime; +struct M3Module; typedef struct M3Module * IM3Module; +struct M3Function; typedef struct M3Function * IM3Function; +struct M3Global; typedef struct M3Global * IM3Global; + +typedef struct M3ErrorInfo +{ + M3Result result; + + IM3Runtime runtime; + IM3Module module; + IM3Function function; + + const char * file; + uint32_t line; + + const char * message; +} M3ErrorInfo; + +typedef struct M3BacktraceFrame +{ + uint32_t moduleOffset; + IM3Function function; + + struct M3BacktraceFrame * next; +} +M3BacktraceFrame, * IM3BacktraceFrame; + +typedef struct M3BacktraceInfo +{ + IM3BacktraceFrame frames; + IM3BacktraceFrame lastFrame; // can be M3_BACKTRACE_TRUNCATED +} +M3BacktraceInfo, * IM3BacktraceInfo; + + +typedef enum M3ValueType +{ + c_m3Type_none = 0, + c_m3Type_i32 = 1, + c_m3Type_i64 = 2, + c_m3Type_f32 = 3, + c_m3Type_f64 = 4, + + c_m3Type_unknown +} M3ValueType; + +typedef struct M3TaggedValue +{ + M3ValueType type; + union M3ValueUnion + { + uint32_t i32; + uint64_t i64; + float f32; + double f64; + } value; +} +M3TaggedValue, * IM3TaggedValue; + +typedef struct M3ImportInfo +{ + const char * moduleUtf8; + const char * fieldUtf8; +} +M3ImportInfo, * IM3ImportInfo; + + +typedef struct M3ImportContext +{ + void * userdata; + IM3Function function; +} +M3ImportContext, * IM3ImportContext; + +// ------------------------------------------------------------------------------------------------------------------------------- +// error codes +// ------------------------------------------------------------------------------------------------------------------------------- + +# if defined(M3_IMPLEMENT_ERROR_STRINGS) +# if defined(__cplusplus) +# define d_m3ErrorConst(LABEL, STRING) extern const M3Result m3Err_##LABEL = { STRING }; +# else +# define d_m3ErrorConst(LABEL, STRING) const M3Result m3Err_##LABEL = { STRING }; +# endif +# else +# define d_m3ErrorConst(LABEL, STRING) extern const M3Result m3Err_##LABEL; +# endif + +// ------------------------------------------------------------------------------------------------------------------------------- + +d_m3ErrorConst (none, NULL) + +// general errors +d_m3ErrorConst (mallocFailed, "memory allocation failed") + +// parse errors +d_m3ErrorConst (incompatibleWasmVersion, "incompatible Wasm binary version") +d_m3ErrorConst (wasmMalformed, "malformed Wasm binary") +d_m3ErrorConst (misorderedWasmSection, "out of order Wasm section") +d_m3ErrorConst (wasmUnderrun, "underrun while parsing Wasm binary") +d_m3ErrorConst (wasmOverrun, "overrun while parsing Wasm binary") +d_m3ErrorConst (wasmMissingInitExpr, "missing init_expr in Wasm binary") +d_m3ErrorConst (lebOverflow, "LEB encoded value overflow") +d_m3ErrorConst (missingUTF8, "invalid length UTF-8 string") +d_m3ErrorConst (wasmSectionUnderrun, "section underrun while parsing Wasm binary") +d_m3ErrorConst (wasmSectionOverrun, "section overrun while parsing Wasm binary") +d_m3ErrorConst (invalidTypeId, "unknown value_type") +d_m3ErrorConst (tooManyMemorySections, "only one memory per module is supported") +d_m3ErrorConst (tooManyArgsRets, "too many arguments or return values") + +// link errors +d_m3ErrorConst (moduleNotLinked, "attempting to use module that is not loaded") +d_m3ErrorConst (moduleAlreadyLinked, "attempting to bind module to multiple runtimes") +d_m3ErrorConst (functionLookupFailed, "function lookup failed") +d_m3ErrorConst (functionImportMissing, "missing imported function") + +d_m3ErrorConst (malformedFunctionSignature, "malformed function signature") + +// compilation errors +d_m3ErrorConst (noCompiler, "no compiler found for opcode") +d_m3ErrorConst (unknownOpcode, "unknown opcode") +d_m3ErrorConst (restrictedOpcode, "restricted opcode") +d_m3ErrorConst (functionStackOverflow, "compiling function overran its stack height limit") +d_m3ErrorConst (functionStackUnderrun, "compiling function underran the stack") +d_m3ErrorConst (mallocFailedCodePage, "memory allocation failed when acquiring a new M3 code page") +d_m3ErrorConst (settingImmutableGlobal, "attempting to set an immutable global") +d_m3ErrorConst (typeMismatch, "incorrect type on stack") +d_m3ErrorConst (typeCountMismatch, "incorrect value count on stack") + +// runtime errors +d_m3ErrorConst (missingCompiledCode, "function is missing compiled m3 code") +d_m3ErrorConst (wasmMemoryOverflow, "runtime ran out of memory") +d_m3ErrorConst (globalMemoryNotAllocated, "global memory is missing from a module") +d_m3ErrorConst (globaIndexOutOfBounds, "global index is too large") +d_m3ErrorConst (argumentCountMismatch, "argument count mismatch") +d_m3ErrorConst (argumentTypeMismatch, "argument type mismatch") +d_m3ErrorConst (globalLookupFailed, "global lookup failed") +d_m3ErrorConst (globalTypeMismatch, "global type mismatch") +d_m3ErrorConst (globalNotMutable, "global is not mutable") + +// traps +d_m3ErrorConst (trapOutOfBoundsMemoryAccess, "[trap] out of bounds memory access") +d_m3ErrorConst (trapDivisionByZero, "[trap] integer divide by zero") +d_m3ErrorConst (trapIntegerOverflow, "[trap] integer overflow") +d_m3ErrorConst (trapIntegerConversion, "[trap] invalid conversion to integer") +d_m3ErrorConst (trapIndirectCallTypeMismatch, "[trap] indirect call type mismatch") +d_m3ErrorConst (trapTableIndexOutOfRange, "[trap] undefined element") +d_m3ErrorConst (trapTableElementIsNull, "[trap] null table element") +d_m3ErrorConst (trapExit, "[trap] program called exit") +d_m3ErrorConst (trapAbort, "[trap] program called abort") +d_m3ErrorConst (trapUnreachable, "[trap] unreachable executed") +d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") + + +//------------------------------------------------------------------------------------------------------------------------------- +// configuration, can be found in m3_config.h, m3_config_platforms.h, m3_core.h) +//------------------------------------------------------------------------------------------------------------------------------- + +//------------------------------------------------------------------------------------------------------------------------------- +// global environment than can host multiple runtimes +//------------------------------------------------------------------------------------------------------------------------------- + IM3Environment m3_NewEnvironment (void); + + void m3_FreeEnvironment (IM3Environment i_environment); + + typedef M3Result (* M3SectionHandler) (IM3Module i_module, const char* name, const uint8_t * start, const uint8_t * end); + + void m3_SetCustomSectionHandler (IM3Environment i_environment, M3SectionHandler i_handler); + + +//------------------------------------------------------------------------------------------------------------------------------- +// execution context +//------------------------------------------------------------------------------------------------------------------------------- + + IM3Runtime m3_NewRuntime (IM3Environment io_environment, + uint32_t i_stackSizeInBytes, + void * i_userdata); + + void m3_FreeRuntime (IM3Runtime i_runtime); + + // Wasm currently only supports one memory region. i_memoryIndex should be zero. + uint8_t * m3_GetMemory (IM3Runtime i_runtime, + uint32_t * o_memorySizeInBytes, + uint32_t i_memoryIndex); + + // This is used internally by Raw Function helpers + uint32_t m3_GetMemorySize (IM3Runtime i_runtime); + + void * m3_GetUserData (IM3Runtime i_runtime); + + +//------------------------------------------------------------------------------------------------------------------------------- +// modules +//------------------------------------------------------------------------------------------------------------------------------- + + // i_wasmBytes data must be persistent during the lifetime of the module + M3Result m3_ParseModule (IM3Environment i_environment, + IM3Module * o_module, + const uint8_t * const i_wasmBytes, + uint32_t i_numWasmBytes); + + // Only modules not loaded into a M3Runtime need to be freed. A module is considered unloaded if + // a. m3_LoadModule has not yet been called on that module. Or, + // b. m3_LoadModule returned a result. + void m3_FreeModule (IM3Module i_module); + + // LoadModule transfers ownership of a module to the runtime. Do not free modules once successfully loaded into the runtime + M3Result m3_LoadModule (IM3Runtime io_runtime, IM3Module io_module); + + // Optional, compiles all functions in the module + M3Result m3_CompileModule (IM3Module io_module); + + // Calling m3_RunStart is optional + M3Result m3_RunStart (IM3Module i_module); + + // Arguments and return values are passed in and out through the stack pointer _sp. + // Placeholder return value slots are first and arguments after. So, the first argument is at _sp [numReturns] + // Return values should be written into _sp [0] to _sp [num_returns - 1] + typedef const void * (* M3RawCall) (IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem); + + M3Result m3_LinkRawFunction (IM3Module io_module, + const char * const i_moduleName, + const char * const i_functionName, + const char * const i_signature, + M3RawCall i_function); + + M3Result m3_LinkRawFunctionEx (IM3Module io_module, + const char * const i_moduleName, + const char * const i_functionName, + const char * const i_signature, + M3RawCall i_function, + const void * i_userdata); + + const char* m3_GetModuleName (IM3Module i_module); + void m3_SetModuleName (IM3Module i_module, const char* name); + IM3Runtime m3_GetModuleRuntime (IM3Module i_module); + +//------------------------------------------------------------------------------------------------------------------------------- +// globals +//------------------------------------------------------------------------------------------------------------------------------- + IM3Global m3_FindGlobal (IM3Module io_module, + const char * const i_globalName); + + M3Result m3_GetGlobal (IM3Global i_global, + IM3TaggedValue o_value); + + M3Result m3_SetGlobal (IM3Global i_global, + const IM3TaggedValue i_value); + + M3ValueType m3_GetGlobalType (IM3Global i_global); + +//------------------------------------------------------------------------------------------------------------------------------- +// functions +//------------------------------------------------------------------------------------------------------------------------------- + M3Result m3_Yield (void); + + // o_function is valid during the lifetime of the originating runtime + M3Result m3_FindFunction (IM3Function * o_function, + IM3Runtime i_runtime, + const char * const i_functionName); + + uint32_t m3_GetArgCount (IM3Function i_function); + uint32_t m3_GetRetCount (IM3Function i_function); + M3ValueType m3_GetArgType (IM3Function i_function, uint32_t i_index); + M3ValueType m3_GetRetType (IM3Function i_function, uint32_t i_index); + + M3Result m3_CallV (IM3Function i_function, ...); + M3Result m3_CallVL (IM3Function i_function, va_list i_args); + M3Result m3_Call (IM3Function i_function, uint32_t i_argc, const void * i_argptrs[]); + M3Result m3_CallArgv (IM3Function i_function, uint32_t i_argc, const char * i_argv[]); + + M3Result m3_GetResultsV (IM3Function i_function, ...); + M3Result m3_GetResultsVL (IM3Function i_function, va_list o_rets); + M3Result m3_GetResults (IM3Function i_function, uint32_t i_retc, const void * o_retptrs[]); + + + void m3_GetErrorInfo (IM3Runtime i_runtime, M3ErrorInfo* o_info); + void m3_ResetErrorInfo (IM3Runtime i_runtime); + + const char* m3_GetFunctionName (IM3Function i_function); + IM3Module m3_GetFunctionModule (IM3Function i_function); + +//------------------------------------------------------------------------------------------------------------------------------- +// debug info +//------------------------------------------------------------------------------------------------------------------------------- + + void m3_PrintRuntimeInfo (IM3Runtime i_runtime); + void m3_PrintM3Info (void); + void m3_PrintProfilerInfo (void); + + // The runtime owns the backtrace, do not free the backtrace you obtain. Returns NULL if there's no backtrace. + IM3BacktraceInfo m3_GetBacktrace (IM3Runtime i_runtime); + +//------------------------------------------------------------------------------------------------------------------------------- +// raw function definition helpers +//------------------------------------------------------------------------------------------------------------------------------- + +# define m3ApiOffsetToPtr(offset) (void*)((uint8_t*)_mem + (uint32_t)(offset)) +# define m3ApiPtrToOffset(ptr) (uint32_t)((uint8_t*)ptr - (uint8_t*)_mem) + +# define m3ApiReturnType(TYPE) TYPE* raw_return = ((TYPE*) (_sp++)); +# define m3ApiMultiValueReturnType(TYPE, NAME) TYPE* NAME = ((TYPE*) (_sp++)); +# define m3ApiGetArg(TYPE, NAME) TYPE NAME = * ((TYPE *) (_sp++)); +# define m3ApiGetArgMem(TYPE, NAME) TYPE NAME = (TYPE)m3ApiOffsetToPtr(* ((uint32_t *) (_sp++))); + +# define m3ApiIsNullPtr(addr) ((void*)(addr) <= _mem) +# define m3ApiCheckMem(addr, len) { if (M3_UNLIKELY(((void*)(addr) < _mem) || ((uint64_t)(uintptr_t)(addr) + (len)) > ((uint64_t)(uintptr_t)(_mem)+m3_GetMemorySize(runtime)))) m3ApiTrap(m3Err_trapOutOfBoundsMemoryAccess); } + +# define m3ApiRawFunction(NAME) const void * NAME (IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem) +# define m3ApiReturn(VALUE) { *raw_return = (VALUE); return m3Err_none;} +# define m3ApiMultiValueReturn(NAME, VALUE) { *NAME = (VALUE); } +# define m3ApiTrap(VALUE) { return VALUE; } +# define m3ApiSuccess() { return m3Err_none; } + +# if defined(M3_BIG_ENDIAN) +# define m3ApiReadMem8(ptr) (* (uint8_t *)(ptr)) +# define m3ApiReadMem16(ptr) m3_bswap16((* (uint16_t *)(ptr))) +# define m3ApiReadMem32(ptr) m3_bswap32((* (uint32_t *)(ptr))) +# define m3ApiReadMem64(ptr) m3_bswap64((* (uint64_t *)(ptr))) +# define m3ApiWriteMem8(ptr, val) { * (uint8_t *)(ptr) = (val); } +# define m3ApiWriteMem16(ptr, val) { * (uint16_t *)(ptr) = m3_bswap16((val)); } +# define m3ApiWriteMem32(ptr, val) { * (uint32_t *)(ptr) = m3_bswap32((val)); } +# define m3ApiWriteMem64(ptr, val) { * (uint64_t *)(ptr) = m3_bswap64((val)); } +# else +# define m3ApiReadMem8(ptr) (* (uint8_t *)(ptr)) +# define m3ApiReadMem16(ptr) (* (uint16_t *)(ptr)) +# define m3ApiReadMem32(ptr) (* (uint32_t *)(ptr)) +# define m3ApiReadMem64(ptr) (* (uint64_t *)(ptr)) +# define m3ApiWriteMem8(ptr, val) { * (uint8_t *)(ptr) = (val); } +# define m3ApiWriteMem16(ptr, val) { * (uint16_t *)(ptr) = (val); } +# define m3ApiWriteMem32(ptr, val) { * (uint32_t *)(ptr) = (val); } +# define m3ApiWriteMem64(ptr, val) { * (uint64_t *)(ptr) = (val); } +# endif + +#if defined(__cplusplus) +} +#endif + +#endif // wasm3_h diff --git a/cpp/wasm3_cpp.h b/cpp/wasm3_cpp.h new file mode 100644 index 0000000..d9294c6 --- /dev/null +++ b/cpp/wasm3_cpp.h @@ -0,0 +1,491 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wasm3.h" + + +namespace wasm3 { + /** @cond */ + namespace detail { + typedef uint64_t *stack_type; + typedef void *mem_type; + template struct first_type { typedef T type; }; + + typedef const void *(*m3_api_raw_fn)(IM3Runtime, uint64_t *, void *); + + template + void arg_from_stack(T &dest, stack_type &_sp, [[maybe_unused]] mem_type mem) { + m3ApiGetArg(T, tmp); + dest = tmp; + } + + template + void arg_from_stack(T* &dest, stack_type &_sp, [[maybe_unused]] mem_type _mem) { + m3ApiGetArgMem(T*, tmp); + dest = tmp; + }; + + template + void arg_from_stack(const T* &dest, stack_type &_sp, [[maybe_unused]] mem_type _mem) { + m3ApiGetArgMem(const T*, tmp); + dest = tmp; + }; + + template + struct m3_sig { + static const char value = c; + }; + template struct m3_type_to_sig; + + template + using is_enum_of_t = typename std::enable_if::value + && std::is_same, U>::value>::type; + + template + struct m3_type_to_sig> : m3_sig<'i'> {}; + + template + struct m3_type_to_sig> : m3_sig<'I'> {}; + + template<> struct m3_type_to_sig : m3_sig<'i'> {}; + template<> struct m3_type_to_sig : m3_sig<'I'> {}; + template<> struct m3_type_to_sig : m3_sig<'f'> {}; + template<> struct m3_type_to_sig : m3_sig<'F'> {}; + template<> struct m3_type_to_sig : m3_sig<'v'> {}; + template<> struct m3_type_to_sig : m3_sig<'*'> {}; + template<> struct m3_type_to_sig : m3_sig<'*'> {}; + + + template + struct m3_signature { + constexpr static size_t n_args = sizeof...(Args); + constexpr static const char value[n_args + 4] = { + m3_type_to_sig::value, + '(', + m3_type_to_sig::value..., + ')', + 0 + }; + }; + + template + static void get_args_from_stack(stack_type &sp, mem_type mem, std::tuple &tuple) { + std::apply([&](auto &... item) { + (arg_from_stack(item, sp, mem), ...); + }, tuple); + } + + template + struct wrap_helper; + + template + struct wrap_helper { + using Func = Ret(Args...); + static const void *wrap_fn([[maybe_unused]] IM3Runtime rt, IM3ImportContext _ctx, stack_type _sp, mem_type mem) { + std::tuple args; + // The order here matters: m3ApiReturnType should go before calling get_args_from_stack, + // since both modify `_sp`, and the return value on the stack is reserved before the arguments. + m3ApiReturnType(Ret); + get_args_from_stack(_sp, mem, args); + Func* function = reinterpret_cast(_ctx->userdata); + Ret r = std::apply(function, args); + m3ApiReturn(r); + } + }; + + template + struct wrap_helper { + using Func = void(Args...); + static const void *wrap_fn([[maybe_unused]] IM3Runtime rt, IM3ImportContext _ctx, stack_type sp, mem_type mem) { + std::tuple args; + get_args_from_stack(sp, mem, args); + Func* function = reinterpret_cast(_ctx->userdata); + std::apply(function, args); + m3ApiSuccess(); + } + }; + + template + class m3_wrapper; + + template + class m3_wrapper { + public: + static M3Result link(IM3Module io_module, + const char *const i_moduleName, + const char *const i_functionName, + Ret (*function)(Args...)) { + + return m3_LinkRawFunctionEx(io_module, i_moduleName, i_functionName, + m3_signature::value, + &wrap_helper::wrap_fn, + reinterpret_cast(function)); + } + }; + } // namespace detail + /** @endcond */ + + class module; + class runtime; + class function; + + /** + * Exception thrown for wasm3 errors. + * + * Use error:what() to get the reason as a string. + */ + class error : public std::runtime_error { + public: + explicit error(M3Result err) : std::runtime_error(err) {} + }; + + /** @cond */ + namespace detail { + static inline void check_error(M3Result err) { + if (err != m3Err_none) { + throw error(err); + } + } + } // namespace detail + /** @endcond */ + + + /** + * Wrapper for WASM3 environment. + * + * Runtimes, modules are owned by an environment. + */ + class environment { + public: + environment() { + m_env.reset(m3_NewEnvironment(), m3_FreeEnvironment); + if (m_env == nullptr) { + throw std::bad_alloc(); + } + } + + /** + * Create new runtime + * + * @param stack_size_bytes size of the WASM stack for this runtime + * @return runtime object + */ + runtime new_runtime(uint32_t stack_size_bytes); + + /** + * Parse a WASM module from file + * + * The parsed module is not loaded into any runtime. Use runtime::load to + * load the module after parsing it. + * + * @param in file (WASM binary) + * @return module object + */ + module parse_module(std::istream &in); + + /** + * Parse a WASM module from binary data + * + * @param data pointer to the start of the binary + * @param size size of the binary + * @return module object + */ + module parse_module(const uint8_t *data, size_t size); + + protected: + std::shared_ptr m_env; + }; + + /** + * Wrapper for the runtime, where modules are loaded and executed. + */ + class runtime { + public: + /** + * Load the module into runtime + * @param mod module parsed by environment::parse_module + */ + void load(module &mod); + + /** + * Get a function handle by name + * + * If the function is not found, throws an exception. + * @param name name of a function, c-string + * @return function object + */ + function find_function(const char *name); + + protected: + friend class environment; + + runtime(const std::shared_ptr &env, uint32_t stack_size_bytes) + : m_env(env) { + m_runtime.reset(m3_NewRuntime(env.get(), stack_size_bytes, nullptr), &m3_FreeRuntime); + if (m_runtime == nullptr) { + throw std::bad_alloc(); + } + } + + /* runtime extends the lifetime of the environment */ + std::shared_ptr m_env; + std::shared_ptr m_runtime; + }; + + /** + * Module object holds a webassembly module + * + * It can be constructed by parsing a WASM binary using environment::parse_module. + * Functions can be linked to the loaded module. + * Once constructed, modules can be loaded into the runtime. + */ + class module { + public: + /** + * Link an external function. + * + * Throws an exception if the module doesn't reference a function with the given name. + * + * @tparam Func Function type (signature) + * @param module Name of the module to link the function to, or "*" to link to any module + * @param function_name Name of the function (as referenced by the module) + * @param function Function to link (a function pointer) + */ + template + void link(const char *module, const char *function_name, Func *function); + + /** + * Same as module::link, but doesn't throw an exception if the function is not referenced. + */ + template + void link_optional(const char *module, const char *function_name, Func *function); + + /** + * Split compiler stage (support for RNWebAssembly). + */ + void compile() { + M3Result err = m3_CompileModule(m_module.get()); + detail::check_error(err); + } + + protected: + friend class environment; + friend class runtime; + + module(const std::shared_ptr &env, std::istream &in_wasm) { + in_wasm.unsetf(std::ios::skipws); + std::copy(std::istream_iterator(in_wasm), + std::istream_iterator(), + std::back_inserter(m_moduleRawData)); + parse(env.get(), m_moduleRawData.data(), m_moduleRawData.size()); + } + + module(const std::shared_ptr &env, const uint8_t *data, size_t size) : m_env(env) { + m_moduleRawData = std::vector{data, data + size}; + parse(env.get(), m_moduleRawData.data(), m_moduleRawData.size()); + } + + void parse(IM3Environment env, const uint8_t *data, size_t size) { + IM3Module p; + M3Result err = m3_ParseModule(env, &p, data, static_cast(size)); + detail::check_error(err); + m_module.reset(p, [this](IM3Module module) { + if (!m_loaded) { + m3_FreeModule(module); + } + }); + } + + void load_into(IM3Runtime runtime) { + M3Result err = m3_LoadModule(runtime, m_module.get()); + detail::check_error(err); + m_loaded = true; + } + + std::shared_ptr m_env; + std::shared_ptr m_module; + bool m_loaded = false; + std::vector m_moduleRawData {}; + }; + + + /** + * Handle of a function. Can be obtained from runtime::find_function method by name. + */ + class function { + public: + /** + * Call the function with the provided arguments, expressed as strings. + * + * Arguments are passed as strings. WASM3 automatically converts them into the types expected + * by the function being called. + * + * Note that the type of the return value must be explicitly specified as a template argument. + * + * @return the return value of the function. + */ + template + typename detail::first_type::value>::type...>::type + call_argv(Args... args) { + /* std::enable_if above checks that all argument types are convertible const char* */ + const char* argv[] = {args...}; + M3Result res = m3_CallArgv(m_func, sizeof...(args), argv); + detail::check_error(res); + Ret ret; + + res = m3_GetResults(m_func, &ret); + + detail::check_error(res); + return ret; + } + + // HACK: RNWebAssembly + template + Ret call_argv(const std::vector& args) { + // Convert the vector of strings into an array of const char* + std::vector argv; + argv.reserve(args.size()); + for (const auto& arg : args) { + argv.push_back(arg.c_str()); + } + + /* std::enable_if above checks that all argument types are convertible const char* */ + M3Result res = m3_CallArgv(m_func, argv.size(), argv.data()); + + detail::check_error(res); + Ret ret; + + res = m3_GetResultsV(m_func, &ret); + + detail::check_error(res); + return ret; + } + + template + typename detail::first_type::value>::type...>::type + call_argv(Args... args) { + /* std::enable_if above checks that all argument types are convertible const char* */ + const char* argv[] = {args...}; + M3Result res = m3_CallArgv(m_func, sizeof...(args), argv); + detail::check_error(res); + } + + /** + * Call the function with the provided arguments (int/float types). + * + * Note that the type of the return value must be explicitly specified as a template argument. + * + * @return the return value of the function or void. + */ + template + Ret call(Args... args) { + std::array arg_ptrs{ reinterpret_cast(&args)... }; + M3Result res = m3_Call(m_func, arg_ptrs.size(), arg_ptrs.data()); + detail::check_error(res); + + if constexpr (!std::is_void::value) { + Ret ret; + const void* ret_ptrs[] = { &ret }; + res = m3_GetResults(m_func, 1, ret_ptrs); + detail::check_error(res); + return ret; + } + } + + /** + * Get the type of a function argument. + * @return the type of the argument at the specified index. + */ + // HACK: RNWebAssembly + M3ValueType GetArgType(const uint32_t index) { + return m3_GetArgType(m_func, index); + } + + /** + * Get the number of arguments for the function. + * @return the number of arguments in the function. + */ + // HACK: RNWebAssembly + uint32_t GetArgCount() { + return m3_GetArgCount(m_func); + } + + /** + * Get the return type of a function. + * @return the return type of the function. + */ + // HACK: RNWebAssembly + M3ValueType GetRetType(const uint32_t index) { + return m3_GetRetType(m_func, index); + } + + /** + * Get the return count of a function. + * @return the return count of the function. + */ + // HACK: RNWebAssembly + uint32_t GetRetCount() { + return m3_GetRetCount(m_func); + } + + protected: + friend class runtime; + + function(const std::shared_ptr &runtime, const char *name) : m_runtime(runtime) { + M3Result err = m3_FindFunction(&m_func, runtime.get(), name); + detail::check_error(err); + assert(m_func != nullptr); + } + + std::shared_ptr m_runtime; + M3Function *m_func = nullptr; + }; + + inline runtime environment::new_runtime(uint32_t stack_size_bytes) { + return runtime(m_env, stack_size_bytes); + } + + inline module environment::parse_module(std::istream &in) { + return module(m_env, in); + } + + inline module environment::parse_module(const uint8_t *data, size_t size) { + return module(m_env, data, size); + } + + inline void runtime::load(module &mod) { + mod.load_into(m_runtime.get()); + } + + inline function runtime::find_function(const char *name) { + return function(m_runtime, name); + } + + template + void module::link(const char *module, const char *function_name, Func *function) { + M3Result ret = detail::m3_wrapper::link(m_module.get(), module, function_name, function); + detail::check_error(ret); + } + + template + void module::link_optional(const char *module, const char *function_name, Func *function) { + M3Result ret = detail::m3_wrapper::link(m_module.get(), module, function_name, function); + if (ret == m3Err_functionLookupFailed) { + return; + } + detail::check_error(ret); + } + +} // namespace wasm3 diff --git a/cpp/wasm3_defs.h b/cpp/wasm3_defs.h new file mode 100644 index 0000000..2a98700 --- /dev/null +++ b/cpp/wasm3_defs.h @@ -0,0 +1,293 @@ +// +// wasm3_defs.h +// +// Created by Volodymyr Shymanskyy on 11/20/19. +// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. +// + +#ifndef wasm3_defs_h +#define wasm3_defs_h + +#define M3_STR__(x) #x +#define M3_STR(x) M3_STR__(x) + +#define M3_CONCAT__(a,b) a##b +#define M3_CONCAT(a,b) M3_CONCAT__(a,b) + +/* + * Detect compiler + */ + +# if defined(__clang__) +# define M3_COMPILER_CLANG 1 +# elif defined(__INTEL_COMPILER) +# define M3_COMPILER_ICC 1 +# elif defined(__GNUC__) || defined(__GNUG__) +# define M3_COMPILER_GCC 1 +# elif defined(_MSC_VER) +# define M3_COMPILER_MSVC 1 +# else +# warning "Compiler not detected" +# endif + +# if defined(M3_COMPILER_CLANG) +# if defined(WIN32) +# define M3_COMPILER_VER __VERSION__ " for Windows" +# else +# define M3_COMPILER_VER __VERSION__ +# endif +# elif defined(M3_COMPILER_GCC) +# define M3_COMPILER_VER "GCC " __VERSION__ +# elif defined(M3_COMPILER_ICC) +# define M3_COMPILER_VER __VERSION__ +# elif defined(M3_COMPILER_MSVC) +# define M3_COMPILER_VER "MSVC " M3_STR(_MSC_VER) +# else +# define M3_COMPILER_VER "unknown" +# endif + +# ifdef __has_feature +# define M3_COMPILER_HAS_FEATURE(x) __has_feature(x) +# else +# define M3_COMPILER_HAS_FEATURE(x) 0 +# endif + +# ifdef __has_builtin +# define M3_COMPILER_HAS_BUILTIN(x) __has_builtin(x) +# else +# define M3_COMPILER_HAS_BUILTIN(x) 0 +# endif + +# ifdef __has_attribute +# define M3_COMPILER_HAS_ATTRIBUTE(x) __has_attribute(x) +# else +# define M3_COMPILER_HAS_ATTRIBUTE(x) 0 +# endif + +/* + * Detect endianness + */ + +# if defined(M3_COMPILER_MSVC) +# define M3_LITTLE_ENDIAN +# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define M3_LITTLE_ENDIAN +# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define M3_BIG_ENDIAN +# else +# error "Byte order not detected" +# endif + +/* + * Detect platform + */ + +# if defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_GCC) || defined(M3_COMPILER_ICC) +# if defined(__wasm__) +# define M3_ARCH "wasm" + +# elif defined(__x86_64__) +# define M3_ARCH "x86_64" + +# elif defined(__i386__) +# define M3_ARCH "i386" + +# elif defined(__aarch64__) +# define M3_ARCH "arm64-v8a" + +# elif defined(__arm__) +# if defined(__ARM_ARCH_7A__) +# if defined(__ARM_NEON__) +# if defined(__ARM_PCS_VFP) +# define M3_ARCH "arm-v7a/NEON hard-float" +# else +# define M3_ARCH "arm-v7a/NEON" +# endif +# else +# if defined(__ARM_PCS_VFP) +# define M3_ARCH "arm-v7a hard-float" +# else +# define M3_ARCH "arm-v7a" +# endif +# endif +# else +# define M3_ARCH "arm" +# endif + +# elif defined(__riscv) +# if defined(__riscv_32e) +# define _M3_ARCH_RV "rv32e" +# elif __riscv_xlen == 128 +# define _M3_ARCH_RV "rv128i" +# elif __riscv_xlen == 64 +# define _M3_ARCH_RV "rv64i" +# elif __riscv_xlen == 32 +# define _M3_ARCH_RV "rv32i" +# endif +# if defined(__riscv_muldiv) +# define _M3_ARCH_RV_M _M3_ARCH_RV "m" +# else +# define _M3_ARCH_RV_M _M3_ARCH_RV +# endif +# if defined(__riscv_atomic) +# define _M3_ARCH_RV_A _M3_ARCH_RV_M "a" +# else +# define _M3_ARCH_RV_A _M3_ARCH_RV_M +# endif +# if defined(__riscv_flen) +# define _M3_ARCH_RV_F _M3_ARCH_RV_A "f" +# else +# define _M3_ARCH_RV_F _M3_ARCH_RV_A +# endif +# if defined(__riscv_flen) && __riscv_flen >= 64 +# define _M3_ARCH_RV_D _M3_ARCH_RV_F "d" +# else +# define _M3_ARCH_RV_D _M3_ARCH_RV_F +# endif +# if defined(__riscv_compressed) +# define _M3_ARCH_RV_C _M3_ARCH_RV_D "c" +# else +# define _M3_ARCH_RV_C _M3_ARCH_RV_D +# endif +# define M3_ARCH _M3_ARCH_RV_C + +# elif defined(__mips__) +# if defined(__MIPSEB__) && defined(__mips64) +# define M3_ARCH "mips64 " _MIPS_ARCH +# elif defined(__MIPSEL__) && defined(__mips64) +# define M3_ARCH "mips64el " _MIPS_ARCH +# elif defined(__MIPSEB__) +# define M3_ARCH "mips " _MIPS_ARCH +# elif defined(__MIPSEL__) +# define M3_ARCH "mipsel " _MIPS_ARCH +# endif + +# elif defined(__PPC__) +# if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) +# define M3_ARCH "ppc64le" +# elif defined(__PPC64__) +# define M3_ARCH "ppc64" +# else +# define M3_ARCH "ppc" +# endif + +# elif defined(__sparc__) +# if defined(__arch64__) +# define M3_ARCH "sparc64" +# else +# define M3_ARCH "sparc" +# endif + +# elif defined(__s390x__) +# define M3_ARCH "s390x" + +# elif defined(__alpha__) +# define M3_ARCH "alpha" + +# elif defined(__m68k__) +# define M3_ARCH "m68k" + +# elif defined(__xtensa__) +# define M3_ARCH "xtensa" + +# elif defined(__arc__) +# define M3_ARCH "arc32" + +# elif defined(__AVR__) +# define M3_ARCH "avr" +# endif +# endif + +# if defined(M3_COMPILER_MSVC) +# if defined(_M_X64) +# define M3_ARCH "x86_64" +# elif defined(_M_IX86) +# define M3_ARCH "i386" +# elif defined(_M_ARM64) +# define M3_ARCH "arm64" +# elif defined(_M_ARM) +# define M3_ARCH "arm" +# endif +# endif + +# if !defined(M3_ARCH) +# warning "Architecture not detected" +# define M3_ARCH "unknown" +# endif + +/* + * Byte swapping (for Big-Endian systems only) + */ + +# if defined(M3_COMPILER_MSVC) +# define m3_bswap16(x) _byteswap_ushort((x)) +# define m3_bswap32(x) _byteswap_ulong((x)) +# define m3_bswap64(x) _byteswap_uint64((x)) +# elif defined(M3_COMPILER_GCC) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) +// __builtin_bswap32/64 added in gcc 4.3, __builtin_bswap16 added in gcc 4.8 +# define m3_bswap16(x) __builtin_bswap16((x)) +# define m3_bswap32(x) __builtin_bswap32((x)) +# define m3_bswap64(x) __builtin_bswap64((x)) +# elif defined(M3_COMPILER_CLANG) && M3_COMPILER_HAS_BUILTIN(__builtin_bswap16) +# define m3_bswap16(x) __builtin_bswap16((x)) +# define m3_bswap32(x) __builtin_bswap32((x)) +# define m3_bswap64(x) __builtin_bswap64((x)) +# elif defined(M3_COMPILER_ICC) +# define m3_bswap16(x) __builtin_bswap16((x)) +# define m3_bswap32(x) __builtin_bswap32((x)) +# define m3_bswap64(x) __builtin_bswap64((x)) +# else +# ifdef __linux__ +# include +# else +# include +# endif +# if defined(__bswap_16) +# define m3_bswap16(x) __bswap_16((x)) +# define m3_bswap32(x) __bswap_32((x)) +# define m3_bswap64(x) __bswap_64((x)) +# else +# warning "Using naive (probably slow) bswap operations" + static inline + uint16_t m3_bswap16(uint16_t x) { + return ((( x >> 8 ) & 0xffu ) | (( x & 0xffu ) << 8 )); + } + static inline + uint32_t m3_bswap32(uint32_t x) { + return ((( x & 0xff000000u ) >> 24 ) | + (( x & 0x00ff0000u ) >> 8 ) | + (( x & 0x0000ff00u ) << 8 ) | + (( x & 0x000000ffu ) << 24 )); + } + static inline + uint64_t m3_bswap64(uint64_t x) { + return ((( x & 0xff00000000000000ull ) >> 56 ) | + (( x & 0x00ff000000000000ull ) >> 40 ) | + (( x & 0x0000ff0000000000ull ) >> 24 ) | + (( x & 0x000000ff00000000ull ) >> 8 ) | + (( x & 0x00000000ff000000ull ) << 8 ) | + (( x & 0x0000000000ff0000ull ) << 24 ) | + (( x & 0x000000000000ff00ull ) << 40 ) | + (( x & 0x00000000000000ffull ) << 56 )); + } +# endif +# endif + +/* + * Bit ops + */ +#define m3_isBitSet(val, pos) ((val & (1 << pos)) != 0) + +/* + * Other + */ + +# if defined(M3_COMPILER_GCC) || defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_ICC) +# define M3_UNLIKELY(x) __builtin_expect(!!(x), 0) +# define M3_LIKELY(x) __builtin_expect(!!(x), 1) +# else +# define M3_UNLIKELY(x) (x) +# define M3_LIKELY(x) (x) +# endif + +#endif // wasm3_defs_h diff --git a/example/.bundle/config b/example/.bundle/config new file mode 100644 index 0000000..848943b --- /dev/null +++ b/example/.bundle/config @@ -0,0 +1,2 @@ +BUNDLE_PATH: "vendor/bundle" +BUNDLE_FORCE_RUBY_PLATFORM: 1 diff --git a/example/.node-version b/example/.node-version new file mode 100644 index 0000000..3c03207 --- /dev/null +++ b/example/.node-version @@ -0,0 +1 @@ +18 diff --git a/example/.ruby-version b/example/.ruby-version new file mode 100644 index 0000000..49cdd66 --- /dev/null +++ b/example/.ruby-version @@ -0,0 +1 @@ +2.7.6 diff --git a/example/.watchmanconfig b/example/.watchmanconfig new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/example/.watchmanconfig @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/example/Gemfile b/example/Gemfile new file mode 100644 index 0000000..567e598 --- /dev/null +++ b/example/Gemfile @@ -0,0 +1,6 @@ +source 'https://rubygems.org' + +# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version +ruby File.read(File.join(__dir__, '.ruby-version')).strip + +gem 'cocoapods', '~> 1.11', '>= 1.11.3' diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle new file mode 100644 index 0000000..4baba61 --- /dev/null +++ b/example/android/app/build.gradle @@ -0,0 +1,170 @@ +apply plugin: "com.android.application" +apply plugin: "com.facebook.react" + +import com.android.build.OutputFile + +/** + * This is the configuration block to customize your React Native Android app. + * By default you don't need to apply any configuration, just uncomment the lines you need. + */ +react { + /* Folders */ + // The root of your project, i.e. where "package.json" lives. Default is '..' + // root = file("../") + // The folder where the react-native NPM package is. Default is ../node_modules/react-native + // reactNativeDir = file("../node_modules/react-native") + // The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen + // codegenDir = file("../node_modules/react-native-codegen") + // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js + // cliFile = file("../node_modules/react-native/cli.js") + + /* Variants */ + // The list of variants to that are debuggable. For those we're going to + // skip the bundling of the JS bundle and the assets. By default is just 'debug'. + // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. + // debuggableVariants = ["liteDebug", "prodDebug"] + + /* Bundling */ + // A list containing the node command and its flags. Default is just 'node'. + // nodeExecutableAndArgs = ["node"] + // + // The command to run when bundling. By default is 'bundle' + // bundleCommand = "ram-bundle" + // + // The path to the CLI configuration file. Default is empty. + // bundleConfig = file(../rn-cli.config.js) + // + // The name of the generated asset file containing your JS bundle + // bundleAssetName = "MyApplication.android.bundle" + // + // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' + // entryFile = file("../js/MyApplication.android.js") + // + // A list of extra flags to pass to the 'bundle' commands. + // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle + // extraPackagerArgs = [] + + /* Hermes Commands */ + // The hermes compiler command to run. By default it is 'hermesc' + // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" + // + // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" + // hermesFlags = ["-O", "-output-source-map"] +} + +/** + * Set this to true to create four separate APKs instead of one, + * one for each native architecture. This is useful if you don't + * use App Bundles (https://developer.android.com/guide/app-bundle/) + * and want to have separate APKs to upload to the Play Store. + */ +def enableSeparateBuildPerCPUArchitecture = false + +/** + * Set this to true to Run Proguard on Release builds to minify the Java bytecode. + */ +def enableProguardInReleaseBuilds = false + +/** + * The preferred build flavor of JavaScriptCore (JSC) + * + * For example, to use the international variant, you can use: + * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` + * + * The international variant includes ICU i18n library and necessary data + * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that + * give correct results when using with locales other than en-US. Note that + * this variant is about 6MiB larger per architecture than default. + */ +def jscFlavor = 'org.webkit:android-jsc:+' + +/** + * Private function to get the list of Native Architectures you want to build. + * This reads the value from reactNativeArchitectures in your gradle.properties + * file and works together with the --active-arch-only flag of react-native run-android. + */ +def reactNativeArchitectures() { + def value = project.getProperties().get("reactNativeArchitectures") + return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] +} + +android { + ndkVersion rootProject.ext.ndkVersion + + compileSdkVersion rootProject.ext.compileSdkVersion + + namespace "com.webassemblyexample" + defaultConfig { + applicationId "com.webassemblyexample" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + } + + splits { + abi { + reset() + enable enableSeparateBuildPerCPUArchitecture + universalApk false // If true, also generate a universal APK + include (*reactNativeArchitectures()) + } + } + signingConfigs { + debug { + storeFile file('debug.keystore') + storePassword 'android' + keyAlias 'androiddebugkey' + keyPassword 'android' + } + } + buildTypes { + debug { + signingConfig signingConfigs.debug + } + release { + // Caution! In production, you need to generate your own keystore file. + // see https://reactnative.dev/docs/signed-apk-android. + signingConfig signingConfigs.debug + minifyEnabled enableProguardInReleaseBuilds + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + } + } + + // applicationVariants are e.g. debug, release + applicationVariants.all { variant -> + variant.outputs.each { output -> + // For each separate APK per architecture, set a unique version code as described here: + // https://developer.android.com/studio/build/configure-apk-splits.html + // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc. + def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] + def abi = output.getFilter(OutputFile.ABI) + if (abi != null) { // null for the universal-debug, universal-release variants + output.versionCodeOverride = + defaultConfig.versionCode * 1000 + versionCodes.get(abi) + } + + } + } +} + +dependencies { + // The version of react-native is set by the React Native Gradle Plugin + implementation("com.facebook.react:react-android") + + implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0") + + debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") + debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { + exclude group:'com.squareup.okhttp3', module:'okhttp' + } + + debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") + if (hermesEnabled.toBoolean()) { + implementation("com.facebook.react:hermes-android") + } else { + implementation jscFlavor + } +} + +apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) diff --git a/example/android/app/debug.keystore b/example/android/app/debug.keystore new file mode 100644 index 0000000000000000000000000000000000000000..364e105ed39fbfd62001429a68140672b06ec0de GIT binary patch literal 2257 zcmchYXEfYt8;7T1^dLH$VOTZ%2NOdOH5j5LYLtZ0q7x-V8_6gU5)#7dkq{HTmsfNq zB3ZqcAxeY^G10@?efK?Q&)M(qInVv!xjx+IKEL}p*K@LYvIzo#AZG>st5|P)KF1_Z;y){W{<7K{nl!CPuE z_^(!C(Ol0n8 zK13*rzAtW>(wULKPRYLd7G18F8#1P`V*9`(Poj26eOXYyBVZPno~Cvvhx7vPjAuZo zF?VD!zB~QG(!zbw#qsxT8%BSpqMZ4f70ZPn-3y$L8{EVbbN9$H`B&Z1quk9tgp5FM zuxp3pJ0b8u|3+#5bkJ4SRnCF2l7#DyLYXYY8*?OuAwK4E6J{0N=O3QNVzQ$L#FKkR zi-c@&!nDvezOV$i$Lr}iF$XEcwnybQ6WZrMKuw8gCL^U#D;q3t&HpTbqyD%vG=TeDlzCT~MXUPC|Leb-Uk+ z=vnMd(|>ld?Fh>V8poP;q;;nc@en$|rnP0ytzD&fFkCeUE^kG9Kx4wUh!!rpjwKDP zyw_e|a^x_w3E zP}}@$g>*LLJ4i0`Gx)qltL}@;mDv}D*xR^oeWcWdPkW@Uu)B^X&4W1$p6}ze!zudJ zyiLg@uggoMIArBr*27EZV7djDg@W1MaL+rcZ-lrANJQ%%>u8)ZMWU@R2qtnmG(acP z0d_^!t>}5W zpT`*2NR+0+SpTHb+6Js4b;%LJB;B_-ChhnU5py}iJtku*hm5F0!iql8Hrpcy1aYbT z1*dKC5ua6pMX@@iONI?Hpr%h;&YaXp9n!ND7-=a%BD7v&g zOO41M6EbE24mJ#S$Ui0-brR5ML%@|ndz^)YLMMV1atna{Fw<;TF@>d&F|!Z>8eg>>hkFrV)W+uv=`^F9^e zzzM2*oOjT9%gLoub%(R57p-`TXFe#oh1_{&N-YN z<}artH|m=d8TQuKSWE)Z%puU|g|^^NFwC#N=@dPhasyYjoy(fdEVfKR@cXKHZV-`06HsP`|Ftx;8(YD$fFXumLWbGnu$GMqRncXYY9mwz9$ap zQtfZB^_BeNYITh^hA7+(XNFox5WMeG_LtJ%*Q}$8VKDI_p8^pqX)}NMb`0e|wgF7D zuQACY_Ua<1ri{;Jwt@_1sW9zzdgnyh_O#8y+C;LcZq6=4e^cs6KvmK@$vVpKFGbQ= z$)Eux5C|Fx;Gtmv9^#Y-g@7Rt7*eLp5n!gJmn7&B_L$G?NCN`AP>cXQEz}%F%K;vUs{+l4Q{}eWW;ATe2 zqvXzxoIDy(u;F2q1JH7Sf;{jy_j})F+cKlIOmNfjBGHoG^CN zM|Ho&&X|L-36f}Q-obEACz`sI%2f&k>z5c$2TyTSj~vmO)BW~+N^kt`Jt@R|s!){H ze1_eCrlNaPkJQhL$WG&iRvF*YG=gXd1IyYQ9ew|iYn7r~g!wOnw;@n42>enAxBv*A zEmV*N#sxdicyNM=A4|yaOC5MByts}s_Hpfj|y<6G=o=!3S@eIFKDdpR7|FY>L&Wat&oW&cm&X~ z5Bt>Fcq(fgnvlvLSYg&o6>&fY`ODg4`V^lWWD=%oJ#Kbad2u~! zLECFS*??>|vDsNR&pH=Ze0Eo`sC_G`OjoEKVHY|wmwlX&(XBE<@sx3Hd^gtd-fNwUHsylg06p`U2y_={u}Bc + + + + + + + + diff --git a/example/android/app/src/debug/java/com/webassemblyexample/ReactNativeFlipper.java b/example/android/app/src/debug/java/com/webassemblyexample/ReactNativeFlipper.java new file mode 100644 index 0000000..831f4fc --- /dev/null +++ b/example/android/app/src/debug/java/com/webassemblyexample/ReactNativeFlipper.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + *

This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + */ +package com.webassemblyexample; + +import android.content.Context; +import com.facebook.flipper.android.AndroidFlipperClient; +import com.facebook.flipper.android.utils.FlipperUtils; +import com.facebook.flipper.core.FlipperClient; +import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; +import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; +import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; +import com.facebook.flipper.plugins.inspector.DescriptorMapping; +import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; +import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; +import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; +import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; +import com.facebook.react.ReactInstanceEventListener; +import com.facebook.react.ReactInstanceManager; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.modules.network.NetworkingModule; +import okhttp3.OkHttpClient; + +/** + * Class responsible of loading Flipper inside your React Native application. This is the debug + * flavor of it. Here you can add your own plugins and customize the Flipper setup. + */ +public class ReactNativeFlipper { + public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { + if (FlipperUtils.shouldEnableFlipper(context)) { + final FlipperClient client = AndroidFlipperClient.getInstance(context); + + client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); + client.addPlugin(new DatabasesFlipperPlugin(context)); + client.addPlugin(new SharedPreferencesFlipperPlugin(context)); + client.addPlugin(CrashReporterPlugin.getInstance()); + + NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); + NetworkingModule.setCustomClientBuilder( + new NetworkingModule.CustomClientBuilder() { + @Override + public void apply(OkHttpClient.Builder builder) { + builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); + } + }); + client.addPlugin(networkFlipperPlugin); + client.start(); + + // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized + // Hence we run if after all native modules have been initialized + ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); + if (reactContext == null) { + reactInstanceManager.addReactInstanceEventListener( + new ReactInstanceEventListener() { + @Override + public void onReactContextInitialized(ReactContext reactContext) { + reactInstanceManager.removeReactInstanceEventListener(this); + reactContext.runOnNativeModulesQueueThread( + new Runnable() { + @Override + public void run() { + client.addPlugin(new FrescoFlipperPlugin()); + } + }); + } + }); + } else { + client.addPlugin(new FrescoFlipperPlugin()); + } + } + } +} diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..4122f36 --- /dev/null +++ b/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/example/android/app/src/main/java/com/webassemblyexample/MainActivity.java b/example/android/app/src/main/java/com/webassemblyexample/MainActivity.java new file mode 100644 index 0000000..cb52ad9 --- /dev/null +++ b/example/android/app/src/main/java/com/webassemblyexample/MainActivity.java @@ -0,0 +1,35 @@ +package com.webassemblyexample; + +import com.facebook.react.ReactActivity; +import com.facebook.react.ReactActivityDelegate; +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; +import com.facebook.react.defaults.DefaultReactActivityDelegate; + +public class MainActivity extends ReactActivity { + + /** + * Returns the name of the main component registered from JavaScript. This is used to schedule + * rendering of the component. + */ + @Override + protected String getMainComponentName() { + return "WebassemblyExample"; + } + + /** + * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link + * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React + * (aka React 18) with two boolean flags. + */ + @Override + protected ReactActivityDelegate createReactActivityDelegate() { + return new DefaultReactActivityDelegate( + this, + getMainComponentName(), + // If you opted-in for the New Architecture, we enable the Fabric Renderer. + DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled + // If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18). + DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled + ); + } +} diff --git a/example/android/app/src/main/java/com/webassemblyexample/MainApplication.java b/example/android/app/src/main/java/com/webassemblyexample/MainApplication.java new file mode 100644 index 0000000..87f00b7 --- /dev/null +++ b/example/android/app/src/main/java/com/webassemblyexample/MainApplication.java @@ -0,0 +1,62 @@ +package com.webassemblyexample; + +import android.app.Application; +import com.facebook.react.PackageList; +import com.facebook.react.ReactApplication; +import com.facebook.react.ReactNativeHost; +import com.facebook.react.ReactPackage; +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; +import com.facebook.react.defaults.DefaultReactNativeHost; +import com.facebook.soloader.SoLoader; +import java.util.List; + +public class MainApplication extends Application implements ReactApplication { + + private final ReactNativeHost mReactNativeHost = + new DefaultReactNativeHost(this) { + @Override + public boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; + } + + @Override + protected List getPackages() { + @SuppressWarnings("UnnecessaryLocalVariable") + List packages = new PackageList(this).getPackages(); + // Packages that cannot be autolinked yet can be added manually here, for example: + // packages.add(new MyReactNativePackage()); + return packages; + } + + @Override + protected String getJSMainModuleName() { + return "index"; + } + + @Override + protected boolean isNewArchEnabled() { + return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; + } + + @Override + protected Boolean isHermesEnabled() { + return BuildConfig.IS_HERMES_ENABLED; + } + }; + + @Override + public ReactNativeHost getReactNativeHost() { + return mReactNativeHost; + } + + @Override + public void onCreate() { + super.onCreate(); + SoLoader.init(this, /* native exopackage */ false); + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + // If you opted-in for the New Architecture, we load the native entry point for this app. + DefaultNewArchitectureEntryPoint.load(); + } + ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); + } +} diff --git a/example/android/app/src/main/res/drawable/rn_edit_text_material.xml b/example/android/app/src/main/res/drawable/rn_edit_text_material.xml new file mode 100644 index 0000000..f35d996 --- /dev/null +++ b/example/android/app/src/main/res/drawable/rn_edit_text_material.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..a2f5908281d070150700378b64a84c7db1f97aa1 GIT binary patch literal 3056 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 axPAxe%7rDusX+w*00006jj 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⁣ zwiG+x#%j}{0gU~J8;<|!B1@-XaB@{KORFwrYg_8rOv({b0EO#DbeQRm;B6_9=mXGf z-x|VL{zd`)#@yN}HkCSJbjbNlE|zL3Wm9Q8HY`sV)}3%pgN>cL^67{Z;PPL(*wT8N zUjXU{@|*hvm}({wsAC=x0^ok0%UAz0;sogW{B!nDqk|JJ5x~4NfTDgP49^zeu`csl?5mY@JdQdISc zFs!E{^grmkLnUk9 zny~m)1vws@5BFI<-0Tuo2JWX(0v`W|t(wg;s--L47WTvTMz-8l#TL^=OJNRS2?_Qj z3AKT+gvbyBi#H*-tJ%tWD|>EV3wy|8qxfzS!5RW;Jpl5*zo&^UBU=fG#2}UvRyNkK zA06Dy9;K1ca@r2T>yThYgI!ont$(G{6q#2QT+00r_x0(b)gsE`lBB?2gr55gq^D3Fi&p%E(p9>U%bv zkg1Jco(RbyTX7FDHOnl7-O@ zI$AaIl?9NJKPm(WiBP`1-#CB1QzU>&hKm)fpa5DKE{2$X0hGz-0uZ?cyTk(YC!Y&| zL=1VrNERSA5NA2jq7FACfX4JfPyj5XXl1yv0>~s;eF7L2$>&oMqeTFT2m$y7FlkON z_yurD1yIOvA;5C6016pyxBznGUt0kJ&k5r#;&>Jow`r)sp9R~PmK~lz$3xH%LT*1U zJdOyABZ3!FvNoR*vN$5ykHS8f`jA4zV+|L}i1C4`B2c{R0;UdYxaU|H)2avz@ z=mEYc|2S<+(B2Tj+FkX+2D+yFI!k9lWMA61DJ{)e;lum$(;O87?vGJJe!KtK04+N_ zI*P~t@dUb>9Xh{dbyl{-ZQ(UMgz7$|QfL5XSPkskt^NgctYC#;4WcZB1@%@wy@2t3 z2z0DI7&%b$*Aw~abe?GxE`ez@+6hOh-6*8fHRV{1os$EL@}uUZeG4h1&Be`98q*7j z=3-v+lhIjfWVo12!<>%V^a6lTgW3+_#W6n|p*~==zOH7z$0{LSZk(Tpd7EaD04hnA zL;#fxS0aD{`5^&D`}>0Uq?byDD-l2=!wm_bLcUl4gc(% za1p|itVANvFF>hghAS07Im1;IK;|b*W)}VDyI;BIp2=K*yu2a)j?B|f<44NI$NbmJ z#dE0>jI$fMr&@>4kN8MLFb4&2O9fEKaQg%(QO$4_1rVQywG^CmBLh#}_7gKW3vd?| z2?1^&KWq8}8I^_S0|)MowU_pw$q@nl@Nkn$z>BQq_KA^9yaR`(R3u{{Ig;cwt z@AJ^{ODQCm^neroM9nKNUAXi9RCK`OsP_LuR0PUR(YZCCX5dNF6VzcoK&=b^r`W?ltt|*F zpkoae%ZT{C1h~EcFui~b7fF`vb<<~j_VquuUA$}QqIKYELPp#;{u?q8Dz}WAG-(3; zjrm$i%7UbyZMM(Y{>!uJ#vNB?R~B{6Htp=>e*<{fQQ5W7V(1coCWlOON!MzZxhum| ztZBQpGR z;~#ur^&PockKdV{Q6R>o`Pl{0x!DEbpZ7y9Y;*ZvE!*gU`V1W3znva{f=?WO5I&>B z&hw6}tjECtaghm5z|C#%M;Yf_*pI^};h}Vl=^r9EN=tVDj86D;C$jIJ?K7VP+00000NkvXXu0mjf D5i!M* literal 0 HcmV?d00001 diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..459ca609d3ae0d3943ab44cdc27feef9256dc6d7 GIT binary patch literal 7098 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 literal 0 HcmV?d00001 diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..8ca12fe024be86e868d14e91120a6902f8e88ac6 GIT binary patch literal 6464 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 B0*L?s literal 0 HcmV?d00001 diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..8e19b410a1b15ff180f3dacac19395fe3046cdec GIT binary patch literal 10676 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 literal 0 HcmV?d00001 diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..4c19a13c239cb67b8a2134ddd5f325db1d2d5bee GIT binary patch literal 15523 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 BIP(Ai literal 0 HcmV?d00001 diff --git a/example/android/app/src/main/res/values/strings.xml b/example/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..7ca6b48 --- /dev/null +++ b/example/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + WebassemblyExample + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..7ba83a2 --- /dev/null +++ b/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/example/android/app/src/release/java/com/webassemblyexample/ReactNativeFlipper.java b/example/android/app/src/release/java/com/webassemblyexample/ReactNativeFlipper.java new file mode 100644 index 0000000..1b5e10e --- /dev/null +++ b/example/android/app/src/release/java/com/webassemblyexample/ReactNativeFlipper.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + *

This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + */ +package com.webassemblyexample; + +import android.content.Context; +import com.facebook.react.ReactInstanceManager; + +/** + * Class responsible of loading Flipper inside your React Native application. This is the release + * flavor of it so it's empty as we don't want to load Flipper. + */ +public class ReactNativeFlipper { + public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { + // Do nothing as we don't want to initialize Flipper on Release. + } +} diff --git a/example/android/build.gradle b/example/android/build.gradle new file mode 100644 index 0000000..67d887b --- /dev/null +++ b/example/android/build.gradle @@ -0,0 +1,21 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + ext { + buildToolsVersion = "33.0.0" + minSdkVersion = 21 + compileSdkVersion = 33 + targetSdkVersion = 33 + + // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. + ndkVersion = "23.1.7779620" + } + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.android.tools.build:gradle:7.3.1") + classpath("com.facebook.react:react-native-gradle-plugin") + } +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties new file mode 100644 index 0000000..e1ddc51 --- /dev/null +++ b/example/android/gradle.properties @@ -0,0 +1,44 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m +org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true + +# Version of flipper SDK to use with React Native +FLIPPER_VERSION=0.125.0 + +# Use this property to specify which architecture you want to build. +# You can also override it from the CLI using +# ./gradlew -PreactNativeArchitectures=x86_64 +reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 + +# Use this property to enable support to the new architecture. +# This will allow you to use TurboModules and the Fabric render in +# your application. You should enable this flag either if you want +# to write custom TurboModules/Fabric components OR use libraries that +# are providing them. +newArchEnabled=true + +# Use this property to enable or disable the Hermes JS engine. +# If set to false, you will be using JSC instead. +hermesEnabled=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.jar b/example/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..41d9927a4d4fb3f96a785543079b8df6723c946b GIT binary patch literal 59821 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 literal 0 HcmV?d00001 diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..8fad3f5 --- /dev/null +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/example/android/gradlew b/example/android/gradlew new file mode 100755 index 0000000..1b6c787 --- /dev/null +++ b/example/android/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/example/android/gradlew.bat b/example/android/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/example/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/example/android/settings.gradle b/example/android/settings.gradle new file mode 100644 index 0000000..70bae98 --- /dev/null +++ b/example/android/settings.gradle @@ -0,0 +1,4 @@ +rootProject.name = 'WebassemblyExample' +apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) +include ':app' +includeBuild('../node_modules/react-native-gradle-plugin') diff --git a/example/app.json b/example/app.json new file mode 100644 index 0000000..9d79c41 --- /dev/null +++ b/example/app.json @@ -0,0 +1,4 @@ +{ + "name": "WebassemblyExample", + "displayName": "WebassemblyExample" +} \ No newline at end of file diff --git a/example/babel.config.js b/example/babel.config.js new file mode 100644 index 0000000..adea77b --- /dev/null +++ b/example/babel.config.js @@ -0,0 +1,17 @@ +const path = require('path'); +const pak = require('../package.json'); + +module.exports = { + presets: ['module:metro-react-native-babel-preset'], + plugins: [ + [ + 'module-resolver', + { + extensions: ['.tsx', '.ts', '.js', '.json'], + alias: { + [pak.name]: path.join(__dirname, '..', pak.source), + }, + }, + ], + ], +}; diff --git a/example/index.js b/example/index.js new file mode 100644 index 0000000..117ddca --- /dev/null +++ b/example/index.js @@ -0,0 +1,5 @@ +import { AppRegistry } from 'react-native'; +import App from './src/App'; +import { name as appName } from './app.json'; + +AppRegistry.registerComponent(appName, () => App); diff --git a/example/ios/.xcode.env b/example/ios/.xcode.env new file mode 100644 index 0000000..3d5782c --- /dev/null +++ b/example/ios/.xcode.env @@ -0,0 +1,11 @@ +# This `.xcode.env` file is versioned and is used to source the environment +# used when running script phases inside Xcode. +# To customize your local environment, you can create an `.xcode.env.local` +# file that is not versioned. + +# NODE_BINARY variable contains the PATH to the node executable. +# +# Customize the NODE_BINARY variable here. +# For example, to use nvm with brew, add the following line +# . "$(brew --prefix nvm)/nvm.sh" --no-use +export NODE_BINARY=$(command -v node) diff --git a/example/ios/File.swift b/example/ios/File.swift new file mode 100644 index 0000000..19316b8 --- /dev/null +++ b/example/ios/File.swift @@ -0,0 +1,6 @@ +// +// File.swift +// WebassemblyExample +// + +import Foundation diff --git a/example/ios/Podfile b/example/ios/Podfile new file mode 100644 index 0000000..cf860f5 --- /dev/null +++ b/example/ios/Podfile @@ -0,0 +1,62 @@ +ENV['RCT_NEW_ARCH_ENABLED'] = '1' + +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, min_ios_version_supported +prepare_react_native_project! + +# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. +# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded +# +# To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` +# ```js +# module.exports = { +# dependencies: { +# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), +# ``` +flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled + +linkage = ENV['USE_FRAMEWORKS'] +if linkage != nil + Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green + use_frameworks! :linkage => linkage.to_sym +end + +target 'WebassemblyExample' do + config = use_native_modules! + + # Flags change depending on the env values. + flags = get_default_flags() + + use_react_native!( + :path => config[:reactNativePath], + # Hermes is now enabled by default. Disable by setting this flag to false. + # Upcoming versions of React Native may rely on get_default_flags(), but + # we make it explicit here to aid in the React Native upgrade process. + :hermes_enabled => flags[:hermes_enabled], + :fabric_enabled => flags[:fabric_enabled], + # Enables Flipper. + # + # Note that if you have use_frameworks! enabled, Flipper will not work and + # you should disable the next line. + :flipper_configuration => flipper_config, + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/.." + ) + + target 'WebassemblyExampleTests' do + inherit! :complete + # Pods for testing + end + + post_install do |installer| + react_native_post_install( + installer, + # Set `mac_catalyst_enabled` to `true` in order to apply patches + # necessary for Mac Catalyst builds + :mac_catalyst_enabled => false + ) + __apply_Xcode_12_5_M1_post_install_workaround(installer) + end +end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock new file mode 100644 index 0000000..0579171 --- /dev/null +++ b/example/ios/Podfile.lock @@ -0,0 +1,986 @@ +PODS: + - boost (1.76.0) + - CocoaAsyncSocket (7.6.5) + - DoubleConversion (1.1.6) + - FBLazyVector (0.71.4) + - FBReactNativeSpec (0.71.4): + - RCT-Folly (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-Core (= 0.71.4) + - React-jsi (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - Flipper (0.125.0): + - Flipper-Folly (~> 2.6) + - Flipper-RSocket (~> 1.4) + - Flipper-Boost-iOSX (1.76.0.1.11) + - Flipper-DoubleConversion (3.2.0.1) + - Flipper-Fmt (7.1.7) + - Flipper-Folly (2.6.10): + - Flipper-Boost-iOSX + - Flipper-DoubleConversion + - Flipper-Fmt (= 7.1.7) + - Flipper-Glog + - libevent (~> 2.1.12) + - OpenSSL-Universal (= 1.1.1100) + - Flipper-Glog (0.5.0.5) + - Flipper-PeerTalk (0.0.4) + - Flipper-RSocket (1.4.3): + - Flipper-Folly (~> 2.6) + - FlipperKit (0.125.0): + - FlipperKit/Core (= 0.125.0) + - FlipperKit/Core (0.125.0): + - Flipper (~> 0.125.0) + - FlipperKit/CppBridge + - FlipperKit/FBCxxFollyDynamicConvert + - FlipperKit/FBDefines + - FlipperKit/FKPortForwarding + - SocketRocket (~> 0.6.0) + - FlipperKit/CppBridge (0.125.0): + - Flipper (~> 0.125.0) + - FlipperKit/FBCxxFollyDynamicConvert (0.125.0): + - Flipper-Folly (~> 2.6) + - FlipperKit/FBDefines (0.125.0) + - FlipperKit/FKPortForwarding (0.125.0): + - CocoaAsyncSocket (~> 7.6) + - Flipper-PeerTalk (~> 0.0.4) + - FlipperKit/FlipperKitHighlightOverlay (0.125.0) + - FlipperKit/FlipperKitLayoutHelpers (0.125.0): + - FlipperKit/Core + - FlipperKit/FlipperKitHighlightOverlay + - FlipperKit/FlipperKitLayoutTextSearchable + - FlipperKit/FlipperKitLayoutIOSDescriptors (0.125.0): + - FlipperKit/Core + - FlipperKit/FlipperKitHighlightOverlay + - FlipperKit/FlipperKitLayoutHelpers + - YogaKit (~> 1.18) + - FlipperKit/FlipperKitLayoutPlugin (0.125.0): + - FlipperKit/Core + - FlipperKit/FlipperKitHighlightOverlay + - FlipperKit/FlipperKitLayoutHelpers + - FlipperKit/FlipperKitLayoutIOSDescriptors + - FlipperKit/FlipperKitLayoutTextSearchable + - YogaKit (~> 1.18) + - FlipperKit/FlipperKitLayoutTextSearchable (0.125.0) + - FlipperKit/FlipperKitNetworkPlugin (0.125.0): + - FlipperKit/Core + - FlipperKit/FlipperKitReactPlugin (0.125.0): + - FlipperKit/Core + - FlipperKit/FlipperKitUserDefaultsPlugin (0.125.0): + - FlipperKit/Core + - FlipperKit/SKIOSNetworkPlugin (0.125.0): + - FlipperKit/Core + - FlipperKit/FlipperKitNetworkPlugin + - fmt (6.2.1) + - glog (0.3.5) + - hermes-engine (0.71.4): + - hermes-engine/Pre-built (= 0.71.4) + - hermes-engine/Pre-built (0.71.4) + - libevent (2.1.12) + - OpenSSL-Universal (1.1.1100) + - RCT-Folly (2021.07.22.00): + - boost + - DoubleConversion + - fmt (~> 6.2.1) + - glog + - RCT-Folly/Default (= 2021.07.22.00) + - RCT-Folly/Default (2021.07.22.00): + - boost + - DoubleConversion + - fmt (~> 6.2.1) + - glog + - RCT-Folly/Fabric (2021.07.22.00): + - boost + - DoubleConversion + - fmt (~> 6.2.1) + - glog + - RCT-Folly/Futures (2021.07.22.00): + - boost + - DoubleConversion + - fmt (~> 6.2.1) + - glog + - libevent + - RCTRequired (0.71.4) + - RCTTypeSafety (0.71.4): + - FBLazyVector (= 0.71.4) + - RCTRequired (= 0.71.4) + - React-Core (= 0.71.4) + - React (0.71.4): + - React-Core (= 0.71.4) + - React-Core/DevSupport (= 0.71.4) + - React-Core/RCTWebSocket (= 0.71.4) + - React-RCTActionSheet (= 0.71.4) + - React-RCTAnimation (= 0.71.4) + - React-RCTBlob (= 0.71.4) + - React-RCTImage (= 0.71.4) + - React-RCTLinking (= 0.71.4) + - React-RCTNetwork (= 0.71.4) + - React-RCTSettings (= 0.71.4) + - React-RCTText (= 0.71.4) + - React-RCTVibration (= 0.71.4) + - React-callinvoker (0.71.4) + - React-Codegen (0.71.4): + - FBReactNativeSpec + - hermes-engine + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React-Core + - React-graphics + - React-jsi + - React-jsiexecutor + - React-rncore + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - React-Core (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default (= 0.71.4) + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/CoreModulesHeaders (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/Default (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/DevSupport (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default (= 0.71.4) + - React-Core/RCTWebSocket (= 0.71.4) + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-jsinspector (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/RCTActionSheetHeaders (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/RCTAnimationHeaders (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/RCTBlobHeaders (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/RCTImageHeaders (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/RCTLinkingHeaders (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/RCTNetworkHeaders (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/RCTSettingsHeaders (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/RCTTextHeaders (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/RCTVibrationHeaders (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-Core/RCTWebSocket (0.71.4): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default (= 0.71.4) + - React-cxxreact (= 0.71.4) + - React-hermes + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-perflogger (= 0.71.4) + - Yoga + - React-CoreModules (0.71.4): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.4) + - React-Codegen (= 0.71.4) + - React-Core/CoreModulesHeaders (= 0.71.4) + - React-jsi (= 0.71.4) + - React-RCTBlob + - React-RCTImage (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-cxxreact (0.71.4): + - boost (= 1.76.0) + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-callinvoker (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsinspector (= 0.71.4) + - React-logger (= 0.71.4) + - React-perflogger (= 0.71.4) + - React-runtimeexecutor (= 0.71.4) + - React-Fabric (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-Fabric/animations (= 0.71.4) + - React-Fabric/attributedstring (= 0.71.4) + - React-Fabric/butter (= 0.71.4) + - React-Fabric/componentregistry (= 0.71.4) + - React-Fabric/componentregistrynative (= 0.71.4) + - React-Fabric/components (= 0.71.4) + - React-Fabric/config (= 0.71.4) + - React-Fabric/core (= 0.71.4) + - React-Fabric/debug_core (= 0.71.4) + - React-Fabric/debug_renderer (= 0.71.4) + - React-Fabric/imagemanager (= 0.71.4) + - React-Fabric/leakchecker (= 0.71.4) + - React-Fabric/mapbuffer (= 0.71.4) + - React-Fabric/mounting (= 0.71.4) + - React-Fabric/runtimescheduler (= 0.71.4) + - React-Fabric/scheduler (= 0.71.4) + - React-Fabric/telemetry (= 0.71.4) + - React-Fabric/templateprocessor (= 0.71.4) + - React-Fabric/textlayoutmanager (= 0.71.4) + - React-Fabric/uimanager (= 0.71.4) + - React-Fabric/utils (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/animations (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/attributedstring (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/butter (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/componentregistry (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/componentregistrynative (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-Fabric/components/activityindicator (= 0.71.4) + - React-Fabric/components/image (= 0.71.4) + - React-Fabric/components/inputaccessory (= 0.71.4) + - React-Fabric/components/legacyviewmanagerinterop (= 0.71.4) + - React-Fabric/components/modal (= 0.71.4) + - React-Fabric/components/root (= 0.71.4) + - React-Fabric/components/safeareaview (= 0.71.4) + - React-Fabric/components/scrollview (= 0.71.4) + - React-Fabric/components/slider (= 0.71.4) + - React-Fabric/components/text (= 0.71.4) + - React-Fabric/components/textinput (= 0.71.4) + - React-Fabric/components/unimplementedview (= 0.71.4) + - React-Fabric/components/view (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/activityindicator (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/image (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/inputaccessory (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/legacyviewmanagerinterop (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/modal (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/root (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/safeareaview (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/scrollview (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/slider (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/text (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/textinput (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/unimplementedview (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/components/view (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - Yoga + - React-Fabric/config (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/core (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/debug_core (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/debug_renderer (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/imagemanager (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - React-RCTImage (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/leakchecker (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/mapbuffer (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/mounting (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/runtimescheduler (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/scheduler (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/telemetry (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/templateprocessor (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/textlayoutmanager (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-Fabric/uimanager + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/uimanager (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-Fabric/utils (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.4) + - RCTTypeSafety (= 0.71.4) + - React-graphics (= 0.71.4) + - React-jsi (= 0.71.4) + - React-jsiexecutor (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-graphics (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - React-Core/Default (= 0.71.4) + - React-hermes (0.71.4): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - RCT-Folly/Futures (= 2021.07.22.00) + - React-cxxreact (= 0.71.4) + - React-jsi + - React-jsiexecutor (= 0.71.4) + - React-jsinspector (= 0.71.4) + - React-perflogger (= 0.71.4) + - React-jsi (0.71.4): + - boost (= 1.76.0) + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-jsiexecutor (0.71.4): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-cxxreact (= 0.71.4) + - React-jsi (= 0.71.4) + - React-perflogger (= 0.71.4) + - React-jsinspector (0.71.4) + - React-logger (0.71.4): + - glog + - react-native-webassembly (0.1.0): + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - ReactCommon/turbomodule/core + - React-perflogger (0.71.4) + - React-RCTActionSheet (0.71.4): + - React-Core/RCTActionSheetHeaders (= 0.71.4) + - React-RCTAnimation (0.71.4): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.4) + - React-Codegen (= 0.71.4) + - React-Core/RCTAnimationHeaders (= 0.71.4) + - React-jsi (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-RCTAppDelegate (0.71.4): + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React-Core + - React-graphics + - React-RCTFabric + - ReactCommon/turbomodule/core + - React-RCTBlob (0.71.4): + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Codegen (= 0.71.4) + - React-Core/RCTBlobHeaders (= 0.71.4) + - React-Core/RCTWebSocket (= 0.71.4) + - React-jsi (= 0.71.4) + - React-RCTNetwork (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-RCTFabric (0.71.4): + - RCT-Folly/Fabric (= 2021.07.22.00) + - React-Core (= 0.71.4) + - React-Fabric (= 0.71.4) + - React-RCTImage (= 0.71.4) + - React-RCTImage (0.71.4): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.4) + - React-Codegen (= 0.71.4) + - React-Core/RCTImageHeaders (= 0.71.4) + - React-jsi (= 0.71.4) + - React-RCTNetwork (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-RCTLinking (0.71.4): + - React-Codegen (= 0.71.4) + - React-Core/RCTLinkingHeaders (= 0.71.4) + - React-jsi (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-RCTNetwork (0.71.4): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.4) + - React-Codegen (= 0.71.4) + - React-Core/RCTNetworkHeaders (= 0.71.4) + - React-jsi (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-RCTSettings (0.71.4): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.4) + - React-Codegen (= 0.71.4) + - React-Core/RCTSettingsHeaders (= 0.71.4) + - React-jsi (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-RCTText (0.71.4): + - React-Core/RCTTextHeaders (= 0.71.4) + - React-RCTVibration (0.71.4): + - RCT-Folly (= 2021.07.22.00) + - React-Codegen (= 0.71.4) + - React-Core/RCTVibrationHeaders (= 0.71.4) + - React-jsi (= 0.71.4) + - ReactCommon/turbomodule/core (= 0.71.4) + - React-rncore (0.71.4) + - React-runtimeexecutor (0.71.4): + - React-jsi (= 0.71.4) + - ReactCommon/turbomodule/bridging (0.71.4): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-callinvoker (= 0.71.4) + - React-Core (= 0.71.4) + - React-cxxreact (= 0.71.4) + - React-jsi (= 0.71.4) + - React-logger (= 0.71.4) + - React-perflogger (= 0.71.4) + - ReactCommon/turbomodule/core (0.71.4): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-callinvoker (= 0.71.4) + - React-Core (= 0.71.4) + - React-cxxreact (= 0.71.4) + - React-jsi (= 0.71.4) + - React-logger (= 0.71.4) + - React-perflogger (= 0.71.4) + - SocketRocket (0.6.0) + - Yoga (1.14.0) + - YogaKit (1.18.1): + - Yoga (~> 1.14) + +DEPENDENCIES: + - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) + - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) + - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) + - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) + - Flipper (= 0.125.0) + - Flipper-Boost-iOSX (= 1.76.0.1.11) + - Flipper-DoubleConversion (= 3.2.0.1) + - Flipper-Fmt (= 7.1.7) + - Flipper-Folly (= 2.6.10) + - Flipper-Glog (= 0.5.0.5) + - Flipper-PeerTalk (= 0.0.4) + - Flipper-RSocket (= 1.4.3) + - FlipperKit (= 0.125.0) + - FlipperKit/Core (= 0.125.0) + - FlipperKit/CppBridge (= 0.125.0) + - FlipperKit/FBCxxFollyDynamicConvert (= 0.125.0) + - FlipperKit/FBDefines (= 0.125.0) + - FlipperKit/FKPortForwarding (= 0.125.0) + - FlipperKit/FlipperKitHighlightOverlay (= 0.125.0) + - FlipperKit/FlipperKitLayoutPlugin (= 0.125.0) + - FlipperKit/FlipperKitLayoutTextSearchable (= 0.125.0) + - FlipperKit/FlipperKitNetworkPlugin (= 0.125.0) + - FlipperKit/FlipperKitReactPlugin (= 0.125.0) + - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.125.0) + - FlipperKit/SKIOSNetworkPlugin (= 0.125.0) + - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) + - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) + - libevent (~> 2.1.12) + - OpenSSL-Universal (= 1.1.1100) + - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) + - RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) + - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) + - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) + - React (from `../node_modules/react-native/`) + - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) + - React-Codegen (from `build/generated/ios`) + - React-Core (from `../node_modules/react-native/`) + - React-Core/DevSupport (from `../node_modules/react-native/`) + - React-Core/RCTWebSocket (from `../node_modules/react-native/`) + - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) + - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) + - React-Fabric (from `../node_modules/react-native/ReactCommon`) + - React-graphics (from `../node_modules/react-native/ReactCommon/react/renderer/graphics`) + - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`) + - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) + - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) + - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) + - React-logger (from `../node_modules/react-native/ReactCommon/logger`) + - react-native-webassembly (from `../..`) + - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) + - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) + - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) + - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`) + - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) + - React-RCTFabric (from `../node_modules/react-native/React`) + - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) + - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) + - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) + - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) + - React-RCTText (from `../node_modules/react-native/Libraries/Text`) + - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) + - React-rncore (from `../node_modules/react-native/ReactCommon`) + - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) + - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) + +SPEC REPOS: + trunk: + - CocoaAsyncSocket + - Flipper + - Flipper-Boost-iOSX + - Flipper-DoubleConversion + - Flipper-Fmt + - Flipper-Folly + - Flipper-Glog + - Flipper-PeerTalk + - Flipper-RSocket + - FlipperKit + - fmt + - libevent + - OpenSSL-Universal + - SocketRocket + - YogaKit + +EXTERNAL SOURCES: + boost: + :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" + DoubleConversion: + :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + FBLazyVector: + :path: "../node_modules/react-native/Libraries/FBLazyVector" + FBReactNativeSpec: + :path: "../node_modules/react-native/React/FBReactNativeSpec" + glog: + :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" + hermes-engine: + :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" + RCT-Folly: + :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" + RCTRequired: + :path: "../node_modules/react-native/Libraries/RCTRequired" + RCTTypeSafety: + :path: "../node_modules/react-native/Libraries/TypeSafety" + React: + :path: "../node_modules/react-native/" + React-callinvoker: + :path: "../node_modules/react-native/ReactCommon/callinvoker" + React-Codegen: + :path: build/generated/ios + React-Core: + :path: "../node_modules/react-native/" + React-CoreModules: + :path: "../node_modules/react-native/React/CoreModules" + React-cxxreact: + :path: "../node_modules/react-native/ReactCommon/cxxreact" + React-Fabric: + :path: "../node_modules/react-native/ReactCommon" + React-graphics: + :path: "../node_modules/react-native/ReactCommon/react/renderer/graphics" + React-hermes: + :path: "../node_modules/react-native/ReactCommon/hermes" + React-jsi: + :path: "../node_modules/react-native/ReactCommon/jsi" + React-jsiexecutor: + :path: "../node_modules/react-native/ReactCommon/jsiexecutor" + React-jsinspector: + :path: "../node_modules/react-native/ReactCommon/jsinspector" + React-logger: + :path: "../node_modules/react-native/ReactCommon/logger" + react-native-webassembly: + :path: "../.." + React-perflogger: + :path: "../node_modules/react-native/ReactCommon/reactperflogger" + React-RCTActionSheet: + :path: "../node_modules/react-native/Libraries/ActionSheetIOS" + React-RCTAnimation: + :path: "../node_modules/react-native/Libraries/NativeAnimation" + React-RCTAppDelegate: + :path: "../node_modules/react-native/Libraries/AppDelegate" + React-RCTBlob: + :path: "../node_modules/react-native/Libraries/Blob" + React-RCTFabric: + :path: "../node_modules/react-native/React" + React-RCTImage: + :path: "../node_modules/react-native/Libraries/Image" + React-RCTLinking: + :path: "../node_modules/react-native/Libraries/LinkingIOS" + React-RCTNetwork: + :path: "../node_modules/react-native/Libraries/Network" + React-RCTSettings: + :path: "../node_modules/react-native/Libraries/Settings" + React-RCTText: + :path: "../node_modules/react-native/Libraries/Text" + React-RCTVibration: + :path: "../node_modules/react-native/Libraries/Vibration" + React-rncore: + :path: "../node_modules/react-native/ReactCommon" + React-runtimeexecutor: + :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" + ReactCommon: + :path: "../node_modules/react-native/ReactCommon" + Yoga: + :path: "../node_modules/react-native/ReactCommon/yoga" + +SPEC CHECKSUMS: + boost: 57d2868c099736d80fcd648bf211b4431e51a558 + CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 + DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 + FBLazyVector: 446e84642979fff0ba57f3c804c2228a473aeac2 + FBReactNativeSpec: a7aeb32e7619beba1f8041b2eda82519cba9965c + Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0 + Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c + Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30 + Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b + Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3 + Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446 + Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 + Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541 + FlipperKit: cbdee19bdd4e7f05472a66ce290f1b729ba3cb86 + fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 + glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b + hermes-engine: a1f157c49ea579c28b0296bda8530e980c45bdb3 + libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 + OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c + RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 + RCTRequired: 5a024fdf458fa8c0d82fc262e76f982d4dcdecdd + RCTTypeSafety: b6c253064466411c6810b45f66bc1e43ce0c54ba + React: 715292db5bd46989419445a5547954b25d2090f0 + React-callinvoker: 105392d1179058585b564d35b4592fe1c46d6fba + React-Codegen: 3b3b818a5f579a2b5b3411c1051cadfa06240f36 + React-Core: 88838ed1724c64905fc6c0811d752828a92e395b + React-CoreModules: cd238b4bb8dc8529ccc8b34ceae7267b04ce1882 + React-cxxreact: 291bfab79d8098dc5ebab98f62e6bdfe81b3955a + React-Fabric: 8f2ea388aea7ac634b77a8123fa84c67d8f238cf + React-graphics: d64349105d6a5fee1fe1ebf1f299e60c56ce050e + React-hermes: b1e67e9a81c71745704950516f40ee804349641c + React-jsi: c9d5b563a6af6bb57034a82c2b0d39d0a7483bdc + React-jsiexecutor: d6b7fa9260aa3cb40afee0507e3bc1d17ecaa6f2 + React-jsinspector: 1f51e775819199d3fe9410e69ee8d4c4161c7b06 + React-logger: 0d58569ec51d30d1792c5e86a8e3b78d24b582c6 + react-native-webassembly: 059bb71a8e754a9e58c408e744ae59e98205dfc5 + React-perflogger: 0bb0522a12e058f6eb69d888bc16f40c16c4b907 + React-RCTActionSheet: bfd675a10f06a18728ea15d82082d48f228a213a + React-RCTAnimation: 2fa220b2052ec75b733112aca39143d34546a941 + React-RCTAppDelegate: 5c8fa743a35915ca97e25b255a0783ab2beb17f4 + React-RCTBlob: d0336111f46301ae8aba2e161817e451aad72dd6 + React-RCTFabric: 4d1cb06de10f3d2435af88a244e140fe06758e4b + React-RCTImage: fec592c46edb7c12a9cde08780bdb4a688416c62 + React-RCTLinking: 14eccac5d2a3b34b89dbfa29e8ef6219a153fe2d + React-RCTNetwork: 1fbce92e772e39ca3687a2ebb854501ff6226dd7 + React-RCTSettings: 1abea36c9bb16d9979df6c4b42e2ea281b4bbcc5 + React-RCTText: 15355c41561a9f43dfd23616d0a0dd40ba05ed61 + React-RCTVibration: ad17efcfb2fa8f6bfd8ac0cf48d96668b8b28e0b + React-rncore: 494f091f1b97538bbdd62edf1a793e41558671a5 + React-runtimeexecutor: 8fa50b38df6b992c76537993a2b0553d3b088004 + ReactCommon: b49a4b00ca6d181ff74b17c12b2d59ac4add0bde + SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608 + Yoga: 79dd7410de6f8ad73a77c868d3d368843f0c93e0 + YogaKit: f782866e155069a2cca2517aafea43200b01fd5a + +PODFILE CHECKSUM: e89c1bacc98f05863117b4b82401538b27866eb6 + +COCOAPODS: 1.11.3 diff --git a/example/ios/WebassemblyExample-Bridging-Header.h b/example/ios/WebassemblyExample-Bridging-Header.h new file mode 100644 index 0000000..e11d920 --- /dev/null +++ b/example/ios/WebassemblyExample-Bridging-Header.h @@ -0,0 +1,3 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// diff --git a/example/ios/WebassemblyExample.xcodeproj/project.pbxproj b/example/ios/WebassemblyExample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c6578b4 --- /dev/null +++ b/example/ios/WebassemblyExample.xcodeproj/project.pbxproj @@ -0,0 +1,704 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 00E356F31AD99517003FC87E /* WebassemblyExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* WebassemblyExampleTests.m */; }; + 0C80B921A6F3F58F76C31292 /* libPods-WebassemblyExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-WebassemblyExample.a */; }; + 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 7699B88040F8A987B510C191 /* libPods-WebassemblyExample-WebassemblyExampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-WebassemblyExample-WebassemblyExampleTests.a */; }; + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 13B07F861A680F5B00A75B9A; + remoteInfo = WebassemblyExample; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 00E356EE1AD99517003FC87E /* WebassemblyExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WebassemblyExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 00E356F21AD99517003FC87E /* WebassemblyExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WebassemblyExampleTests.m; sourceTree = ""; }; + 13B07F961A680F5B00A75B9A /* WebassemblyExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebassemblyExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = WebassemblyExample/AppDelegate.h; sourceTree = ""; }; + 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = WebassemblyExample/AppDelegate.mm; sourceTree = ""; }; + 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = WebassemblyExample/Images.xcassets; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = WebassemblyExample/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = WebassemblyExample/main.m; sourceTree = ""; }; + 19F6CBCC0A4E27FBF8BF4A61 /* libPods-WebassemblyExample-WebassemblyExampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WebassemblyExample-WebassemblyExampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B4392A12AC88292D35C810B /* Pods-WebassemblyExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebassemblyExample.debug.xcconfig"; path = "Target Support Files/Pods-WebassemblyExample/Pods-WebassemblyExample.debug.xcconfig"; sourceTree = ""; }; + 5709B34CF0A7D63546082F79 /* Pods-WebassemblyExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebassemblyExample.release.xcconfig"; path = "Target Support Files/Pods-WebassemblyExample/Pods-WebassemblyExample.release.xcconfig"; sourceTree = ""; }; + 5B7EB9410499542E8C5724F5 /* Pods-WebassemblyExample-WebassemblyExampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebassemblyExample-WebassemblyExampleTests.debug.xcconfig"; path = "Target Support Files/Pods-WebassemblyExample-WebassemblyExampleTests/Pods-WebassemblyExample-WebassemblyExampleTests.debug.xcconfig"; sourceTree = ""; }; + 5DCACB8F33CDC322A6C60F78 /* libPods-WebassemblyExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WebassemblyExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = WebassemblyExample/LaunchScreen.storyboard; sourceTree = ""; }; + 89C6BE57DB24E9ADA2F236DE /* Pods-WebassemblyExample-WebassemblyExampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebassemblyExample-WebassemblyExampleTests.release.xcconfig"; path = "Target Support Files/Pods-WebassemblyExample-WebassemblyExampleTests/Pods-WebassemblyExample-WebassemblyExampleTests.release.xcconfig"; sourceTree = ""; }; + ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 00E356EB1AD99517003FC87E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7699B88040F8A987B510C191 /* libPods-WebassemblyExample-WebassemblyExampleTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C80B921A6F3F58F76C31292 /* libPods-WebassemblyExample.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 00E356EF1AD99517003FC87E /* WebassemblyExampleTests */ = { + isa = PBXGroup; + children = ( + 00E356F21AD99517003FC87E /* WebassemblyExampleTests.m */, + 00E356F01AD99517003FC87E /* Supporting Files */, + ); + path = WebassemblyExampleTests; + sourceTree = ""; + }; + 00E356F01AD99517003FC87E /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 00E356F11AD99517003FC87E /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 13B07FAE1A68108700A75B9A /* WebassemblyExample */ = { + isa = PBXGroup; + children = ( + 13B07FAF1A68108700A75B9A /* AppDelegate.h */, + 13B07FB01A68108700A75B9A /* AppDelegate.mm */, + 13B07FB51A68108700A75B9A /* Images.xcassets */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, + 13B07FB71A68108700A75B9A /* main.m */, + ); + name = WebassemblyExample; + sourceTree = ""; + }; + 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ED297162215061F000B7C4FE /* JavaScriptCore.framework */, + 5DCACB8F33CDC322A6C60F78 /* libPods-WebassemblyExample.a */, + 19F6CBCC0A4E27FBF8BF4A61 /* libPods-WebassemblyExample-WebassemblyExampleTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + ); + name = Libraries; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 13B07FAE1A68108700A75B9A /* WebassemblyExample */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 00E356EF1AD99517003FC87E /* WebassemblyExampleTests */, + 83CBBA001A601CBA00E9B192 /* Products */, + 2D16E6871FA4F8E400B85C8A /* Frameworks */, + BBD78D7AC51CEA395F1C20DB /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* WebassemblyExample.app */, + 00E356EE1AD99517003FC87E /* WebassemblyExampleTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + BBD78D7AC51CEA395F1C20DB /* Pods */ = { + isa = PBXGroup; + children = ( + 3B4392A12AC88292D35C810B /* Pods-WebassemblyExample.debug.xcconfig */, + 5709B34CF0A7D63546082F79 /* Pods-WebassemblyExample.release.xcconfig */, + 5B7EB9410499542E8C5724F5 /* Pods-WebassemblyExample-WebassemblyExampleTests.debug.xcconfig */, + 89C6BE57DB24E9ADA2F236DE /* Pods-WebassemblyExample-WebassemblyExampleTests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 00E356ED1AD99517003FC87E /* WebassemblyExampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "WebassemblyExampleTests" */; + buildPhases = ( + A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */, + 00E356EA1AD99517003FC87E /* Sources */, + 00E356EB1AD99517003FC87E /* Frameworks */, + 00E356EC1AD99517003FC87E /* Resources */, + C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */, + F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 00E356F51AD99517003FC87E /* PBXTargetDependency */, + ); + name = WebassemblyExampleTests; + productName = WebassemblyExampleTests; + productReference = 00E356EE1AD99517003FC87E /* WebassemblyExampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 13B07F861A680F5B00A75B9A /* WebassemblyExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "WebassemblyExample" */; + buildPhases = ( + C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */, + FD10A7F022414F080027D42C /* Start Packager */, + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */, + E235C05ADACE081382539298 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = WebassemblyExample; + productName = WebassemblyExample; + productReference = 13B07F961A680F5B00A75B9A /* WebassemblyExample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1210; + TargetAttributes = { + 00E356ED1AD99517003FC87E = { + CreatedOnToolsVersion = 6.2; + TestTargetID = 13B07F861A680F5B00A75B9A; + }; + 13B07F861A680F5B00A75B9A = { + LastSwiftMigration = 1120; + }; + }; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "WebassemblyExample" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* WebassemblyExample */, + 00E356ED1AD99517003FC87E /* WebassemblyExampleTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 00E356EC1AD99517003FC87E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/.xcode.env.local", + "$(SRCROOT)/.xcode.env", + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; + }; + 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-WebassemblyExample/Pods-WebassemblyExample-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-WebassemblyExample/Pods-WebassemblyExample-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-WebassemblyExample/Pods-WebassemblyExample-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-WebassemblyExample-WebassemblyExampleTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-WebassemblyExample-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-WebassemblyExample-WebassemblyExampleTests/Pods-WebassemblyExample-WebassemblyExampleTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-WebassemblyExample-WebassemblyExampleTests/Pods-WebassemblyExample-WebassemblyExampleTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-WebassemblyExample-WebassemblyExampleTests/Pods-WebassemblyExample-WebassemblyExampleTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-WebassemblyExample/Pods-WebassemblyExample-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-WebassemblyExample/Pods-WebassemblyExample-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-WebassemblyExample/Pods-WebassemblyExample-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-WebassemblyExample-WebassemblyExampleTests/Pods-WebassemblyExample-WebassemblyExampleTests-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-WebassemblyExample-WebassemblyExampleTests/Pods-WebassemblyExample-WebassemblyExampleTests-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-WebassemblyExample-WebassemblyExampleTests/Pods-WebassemblyExample-WebassemblyExampleTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + FD10A7F022414F080027D42C /* Start Packager */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Start Packager"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 00E356EA1AD99517003FC87E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00E356F31AD99517003FC87E /* WebassemblyExampleTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, + 13B07FC11A68108700A75B9A /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 13B07F861A680F5B00A75B9A /* WebassemblyExample */; + targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 00E356F61AD99517003FC87E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-WebassemblyExample-WebassemblyExampleTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = WebassemblyExampleTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WebassemblyExample.app/WebassemblyExample"; + }; + name = Debug; + }; + 00E356F71AD99517003FC87E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-WebassemblyExample-WebassemblyExampleTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COPY_PHASE_STRIP = NO; + INFOPLIST_FILE = WebassemblyExampleTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WebassemblyExample.app/WebassemblyExample"; + }; + name = Release; + }; + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-WebassemblyExample.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = WebassemblyExample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = WebassemblyExample; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-WebassemblyExample.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = WebassemblyExample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = WebassemblyExample; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + ); + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + ); + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "WebassemblyExampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 00E356F61AD99517003FC87E /* Debug */, + 00E356F71AD99517003FC87E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "WebassemblyExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "WebassemblyExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/example/ios/WebassemblyExample.xcodeproj/xcshareddata/xcschemes/WebassemblyExample.xcscheme b/example/ios/WebassemblyExample.xcodeproj/xcshareddata/xcschemes/WebassemblyExample.xcscheme new file mode 100644 index 0000000..0c9f415 --- /dev/null +++ b/example/ios/WebassemblyExample.xcodeproj/xcshareddata/xcschemes/WebassemblyExample.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/WebassemblyExample.xcworkspace/contents.xcworkspacedata b/example/ios/WebassemblyExample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1370559 --- /dev/null +++ b/example/ios/WebassemblyExample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/example/ios/WebassemblyExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/WebassemblyExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/WebassemblyExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/WebassemblyExample/AppDelegate.h b/example/ios/WebassemblyExample/AppDelegate.h new file mode 100644 index 0000000..5d28082 --- /dev/null +++ b/example/ios/WebassemblyExample/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : RCTAppDelegate + +@end diff --git a/example/ios/WebassemblyExample/AppDelegate.mm b/example/ios/WebassemblyExample/AppDelegate.mm new file mode 100644 index 0000000..98c5eff --- /dev/null +++ b/example/ios/WebassemblyExample/AppDelegate.mm @@ -0,0 +1,36 @@ +#import "AppDelegate.h" + +#import + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.moduleName = @"WebassemblyExample"; + // You can add your custom initial props in the dictionary below. + // They will be passed down to the ViewController used by React Native. + self.initialProps = @{}; + + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge +{ +#if DEBUG + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; +#else + return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; +#endif +} + +/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. +/// +/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html +/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). +/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`. +- (BOOL)concurrentRootEnabled +{ + return true; +} + +@end diff --git a/example/ios/WebassemblyExample/Images.xcassets/AppIcon.appiconset/Contents.json b/example/ios/WebassemblyExample/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..8121323 --- /dev/null +++ b/example/ios/WebassemblyExample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,53 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example/ios/WebassemblyExample/Images.xcassets/Contents.json b/example/ios/WebassemblyExample/Images.xcassets/Contents.json new file mode 100644 index 0000000..2d92bd5 --- /dev/null +++ b/example/ios/WebassemblyExample/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/WebassemblyExample/Info.plist b/example/ios/WebassemblyExample/Info.plist new file mode 100644 index 0000000..7030119 --- /dev/null +++ b/example/ios/WebassemblyExample/Info.plist @@ -0,0 +1,55 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + WebassemblyExample + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + + + NSLocationWhenInUseUsageDescription + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/example/ios/WebassemblyExample/LaunchScreen.storyboard b/example/ios/WebassemblyExample/LaunchScreen.storyboard new file mode 100644 index 0000000..e3ea514 --- /dev/null +++ b/example/ios/WebassemblyExample/LaunchScreen.storyboard @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/WebassemblyExample/main.m b/example/ios/WebassemblyExample/main.m new file mode 100644 index 0000000..d645c72 --- /dev/null +++ b/example/ios/WebassemblyExample/main.m @@ -0,0 +1,10 @@ +#import + +#import "AppDelegate.h" + +int main(int argc, char *argv[]) +{ + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/example/ios/WebassemblyExampleTests/Info.plist b/example/ios/WebassemblyExampleTests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/example/ios/WebassemblyExampleTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/example/ios/WebassemblyExampleTests/WebassemblyExampleTests.m b/example/ios/WebassemblyExampleTests/WebassemblyExampleTests.m new file mode 100644 index 0000000..2e929cd --- /dev/null +++ b/example/ios/WebassemblyExampleTests/WebassemblyExampleTests.m @@ -0,0 +1,66 @@ +#import +#import + +#import +#import + +#define TIMEOUT_SECONDS 600 +#define TEXT_TO_LOOK_FOR @"Welcome to React" + +@interface WebassemblyExampleTests : XCTestCase + +@end + +@implementation WebassemblyExampleTests + +- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test +{ + if (test(view)) { + return YES; + } + for (UIView *subview in [view subviews]) { + if ([self findSubviewInView:subview matching:test]) { + return YES; + } + } + return NO; +} + +- (void)testRendersWelcomeScreen +{ + UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; + BOOL foundElement = NO; + + __block NSString *redboxError = nil; +#ifdef DEBUG + RCTSetLogFunction( + ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { + if (level >= RCTLogLevelError) { + redboxError = message; + } + }); +#endif + + while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + + foundElement = [self findSubviewInView:vc.view + matching:^BOOL(UIView *view) { + if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { + return YES; + } + return NO; + }]; + } + +#ifdef DEBUG + RCTSetLogFunction(RCTDefaultLogFunction); +#endif + + XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); + XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); +} + +@end diff --git a/example/metro.config.js b/example/metro.config.js new file mode 100644 index 0000000..b5c0064 --- /dev/null +++ b/example/metro.config.js @@ -0,0 +1,40 @@ +const path = require('path'); +const escape = require('escape-string-regexp'); +const exclusionList = require('metro-config/src/defaults/exclusionList'); +const pak = require('../package.json'); + +const root = path.resolve(__dirname, '..'); + +const modules = Object.keys({ + ...pak.peerDependencies, +}); + +module.exports = { + projectRoot: __dirname, + watchFolders: [root], + + // We need to make sure that only one version is loaded for peerDependencies + // So we block them at the root, and alias them to the versions in example's node_modules + resolver: { + blacklistRE: exclusionList( + modules.map( + (m) => + new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) + ) + ), + + extraNodeModules: modules.reduce((acc, name) => { + acc[name] = path.join(__dirname, 'node_modules', name); + return acc; + }, {}), + }, + + transformer: { + getTransformOptions: async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: true, + }, + }), + }, +}; diff --git a/example/package.json b/example/package.json new file mode 100644 index 0000000..746899b --- /dev/null +++ b/example/package.json @@ -0,0 +1,23 @@ +{ + "name": "WebassemblyExample", + "version": "0.0.1", + "private": true, + "scripts": { + "android": "react-native run-android", + "ios": "react-native run-ios", + "start": "react-native start", + "pods": "pod-install --quiet" + }, + "dependencies": { + "axios": "1.3.4", + "react": "18.2.0", + "react-native": "0.71.4" + }, + "devDependencies": { + "@babel/core": "^7.20.0", + "@babel/preset-env": "^7.20.0", + "@babel/runtime": "^7.20.0", + "babel-plugin-module-resolver": "^4.1.0", + "metro-react-native-babel-preset": "0.73.8" + } +} diff --git a/example/react-native.config.js b/example/react-native.config.js new file mode 100644 index 0000000..a516695 --- /dev/null +++ b/example/react-native.config.js @@ -0,0 +1,10 @@ +const path = require('path'); +const pak = require('../package.json'); + +module.exports = { + dependencies: { + [pak.name]: { + root: path.join(__dirname, '..'), + }, + }, +}; diff --git a/example/src/App.tsx b/example/src/App.tsx new file mode 100644 index 0000000..4158639 --- /dev/null +++ b/example/src/App.tsx @@ -0,0 +1,48 @@ +import * as React from 'react'; +import { Button, StyleSheet, View } from 'react-native'; + +import { useWasmCircomRuntime, useWasmHelloWorld } from './hooks'; + +export default function App() { + const helloWorld = useWasmHelloWorld(); + const helloWorldResult = + 'result' in helloWorld ? helloWorld.result : undefined; + + const { calculateWTNSBin } = useWasmCircomRuntime(); + + React.useEffect(() => { + if (!helloWorldResult) return; + + const result = helloWorldResult.instance.exports.add(103, 202); + + if (result !== 305) throw new Error('Failed to add.'); + }, [helloWorldResult]); + + return ( + +