Merge pull request #1864 from tree-sitter/wasm-language

Add optional WASM feature to the native library, allowing it to run wasm-compiled parsers via wasmtime
This commit is contained in:
Max Brunsfeld 2023-11-28 12:08:47 -08:00 committed by GitHub
commit 034f0d0280
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
64 changed files with 9642 additions and 517 deletions

View file

@ -41,10 +41,6 @@ jobs:
- linux-x64 #
- linux-x86 #
- linux-i586 #
- linux-mips #
- linux-mips64 #
- linux-mipsel #
- linux-mips64el #
- linux-powerpc #
- linux-powerpc64 #
- linux-powerpc64el #
@ -67,13 +63,9 @@ jobs:
- { platform: linux-armhf , target: arm-unknown-linux-gnueabihf , os: ubuntu-latest , use-cross: true }
- { platform: linux-armv5te , target: armv5te-unknown-linux-gnueabi , os: ubuntu-latest , use-cross: true }
- { platform: linux-armv7l , target: armv7-unknown-linux-gnueabihf , os: ubuntu-latest , use-cross: true }
- { platform: linux-x64 , target: x86_64-unknown-linux-gnu , os: ubuntu-20.04 } #2272
- { platform: linux-x64 , target: x86_64-unknown-linux-gnu , os: ubuntu-20.04 , enable-wasm: true } #2272
- { platform: linux-x86 , target: i686-unknown-linux-gnu , os: ubuntu-latest , use-cross: true }
- { platform: linux-i586 , target: i586-unknown-linux-gnu , os: ubuntu-latest , use-cross: true }
- { platform: linux-mips , target: mips-unknown-linux-gnu , os: ubuntu-latest , use-cross: true }
- { platform: linux-mips64 , target: mips64-unknown-linux-gnuabi64 , os: ubuntu-latest , use-cross: true }
- { platform: linux-mipsel , target: mipsel-unknown-linux-gnu , os: ubuntu-latest , use-cross: true }
- { platform: linux-mips64el , target: mips64el-unknown-linux-gnuabi64 , os: ubuntu-latest , use-cross: true }
- { platform: linux-powerpc , target: powerpc-unknown-linux-gnu , os: ubuntu-latest , use-cross: true }
- { platform: linux-powerpc64 , target: powerpc64-unknown-linux-gnu , os: ubuntu-latest , use-cross: true }
- { platform: linux-powerpc64el , target: powerpc64le-unknown-linux-gnu , os: ubuntu-latest , use-cross: true }
@ -82,10 +74,10 @@ jobs:
- { platform: linux-sparc64 , target: sparc64-unknown-linux-gnu , os: ubuntu-latest , use-cross: true }
- { platform: linux-thumbv7neon , target: thumbv7neon-unknown-linux-gnueabihf , os: ubuntu-latest , use-cross: true }
- { platform: windows-arm64 , target: aarch64-pc-windows-msvc , os: windows-latest }
- { platform: windows-x64 , target: x86_64-pc-windows-msvc , os: windows-latest }
- { platform: windows-x64 , target: x86_64-pc-windows-msvc , os: windows-latest , enable-wasm: true }
- { platform: windows-x86 , target: i686-pc-windows-msvc , os: windows-latest }
- { platform: macos-arm64 , target: aarch64-apple-darwin , os: macos-latest }
- { platform: macos-x64 , target: x86_64-apple-darwin , os: macos-latest }
- { platform: macos-arm64 , target: aarch64-apple-darwin , os: macos-latest , enable-wasm: true }
- { platform: macos-x64 , target: x86_64-apple-darwin , os: macos-latest , enable-wasm: true }
# Cross compilers for C library
- { platform: linux-arm64 , cc: aarch64-linux-gnu-gcc , ar: aarch64-linux-gnu-ar }
@ -95,10 +87,6 @@ jobs:
- { platform: linux-armv7l , cc: arm-linux-gnueabihf-gcc , ar: arm-linux-gnueabihf-ar }
- { platform: linux-x86 , cc: i686-linux-gnu-gcc , ar: i686-linux-gnu-ar }
- { platform: linux-i586 , cc: i686-linux-gnu-gcc , ar: i686-linux-gnu-ar }
- { platform: linux-mips , cc: mips-linux-gnu-gcc , ar: mips-linux-gnu-ar }
- { platform: linux-mips64 , cc: mips64-linux-gnuabi64-gcc , ar: mips64-linux-gnuabi64-ar }
- { platform: linux-mipsel , cc: mipsel-linux-gnu-gcc , ar: mipsel-linux-gnu-ar }
- { platform: linux-mips64el , cc: mips64el-linux-gnuabi64-gcc , ar: mips64el-linux-gnuabi64-ar }
- { platform: linux-powerpc , cc: powerpc-linux-gnu-gcc , ar: powerpc-linux-gnu-ar }
- { platform: linux-powerpc64 , cc: powerpc64-linux-gnu-gcc , ar: powerpc64-linux-gnu-ar }
- { platform: linux-powerpc64el , cc: powerpc64le-linux-gnu-gcc , ar: powerpc64le-linux-gnu-ar }
@ -107,12 +95,6 @@ jobs:
- { platform: linux-sparc64 , cc: sparc64-linux-gnu-gcc , ar: sparc64-linux-gnu-ar }
- { platform: linux-thumbv7neon , cc: arm-linux-gnueabihf-gcc , ar: arm-linux-gnueabihf-ar }
# Rust toolchains
- { platform: linux-mips , rust-toolchain: 1.71.1 }
- { platform: linux-mips64 , rust-toolchain: 1.71.1 }
- { platform: linux-mipsel , rust-toolchain: 1.71.1 }
- { platform: linux-mips64el , rust-toolchain: 1.71.1 }
# See #2041 tree-sitter issue
- { platform: windows-x64 , rust-test-threads: 1 }
- { platform: windows-x86 , rust-test-threads: 1 }
@ -138,7 +120,7 @@ jobs:
- name: Read Emscripten version
run: |
echo "EMSCRIPTEN_VERSION=$(cat cli/emscripten-version)" >> $GITHUB_ENV
echo "EMSCRIPTEN_VERSION=$(cat cli/loader/emscripten-version)" >> $GITHUB_ENV
- name: Install Emscripten
if: ${{ !matrix.cli-only && !matrix.use-cross }}
@ -184,6 +166,7 @@ jobs:
CC: ${{ matrix.cc }}
AR: ${{ matrix.ar }}
IS_WINDOWS: ${{ contains(matrix.os, 'windows') }}
ENABLE_WASM: ${{ matrix.enable-wasm }}
run: |
PATH="$PWD/.github/scripts:$PATH"
echo "$PWD/.github/scripts" >> $GITHUB_PATH
@ -200,6 +183,10 @@ jobs:
[ "$IS_WINDOWS" = "false" ] && echo "CFLAGS=-Werror" >> $GITHUB_ENV
if [ "$ENABLE_WASM" == "true" ]; then
echo "CLI_FEATURES=wasm" >> $GITHUB_ENV
fi
if [ "$USE_CROSS" == "true" ]; then
echo "BUILD_CMD=cross" >> $GITHUB_ENV
runner=$(BUILD_CMD=cross cross.sh bash -c "env | sed -nr '/^CARGO_TARGET_.*_RUNNER=/s///p'")
@ -215,7 +202,7 @@ jobs:
run: script/build-wasm
- name: Build CLI
run: $BUILD_CMD build --release --target=${{ matrix.target }}
run: $BUILD_CMD build --release --target=${{ matrix.target }} --features=${CLI_FEATURES}
- name: Info about CLI
if: ${{ startsWith(matrix.platform, 'linux') }}

812
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -19,7 +19,7 @@ OBJ := $(SRC:.c=.o)
# define default flags, and override to append mandatory flags
override CFLAGS := -O3 -std=gnu99 -fPIC -fvisibility=hidden -Wall -Wextra -Wshadow $(CFLAGS)
override CFLAGS += -Ilib/src -Ilib/include
override CFLAGS += -Ilib/src -Ilib/src/wasm -Ilib/include
# ABI versioning
SONAME_MAJOR := 0

View file

@ -20,6 +20,10 @@ doc = false
name = "benchmark"
harness = false
[features]
# default = ["wasm"]
wasm = ["tree-sitter/wasm", "tree-sitter-loader/wasm"]
[dependencies]
ansi_term = "0.12.1"
anyhow = "1.0.72"
@ -38,8 +42,8 @@ regex = "1.9.1"
regex-syntax = "0.7.4"
rustc-hash = "1.1.0"
semver = "1.0.18"
# Due to https://github.com/serde-rs/serde/issues/2538
serde = { version = "1.0, < 1.0.172", features = ["derive"] }
serde = "1.0.188"
serde_derive = "1.0"
smallbitvec = "2.5.1"
tiny_http = "0.12.0"
walkdir = "2.3.3"

View file

@ -16,12 +16,6 @@ fn main() {
"cargo:rustc-env={}={}",
"RUST_BINDING_VERSION", rust_binding_version,
);
let emscripten_version = fs::read_to_string("emscripten-version").unwrap();
println!(
"cargo:rustc-env={}={}",
"EMSCRIPTEN_VERSION", emscripten_version,
);
}
fn web_playground_files_present() -> bool {

View file

@ -14,8 +14,7 @@ rust-version.workspace = true
[dependencies]
anyhow = "1.0"
dirs = "3.0"
# Due to https://github.com/serde-rs/serde/issues/2538
serde = { version = "1.0, < 1.0.172", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
[dependencies.serde_json]
version = "1.0"

View file

@ -11,6 +11,9 @@ categories = ["command-line-utilities", "parsing"]
repository = "https://github.com/tree-sitter/tree-sitter"
rust-version.workspace = true
[features]
wasm = ["tree-sitter/wasm"]
[dependencies]
anyhow = "1.0"
cc = "^1.0.58"
@ -18,8 +21,8 @@ dirs = "3.0"
libloading = "0.7"
once_cell = "1.7"
regex = "1"
# Due to https://github.com/serde-rs/serde/issues/2538
serde = { version = "1.0, < 1.0.172", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
which = "4.1.0"
[dependencies.serde_json]
version = "1.0"

View file

@ -3,4 +3,10 @@ fn main() {
"cargo:rustc-env=BUILD_TARGET={}",
std::env::var("TARGET").unwrap()
);
let emscripten_version = std::fs::read_to_string("emscripten-version").unwrap();
println!(
"cargo:rustc-env={}={}",
"EMSCRIPTEN_VERSION", emscripten_version,
);
}

View file

@ -6,6 +6,7 @@ use once_cell::unsync::OnceCell;
use regex::{Regex, RegexBuilder};
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::io::BufReader;
use std::ops::Range;
use std::path::{Path, PathBuf};
@ -16,6 +17,9 @@ use std::{env, fs, mem};
use tree_sitter::{Language, QueryError, QueryErrorKind};
use tree_sitter_highlight::HighlightConfiguration;
use tree_sitter_tags::{Error as TagsError, TagsConfiguration};
use which::which;
pub const EMSCRIPTEN_TAG: &'static str = concat!("emscripten/emsdk:", env!("EMSCRIPTEN_VERSION"));
#[derive(Default, Deserialize, Serialize)]
pub struct Config {
@ -105,6 +109,9 @@ pub struct Loader {
highlight_names: Box<Mutex<Vec<String>>>,
use_all_highlight_names: bool,
debug_build: bool,
#[cfg(feature = "wasm")]
wasm_store: Mutex<Option<tree_sitter::WasmStore>>,
}
unsafe impl Send for Loader {}
@ -132,6 +139,9 @@ impl Loader {
highlight_names: Box::new(Mutex::new(Vec::new())),
use_all_highlight_names: true,
debug_build: false,
#[cfg(feature = "wasm")]
wasm_store: Default::default(),
}
}
@ -319,8 +329,6 @@ impl Loader {
pub fn load_language_at_path(&self, src_path: &Path, header_path: &Path) -> Result<Language> {
let grammar_path = src_path.join("grammar.json");
let parser_path = src_path.join("parser.c");
let mut scanner_path = src_path.join("scanner.c");
#[derive(Deserialize)]
struct GrammarJSON {
@ -331,116 +339,156 @@ impl Loader {
let grammar_json: GrammarJSON = serde_json::from_reader(BufReader::new(&mut grammar_file))
.with_context(|| "Failed to parse grammar.json")?;
let scanner_path = if scanner_path.exists() {
Some(scanner_path)
} else {
scanner_path.set_extension("cc");
if scanner_path.exists() {
Some(scanner_path)
} else {
None
}
};
self.load_language_from_sources(
&grammar_json.name,
header_path,
&parser_path,
scanner_path.as_deref(),
)
self.load_language_at_path_with_name(src_path, &header_path, &grammar_json.name)
}
pub fn load_language_from_sources(
pub fn load_language_at_path_with_name(
&self,
name: &str,
src_path: &Path,
header_path: &Path,
parser_path: &Path,
scanner_path: Option<&Path>,
name: &str,
) -> Result<Language> {
let mut lib_name = name.to_string();
let language_fn_name = format!("tree_sitter_{}", replace_dashes_with_underscores(name));
if self.debug_build {
lib_name.push_str(".debug._");
}
fs::create_dir_all(&self.parser_lib_path)?;
let mut library_path = self.parser_lib_path.join(lib_name);
library_path.set_extension(DYLIB_EXTENSION);
let recompile = needs_recompile(&library_path, parser_path, scanner_path)
let parser_path = src_path.join("parser.c");
let scanner_path = self.get_scanner_path(&src_path);
#[cfg(feature = "wasm")]
if self.wasm_store.lock().unwrap().is_some() {
library_path.set_extension("wasm");
}
let recompile = needs_recompile(&library_path, &parser_path, scanner_path.as_deref())
.with_context(|| "Failed to compare source and binary timestamps")?;
if recompile {
fs::create_dir_all(&self.parser_lib_path)?;
let mut config = cc::Build::new();
config
.cpp(true)
.opt_level(2)
.cargo_metadata(false)
.target(BUILD_TARGET)
.host(BUILD_TARGET)
.flag_if_supported("-Werror=implicit-function-declaration");
let compiler = config.get_compiler();
let mut command = Command::new(compiler.path());
for (key, value) in compiler.env() {
command.env(key, value);
#[cfg(feature = "wasm")]
if let Some(wasm_store) = self.wasm_store.lock().unwrap().as_mut() {
if recompile {
self.compile_parser_to_wasm(
name,
src_path,
scanner_path
.as_ref()
.and_then(|p| p.strip_prefix(&src_path).ok()),
&library_path,
false,
)?;
}
if compiler.is_like_msvc() {
command.args(["/nologo", "/LD", "/I"]).arg(header_path);
if self.debug_build {
command.arg("/Od");
let wasm_bytes = fs::read(&library_path)?;
return Ok(wasm_store.load_language(name, &wasm_bytes)?);
}
{
if recompile {
self.compile_parser_to_dylib(
header_path,
&parser_path,
&scanner_path,
&library_path,
)?;
}
let library = unsafe { Library::new(&library_path) }
.with_context(|| format!("Error opening dynamic library {:?}", &library_path))?;
let language = unsafe {
let language_fn: Symbol<unsafe extern "C" fn() -> Language> = library
.get(language_fn_name.as_bytes())
.with_context(|| format!("Failed to load symbol {}", language_fn_name))?;
language_fn()
};
mem::forget(library);
return Ok(language);
}
}
fn compile_parser_to_dylib(
&self,
header_path: &Path,
parser_path: &Path,
scanner_path: &Option<PathBuf>,
library_path: &PathBuf,
) -> Result<(), Error> {
let mut config = cc::Build::new();
config
.cpp(true)
.opt_level(2)
.cargo_metadata(false)
.target(BUILD_TARGET)
.host(BUILD_TARGET)
.flag_if_supported("-Werror=implicit-function-declaration");
let compiler = config.get_compiler();
let mut command = Command::new(compiler.path());
for (key, value) in compiler.env() {
command.env(key, value);
}
if compiler.is_like_msvc() {
command.args(&["/nologo", "/LD", "/I"]).arg(header_path);
if self.debug_build {
command.arg("/Od");
} else {
command.arg("/O2");
}
command.arg(parser_path);
if let Some(scanner_path) = scanner_path.as_ref() {
command.arg(scanner_path);
}
command
.arg("/link")
.arg(format!("/out:{}", library_path.to_str().unwrap()));
} else {
command
.arg("-shared")
.arg("-fno-exceptions")
.arg("-g")
.arg("-I")
.arg(header_path)
.arg("-o")
.arg(&library_path);
if !cfg!(windows) {
command.arg("-fPIC");
}
if self.debug_build {
command.arg("-O0");
} else {
command.arg("-O2");
}
// For conditional compilation of external scanner code when
// used internally by `tree-siteer parse` and other sub commands.
command.arg("-DTREE_SITTER_INTERNAL_BUILD");
if let Some(scanner_path) = scanner_path.as_ref() {
if scanner_path.extension() == Some("c".as_ref()) {
command.arg("-xc").arg("-std=c99").arg(scanner_path);
} else {
command.arg("/O2");
}
command.arg(parser_path);
if let Some(scanner_path) = scanner_path.as_ref() {
command.arg(scanner_path);
}
command
.arg("/link")
.arg(format!("/out:{}", library_path.to_str().unwrap()));
} else {
command
.arg("-shared")
.arg("-fno-exceptions")
.arg("-g")
.arg("-I")
.arg(header_path)
.arg("-o")
.arg(&library_path);
if !cfg!(windows) {
command.arg("-fPIC");
}
if self.debug_build {
command.arg("-O0");
} else {
command.arg("-O2");
}
// For conditional compilation of external scanner code when
// used internally by `tree-siteer parse` and other sub commands.
command.arg("-DTREE_SITTER_INTERNAL_BUILD");
if let Some(scanner_path) = scanner_path.as_ref() {
if scanner_path.extension() == Some("c".as_ref()) {
command.arg("-xc").arg("-std=c99").arg(scanner_path);
} else {
command.arg(scanner_path);
}
}
command.arg("-xc").arg(parser_path);
}
command.arg("-xc").arg(parser_path);
}
let output = command
.output()
.with_context(|| "Failed to execute C compiler")?;
if !output.status.success() {
return Err(anyhow!(
"Parser compilation failed.\nStdout: {}\nStderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
));
}
let output = command
.output()
.with_context(|| "Failed to execute C compiler")?;
if !output.status.success() {
return Err(anyhow!(
"Parser compilation failed.\nStdout: {}\nStderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
));
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
@ -473,17 +521,103 @@ impl Loader {
}
}
let library = unsafe { Library::new(&library_path) }
.with_context(|| format!("Error opening dynamic library {:?}", &library_path))?;
let language_fn_name = format!("tree_sitter_{}", replace_dashes_with_underscores(name));
let language = unsafe {
let language_fn: Symbol<unsafe extern "C" fn() -> Language> = library
.get(language_fn_name.as_bytes())
.with_context(|| format!("Failed to load symbol {}", language_fn_name))?;
language_fn()
};
mem::forget(library);
Ok(language)
Ok(())
}
pub fn compile_parser_to_wasm(
&self,
language_name: &str,
src_path: &Path,
scanner_filename: Option<&Path>,
output_path: &PathBuf,
force_docker: bool,
) -> Result<(), Error> {
let emcc_bin = if cfg!(windows) { "emcc.bat" } else { "emcc" };
let emcc_path = which(emcc_bin)
.ok()
.and_then(|p| Command::new(&p).output().and(Ok(p)).ok());
let mut command;
if emcc_path.is_some() && !force_docker {
command = Command::new(emcc_path.unwrap());
command.current_dir(&src_path);
} else if Command::new("docker").output().is_ok() {
command = Command::new("docker");
command.args(&["run", "--rm"]);
// Mount the parser directory as a volume
command.args(&["--workdir", "/src"]);
let mut volume_string = OsString::from(&src_path);
volume_string.push(":/src:Z");
command.args(&[OsStr::new("--volume"), &volume_string]);
// Get the current user id so that files created in the docker container will have
// the same owner.
if cfg!(unix) {
let user_id_output = Command::new("id")
.arg("-u")
.output()
.with_context(|| "Failed to get get current user id")?;
let user_id = String::from_utf8_lossy(&user_id_output.stdout);
let user_id = user_id.trim();
command.args(&["--user", user_id]);
}
// Run `emcc` in a container using the `emscripten-slim` image
command.args(&[EMSCRIPTEN_TAG, "emcc"]);
} else {
return Err(anyhow!(
"You must have either emcc or docker on your PATH to run this command"
));
}
let output_name = "output.wasm";
command.args(&[
"-o",
output_name,
"-Os",
"-s",
"WASM=1",
"-s",
"SIDE_MODULE=1",
"-s",
"TOTAL_MEMORY=33554432",
"-s",
"NODEJS_CATCH_EXIT=0",
"-s",
&format!("EXPORTED_FUNCTIONS=[\"_tree_sitter_{}\"]", language_name),
"-fno-exceptions",
"-fvisibility=hidden",
"-I",
".",
]);
if let Some(scanner_filename) = scanner_filename {
if scanner_filename
.extension()
.and_then(|ext| ext.to_str())
.map_or(false, |ext| ["cc", "cpp"].contains(&ext))
{
command.arg("-xc++");
}
command.arg(&scanner_filename);
}
command.arg("parser.c");
let output = command.output().context("Failed to run emcc command")?;
if !output.status.success() {
return Err(anyhow!(
"emcc command failed - {}",
String::from_utf8_lossy(&output.stderr)
));
}
fs::rename(&src_path.join(output_name), &output_path)
.context("failed to rename wasm output file")?;
Ok(())
}
pub fn highlight_config_for_injection_string<'a>(
@ -731,6 +865,22 @@ impl Loader {
pub fn use_debug_build(&mut self, flag: bool) {
self.debug_build = flag;
}
#[cfg(feature = "wasm")]
pub fn use_wasm(&mut self, engine: tree_sitter::wasmtime::Engine) {
*self.wasm_store.lock().unwrap() = Some(tree_sitter::WasmStore::new(engine).unwrap())
}
pub fn get_scanner_path(&self, src_path: &Path) -> Option<PathBuf> {
let mut path = src_path.join("scanner.c");
for extension in ["c", "cc", "cpp"] {
path.set_extension(extension);
if path.exists() {
return Some(path);
}
}
None
}
}
impl<'a> LanguageConfiguration<'a> {

View file

@ -4,14 +4,16 @@ use glob::glob;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::{env, fs, u64};
use tree_sitter::{ffi, Point};
use tree_sitter_cli::parse::{ParseFileOptions, ParseOutput};
use tree_sitter::{ffi, Parser, Point};
use tree_sitter_cli::{
generate, highlight, logger, parse, playground, query, tags, test, test_highlight, test_tags,
util, wasm,
generate, highlight, logger,
parse::{self, ParseFileOptions, ParseOutput},
playground, query, tags, test, test_highlight, test_tags, util, wasm,
};
use tree_sitter_config::Config;
use tree_sitter_highlight::Highlighter;
use tree_sitter_loader as loader;
use tree_sitter_tags::TagsContext;
const BUILD_VERSION: &'static str = env!("CARGO_PKG_VERSION");
const BUILD_SHA: Option<&'static str> = option_env!("BUILD_SHA");
@ -79,6 +81,9 @@ fn run() -> Result<()> {
.long("quiet")
.short("q");
let wasm_arg = Arg::with_name("wasm")
.long("wasm")
.help("compile parsers to wasm instead of native dynamic libraries");
let apply_all_captures_arg = Arg::with_name("apply-all-captures")
.help("Apply all captures to highlights")
.long("apply-all-captures");
@ -151,6 +156,7 @@ fn run() -> Result<()> {
.arg(&debug_arg)
.arg(&debug_build_arg)
.arg(&debug_graph_arg)
.arg(&wasm_arg)
.arg(Arg::with_name("output-dot").long("dot"))
.arg(Arg::with_name("output-xml").long("xml").short("x"))
.arg(
@ -242,6 +248,7 @@ fn run() -> Result<()> {
.arg(&debug_arg)
.arg(&debug_build_arg)
.arg(&debug_graph_arg)
.arg(&wasm_arg)
.arg(&apply_all_captures_arg),
)
.subcommand(
@ -392,10 +399,23 @@ fn run() -> Result<()> {
loader.use_debug_build(debug_build);
let mut parser = Parser::new();
#[cfg(feature = "wasm")]
if matches.is_present("wasm") {
let engine = tree_sitter::wasmtime::Engine::default();
parser
.set_wasm_store(tree_sitter::WasmStore::new(engine.clone()).unwrap())
.unwrap();
loader.use_wasm(engine);
}
let languages = loader.languages_at_path(&current_dir)?;
let language = languages
.first()
.ok_or_else(|| anyhow!("No language found"))?;
parser.set_language(*language)?;
let test_dir = current_dir.join("test");
// Run the corpus tests. Look for them at two paths: `test/corpus` and `corpus`.
@ -405,7 +425,7 @@ fn run() -> Result<()> {
}
if test_corpus_dir.is_dir() {
test::run_tests_at_path(
*language,
&mut parser,
&test_corpus_dir,
debug,
debug_graph,
@ -420,12 +440,22 @@ fn run() -> Result<()> {
// Run the syntax highlighting tests.
let test_highlight_dir = test_dir.join("highlight");
if test_highlight_dir.is_dir() {
test_highlight::test_highlights(&loader, &test_highlight_dir, apply_all_captures)?;
let mut highlighter = Highlighter::new();
highlighter.parser = parser;
test_highlight::test_highlights(
&loader,
&mut highlighter,
&test_highlight_dir,
apply_all_captures,
)?;
parser = highlighter.parser;
}
let test_tag_dir = test_dir.join("tags");
if test_tag_dir.is_dir() {
test_tags::test_tags(&loader, &test_tag_dir)?;
let mut tags_context = TagsContext::new();
tags_context.parser = parser;
test_tags::test_tags(&loader, &mut tags_context, &test_tag_dir)?;
}
}
@ -459,6 +489,7 @@ fn run() -> Result<()> {
.values_of("edits")
.map_or(Vec::new(), |e| e.collect());
let cancellation_flag = util::cancel_on_signal();
let mut parser = Parser::new();
if debug {
// For augmenting debug logging in external scanners
@ -467,6 +498,15 @@ fn run() -> Result<()> {
loader.use_debug_build(debug_build);
#[cfg(feature = "wasm")]
if matches.is_present("wasm") {
let engine = tree_sitter::wasmtime::Engine::default();
parser
.set_wasm_store(tree_sitter::WasmStore::new(engine.clone()).unwrap())
.unwrap();
loader.use_wasm(engine);
}
let timeout = matches
.value_of("timeout")
.map_or(0, |t| u64::from_str_radix(t, 10).unwrap());
@ -483,8 +523,12 @@ fn run() -> Result<()> {
for path in paths {
let path = Path::new(&path);
let language =
loader.select_language(path, &current_dir, matches.value_of("scope"))?;
parser
.set_language(language)
.context("incompatible language")?;
let opts = ParseFileOptions {
language,
@ -500,7 +544,7 @@ fn run() -> Result<()> {
encoding,
};
let this_file_errored = parse::parse_file_at_path(opts)?;
let this_file_errored = parse::parse_file_at_path(&mut parser, opts)?;
if should_track_stats {
stats.total_parses += 1;
@ -694,7 +738,12 @@ fn run() -> Result<()> {
("build-wasm", Some(matches)) => {
let grammar_path = current_dir.join(matches.value_of("path").unwrap_or(""));
wasm::compile_language_to_wasm(&grammar_path, matches.is_present("docker"))?;
wasm::compile_language_to_wasm(
&loader,
&grammar_path,
&current_dir,
matches.is_present("docker"),
)?;
}
("playground", Some(matches)) => {

View file

@ -52,9 +52,8 @@ pub struct ParseFileOptions<'a> {
pub encoding: Option<u32>,
}
pub fn parse_file_at_path(opts: ParseFileOptions) -> Result<bool> {
pub fn parse_file_at_path(parser: &mut Parser, opts: ParseFileOptions) -> Result<bool> {
let mut _log_session = None;
let mut parser = Parser::new();
parser.set_language(opts.language)?;
let mut source_code = fs::read(opts.path)
.with_context(|| format!("Error reading source file {:?}", opts.path))?;
@ -68,7 +67,7 @@ pub fn parse_file_at_path(opts: ParseFileOptions) -> Result<bool> {
// Render an HTML graph if `--debug-graph` was passed
if opts.debug_graph {
_log_session = Some(util::log_graphs(&mut parser, "log.html")?);
_log_session = Some(util::log_graphs(parser, "log.html")?);
}
// Log to stderr if `--debug` was passed
else if opts.debug {
@ -105,6 +104,8 @@ pub fn parse_file_at_path(opts: ParseFileOptions) -> Result<bool> {
_ => parser.parse(&source_code, None),
};
parser.stop_printing_dot_graphs();
let stdout = io::stdout();
let mut stdout = stdout.lock();

View file

@ -45,15 +45,7 @@ fn get_main_html(tree_sitter_dir: Option<&PathBuf>) -> Cow<'static, [u8]> {
pub fn serve(grammar_path: &Path, open_in_browser: bool) -> Result<()> {
let server = get_server()?;
let grammar_name = wasm::get_grammar_name(&grammar_path.join("src"))
.with_context(|| "Failed to get wasm filename")?;
let wasm_filename = format!("tree-sitter-{}.wasm", grammar_name);
let language_wasm = fs::read(grammar_path.join(&wasm_filename)).with_context(|| {
format!(
"Failed to read {}. Run `tree-sitter build-wasm` first.",
wasm_filename
)
})?;
let (grammar_name, language_wasm) = wasm::load_language_wasm_file(&grammar_path).unwrap();
let url = format!("http://{}", server.server_addr());
println!("Started playground on: {}", url);
if open_in_browser {

View file

@ -57,7 +57,7 @@ impl Default for TestEntry {
}
pub fn run_tests_at_path(
language: Language,
parser: &mut Parser,
path: &Path,
debug: bool,
debug_graph: bool,
@ -66,11 +66,9 @@ pub fn run_tests_at_path(
) -> Result<()> {
let test_entry = parse_tests(path)?;
let mut _log_session = None;
let mut parser = Parser::new();
parser.set_language(language)?;
if debug_graph {
_log_session = Some(util::log_graphs(&mut parser, "log.html")?);
_log_session = Some(util::log_graphs(parser, "log.html")?);
} else if debug {
parser.set_logger(Some(Box::new(|log_type, message| {
if log_type == LogType::Lex {
@ -83,7 +81,7 @@ pub fn run_tests_at_path(
let mut failures = Vec::new();
let mut corrected_entries = Vec::new();
run_tests(
&mut parser,
parser,
test_entry,
filter,
0,
@ -92,6 +90,8 @@ pub fn run_tests_at_path(
&mut corrected_entries,
)?;
parser.stop_printing_dot_graphs();
if failures.len() > 0 {
println!("");
@ -721,7 +721,7 @@ code
---
; Line start comment
(a
(a
; ignore this
(b)
; also ignore this

View file

@ -38,19 +38,24 @@ impl std::fmt::Display for Failure {
}
}
pub fn test_highlights(loader: &Loader, directory: &Path, apply_all_captures: bool) -> Result<()> {
pub fn test_highlights(
loader: &Loader,
highlighter: &mut Highlighter,
directory: &Path,
apply_all_captures: bool,
) -> Result<()> {
println!("syntax highlighting:");
test_highlights_indented(loader, directory, apply_all_captures, 2)
test_highlights_indented(loader, highlighter, directory, apply_all_captures, 2)
}
fn test_highlights_indented(
loader: &Loader,
highlighter: &mut Highlighter,
directory: &Path,
apply_all_captures: bool,
indent_level: usize,
) -> Result<()> {
let mut failed = false;
let mut highlighter = Highlighter::new();
for highlight_test_file in fs::read_dir(directory)? {
let highlight_test_file = highlight_test_file?;
@ -65,6 +70,7 @@ fn test_highlights_indented(
println!("{}:", test_file_name.into_string().unwrap());
if let Err(_) = test_highlights_indented(
loader,
highlighter,
&test_file_path,
apply_all_captures,
indent_level + 1,
@ -80,7 +86,7 @@ fn test_highlights_indented(
.ok_or_else(|| anyhow!("No highlighting config found for {:?}", test_file_path))?;
match test_highlight(
&loader,
&mut highlighter,
highlighter,
highlight_config,
fs::read(&test_file_path)?.as_slice(),
) {

View file

@ -38,9 +38,8 @@ impl std::fmt::Display for Failure {
}
}
pub fn test_tags(loader: &Loader, directory: &Path) -> Result<()> {
pub fn test_tags(loader: &Loader, tags_context: &mut TagsContext, directory: &Path) -> Result<()> {
let mut failed = false;
let mut tags_context = TagsContext::new();
println!("tags:");
for tag_test_file in fs::read_dir(directory)? {
@ -54,7 +53,7 @@ pub fn test_tags(loader: &Loader, directory: &Path) -> Result<()> {
.tags_config(language)?
.ok_or_else(|| anyhow!("No tags config found for {:?}", test_file_path))?;
match test_tag(
&mut tags_context,
tags_context,
tags_config,
fs::read(&test_file_path)?.as_slice(),
) {

View file

@ -8,6 +8,7 @@ lazy_static! {
fs::create_dir_all(&result).unwrap();
result
};
pub static ref WASM_DIR: PathBuf = ROOT_DIR.join("target").join("release");
pub static ref SCRATCH_DIR: PathBuf = {
// https://doc.rust-lang.org/reference/conditional-compilation.html
let vendor = if cfg!(target_vendor = "apple") {

View file

@ -72,31 +72,28 @@ pub fn get_tags_config(language_name: &str) -> TagsConfiguration {
}
pub fn get_test_language(name: &str, parser_code: &str, path: Option<&Path>) -> Language {
let parser_c_path = SCRATCH_DIR.join(&format!("{}-parser.c", name));
if !fs::read_to_string(&parser_c_path)
.map(|content| content == parser_code)
.unwrap_or(false)
{
fs::write(&parser_c_path, parser_code).unwrap();
let src_dir = SCRATCH_DIR.join("src").join(name);
fs::create_dir_all(&src_dir).unwrap();
let parser_path = src_dir.join("parser.c");
if !fs::read_to_string(&parser_path).map_or(false, |content| content == parser_code) {
fs::write(&parser_path, parser_code).unwrap();
}
let scanner_path = path.and_then(|p| {
let result = p.join("scanner.c");
if result.exists() {
Some(result)
} else {
None
if let Some(path) = path {
let scanner_path = path.join("scanner.c");
if scanner_path.exists() {
let scanner_code = fs::read_to_string(&scanner_path).unwrap();
let scanner_copy_path = src_dir.join("scanner.c");
if !fs::read_to_string(&scanner_copy_path)
.map_or(false, |content| content == scanner_code)
{
fs::write(&scanner_copy_path, scanner_code).unwrap();
}
}
});
}
TEST_LOADER
.load_language_from_sources(name, &HEADER_DIR, &parser_c_path, scanner_path.as_deref())
.load_language_at_path_with_name(&src_dir, &HEADER_DIR, name)
.unwrap()
}
pub fn get_test_grammar(name: &str) -> (String, Option<PathBuf>) {
let dir = fixtures_dir().join("test_grammars").join(name);
let grammar = fs::read_to_string(&dir.join("grammar.json")).expect(&format!(
"Can't find grammar.json for test grammar {}",
name
));
(grammar, Some(dir))
}

View file

@ -14,3 +14,6 @@ mod test_highlight_test;
mod test_tags_test;
mod text_provider_test;
mod tree_test;
#[cfg(feature = "wasm")]
mod wasm_language_test;

View file

@ -2,13 +2,15 @@ use super::helpers::{
allocations,
edits::invert_edit,
edits::ReadRecorder,
fixtures::{get_language, get_test_grammar, get_test_language},
fixtures::{get_language, get_test_language},
};
use crate::{
generate::generate_parser_for_grammar,
parse::{perform_edit, Edit},
tests::helpers::fixtures::fixtures_dir,
};
use std::{
fs,
sync::atomic::{AtomicUsize, Ordering},
thread, time,
};
@ -427,16 +429,15 @@ fn test_parsing_empty_file_with_reused_tree() {
#[test]
fn test_parsing_after_editing_tree_that_depends_on_column_values() {
let (grammar, path) = get_test_grammar("uses_current_column");
let dir = fixtures_dir()
.join("test_grammars")
.join("uses_current_column");
let grammar = fs::read_to_string(&dir.join("grammar.json")).unwrap();
let (grammar_name, parser_code) = generate_parser_for_grammar(&grammar).unwrap();
let mut parser = Parser::new();
parser
.set_language(get_test_language(
&grammar_name,
&parser_code,
path.as_ref().map(AsRef::as_ref),
))
.set_language(get_test_language(&grammar_name, &parser_code, Some(&dir)))
.unwrap();
let mut code = b"

View file

@ -0,0 +1,92 @@
use crate::tests::helpers::fixtures::WASM_DIR;
use lazy_static::lazy_static;
use std::fs;
use tree_sitter::{wasmtime::Engine, Parser, WasmError, WasmErrorKind, WasmStore};
lazy_static! {
static ref ENGINE: Engine = Engine::default();
}
#[test]
fn test_load_wasm_language() {
let mut store = WasmStore::new(ENGINE.clone()).unwrap();
let mut parser = Parser::new();
let wasm_cpp = fs::read(&WASM_DIR.join(format!("tree-sitter-cpp.wasm"))).unwrap();
let wasm_rs = fs::read(&WASM_DIR.join(format!("tree-sitter-rust.wasm"))).unwrap();
let wasm_rb = fs::read(&WASM_DIR.join(format!("tree-sitter-ruby.wasm"))).unwrap();
let wasm_typescript = fs::read(&WASM_DIR.join(format!("tree-sitter-typescript.wasm"))).unwrap();
let language_rust = store.load_language("rust", &wasm_rs).unwrap();
let language_cpp = store.load_language("cpp", &wasm_cpp).unwrap();
let language_ruby = store.load_language("ruby", &wasm_rb).unwrap();
let language_typescript = store.load_language("typescript", &wasm_typescript).unwrap();
parser.set_wasm_store(store).unwrap();
let mut parser2 = Parser::new();
parser2
.set_wasm_store(WasmStore::new(ENGINE.clone()).unwrap())
.unwrap();
for mut parser in [parser, parser2] {
for _ in 0..2 {
parser.set_language(language_cpp).unwrap();
let tree = parser.parse("A<B> c = d();", None).unwrap();
assert_eq!(
tree.root_node().to_sexp(),
"(translation_unit (declaration type: (template_type name: (type_identifier) arguments: (template_argument_list (type_descriptor type: (type_identifier)))) declarator: (init_declarator declarator: (identifier) value: (call_expression function: (identifier) arguments: (argument_list)))))"
);
parser.set_language(language_rust).unwrap();
let tree = parser.parse("const A: B = c();", None).unwrap();
assert_eq!(
tree.root_node().to_sexp(),
"(source_file (const_item name: (identifier) type: (type_identifier) value: (call_expression function: (identifier) arguments: (arguments))))"
);
parser.set_language(language_ruby).unwrap();
let tree = parser.parse("class A; end", None).unwrap();
assert_eq!(
tree.root_node().to_sexp(),
"(program (class name: (constant)))"
);
parser.set_language(language_typescript).unwrap();
let tree = parser.parse("class A {}", None).unwrap();
assert_eq!(
tree.root_node().to_sexp(),
"(program (class_declaration name: (type_identifier) body: (class_body)))"
);
}
}
}
#[test]
fn test_load_wasm_errors() {
let mut store = WasmStore::new(ENGINE.clone()).unwrap();
let wasm = fs::read(&WASM_DIR.join(format!("tree-sitter-rust.wasm"))).unwrap();
let bad_wasm = &wasm[1..];
assert_eq!(
store.load_language("rust", &bad_wasm).unwrap_err(),
WasmError {
kind: WasmErrorKind::Parse,
message: "failed to parse dylink section of wasm module".into(),
}
);
assert_eq!(
store.load_language("not_rust", &wasm).unwrap_err(),
WasmError {
kind: WasmErrorKind::Instantiate,
message: "module did not contain language function: tree_sitter_not_rust".into(),
}
);
let mut bad_wasm = wasm.clone();
bad_wasm[300..500].iter_mut().for_each(|b| *b = 0);
assert_eq!(
store.load_language("rust", &bad_wasm).unwrap_err().kind,
WasmErrorKind::Compile,
);
}

View file

@ -1,15 +1,24 @@
use super::generate::parse_grammar::GrammarJSON;
use anyhow::{anyhow, Context, Result};
use path_slash::PathExt as _;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::path::Path;
use std::process::Command;
use which::which;
use anyhow::{Context, Result};
use std::{fs, path::Path};
use tree_sitter_loader::Loader;
const EMSCRIPTEN_TAG: &'static str = concat!("emscripten/emsdk:", env!("EMSCRIPTEN_VERSION"));
pub fn load_language_wasm_file(language_dir: &Path) -> Result<(String, Vec<u8>)> {
let grammar_name = get_grammar_name(&language_dir)
.with_context(|| "Failed to get wasm filename")
.unwrap();
let wasm_filename = format!("tree-sitter-{}.wasm", grammar_name);
let contents = fs::read(language_dir.join(&wasm_filename)).with_context(|| {
format!(
"Failed to read {}. Run `tree-sitter build-wasm` first.",
wasm_filename
)
})?;
Ok((grammar_name, contents))
}
pub fn get_grammar_name(src_dir: &Path) -> Result<String> {
pub fn get_grammar_name(language_dir: &Path) -> Result<String> {
let src_dir = language_dir.join("src");
let grammar_json_path = src_dir.join("grammar.json");
let grammar_json = fs::read_to_string(&grammar_json_path)
.with_context(|| format!("Failed to read grammar file {:?}", grammar_json_path))?;
@ -18,118 +27,24 @@ pub fn get_grammar_name(src_dir: &Path) -> Result<String> {
Ok(grammar.name)
}
pub fn compile_language_to_wasm(language_dir: &Path, force_docker: bool) -> Result<()> {
let src_dir = language_dir.join("src");
let grammar_name = get_grammar_name(&src_dir)?;
let output_filename = format!("tree-sitter-{}.wasm", grammar_name);
let emcc_bin = if cfg!(windows) { "emcc.bat" } else { "emcc" };
let emcc_path = which(emcc_bin)
.ok()
.and_then(|p| Command::new(&p).output().and(Ok(p)).ok());
let mut command;
if !force_docker && emcc_path.is_some() {
command = Command::new(emcc_path.unwrap());
command.current_dir(&language_dir);
} else if Command::new("docker").output().is_ok() {
command = Command::new("docker");
command.args(&["run", "--rm"]);
// Mount the parser directory as a volume
let mut volume_string;
if let (Some(parent), Some(filename)) = (language_dir.parent(), language_dir.file_name()) {
volume_string = OsString::from(parent);
volume_string.push(":/src:Z");
command.arg("--workdir");
command.arg(Path::new("/src").join(filename).to_slash_lossy().as_ref());
} else {
volume_string = OsString::from(language_dir);
volume_string.push(":/src:Z");
command.args(&["--workdir", "/src"]);
}
command.args(&[OsStr::new("--volume"), &volume_string]);
// Get the current user id so that files created in the docker container will have
// the same owner.
if cfg!(unix) {
let user_id_output = Command::new("id")
.arg("-u")
.output()
.with_context(|| "Failed to get get current user id")?;
let user_id = String::from_utf8_lossy(&user_id_output.stdout);
let user_id = user_id.trim();
command.args(&["--user", user_id]);
}
// Run `emcc` in a container using the `emscripten-slim` image
command.args(&[EMSCRIPTEN_TAG, "emcc"]);
} else {
if force_docker {
return Err(anyhow!(
"You must have docker on your PATH to run this command with --docker"
));
}
return Err(anyhow!(
"You must have either emcc or docker on your PATH to run this command"
));
}
command.args(&[
"-o",
pub fn compile_language_to_wasm(
loader: &Loader,
language_dir: &Path,
output_dir: &Path,
force_docker: bool,
) -> Result<()> {
let grammar_name = get_grammar_name(&language_dir)?;
let output_filename = output_dir.join(&format!("tree-sitter-{}.wasm", grammar_name));
let src_path = language_dir.join("src");
let scanner_path = loader.get_scanner_path(&src_path);
loader.compile_parser_to_wasm(
&grammar_name,
&src_path,
scanner_path
.as_ref()
.and_then(|p| Some(Path::new(p.file_name()?))),
&output_filename,
"-Os",
"-s",
"WASM=1",
"-s",
"SIDE_MODULE=1",
"-s",
"TOTAL_MEMORY=33554432",
"-s",
"NODEJS_CATCH_EXIT=0",
"-s",
"NODEJS_CATCH_REJECTION=0",
"-s",
&format!("EXPORTED_FUNCTIONS=[\"_tree_sitter_{}\"]", grammar_name),
"-fno-exceptions",
"-I",
"src",
]);
let src = Path::new("src");
let parser_c_path = src.join("parser.c");
let scanner_c_path = src.join("scanner.c");
let scanner_cc_path = src.join("scanner.cc");
let scanner_cpp_path = src.join("scanner.cpp");
if language_dir.join(&scanner_cc_path).exists() {
command
.arg("-xc++")
.arg(scanner_cc_path.to_slash_lossy().as_ref());
} else if language_dir.join(&scanner_cpp_path).exists() {
command
.arg("-xc++")
.arg(scanner_cpp_path.to_slash_lossy().as_ref());
} else if language_dir.join(&scanner_c_path).exists() {
command.arg(scanner_c_path.to_slash_lossy().as_ref());
}
command.arg(parser_c_path.to_slash_lossy().as_ref());
let output = command
.output()
.with_context(|| "Failed to run emcc command")?;
if !output.status.success() {
return Err(anyhow!(
"emcc command failed - {}",
String::from_utf8_lossy(&output.stderr)
));
}
// Move the created `.wasm` file into the current working directory.
fs::rename(&language_dir.join(&output_filename), &output_filename)
.with_context(|| format!("Couldn't find output file {:?}", output_filename))?;
force_docker,
)?;
Ok(())
}

View file

@ -127,7 +127,7 @@ pub struct HighlightConfiguration {
/// syntax highlighting calls. A separate highlighter is needed for each thread that
/// is performing highlighting.
pub struct Highlighter {
parser: Parser,
pub parser: Parser,
cursors: Vec<QueryCursor>,
}

View file

@ -22,9 +22,26 @@ include = [
"/src/unicode/*",
]
[features]
wasm = ["wasmtime", "wasmtime-c-api"]
[dependencies]
regex = "1.9.1"
[dependencies.wasmtime]
git = "https://github.com/bytecodealliance/wasmtime"
rev = "fa6fcd946b8f6d60c2d191a1b14b9399e261a76d"
optional = true
default-features = false
features = ["cranelift"]
[dependencies.wasmtime-c-api]
git = "https://github.com/bytecodealliance/wasmtime"
rev = "fa6fcd946b8f6d60c2d191a1b14b9399e261a76d"
optional = true
package = "wasmtime-c-api-impl"
default-features = false
[build-dependencies]
bindgen = { version = "^0.66.1", optional = true }
cc = "^1.0.79"

View file

@ -1,4 +1,4 @@
/* automatically generated by rust-bindgen 0.66.1 */
/* automatically generated by rust-bindgen 0.69.1 */
pub const TREE_SITTER_LANGUAGE_VERSION: u32 = 14;
pub const TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION: u32 = 13;
@ -767,6 +767,62 @@ extern "C" {
self_: *const TSLookaheadIterator,
) -> *const ::std::os::raw::c_char;
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct wasm_engine_t {
_unused: [u8; 0],
}
pub type TSWasmEngine = wasm_engine_t;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct TSWasmStore {
_unused: [u8; 0],
}
pub const TSWasmErrorKindNone: TSWasmErrorKind = 0;
pub const TSWasmErrorKindParse: TSWasmErrorKind = 1;
pub const TSWasmErrorKindCompile: TSWasmErrorKind = 2;
pub const TSWasmErrorKindInstantiate: TSWasmErrorKind = 3;
pub const TSWasmErrorKindAllocate: TSWasmErrorKind = 4;
pub type TSWasmErrorKind = ::std::os::raw::c_uint;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct TSWasmError {
pub kind: TSWasmErrorKind,
pub message: *mut ::std::os::raw::c_char,
}
extern "C" {
#[doc = " Create a Wasm store."]
pub fn ts_wasm_store_new(
engine: *mut TSWasmEngine,
error: *mut TSWasmError,
) -> *mut TSWasmStore;
}
extern "C" {
#[doc = " Free the memory associated with the given Wasm store."]
pub fn ts_wasm_store_delete(arg1: *mut TSWasmStore);
}
extern "C" {
#[doc = " Create a language from a buffer of Wasm. The resulting language behaves\n like any other Tree-sitter language, except that in order to use it with\n a parser, that parser must have a Wasm store. Note that the language\n can be used with any Wasm store, it doesn't need to be the same store that\n was used to originally load it."]
pub fn ts_wasm_store_load_language(
arg1: *mut TSWasmStore,
name: *const ::std::os::raw::c_char,
wasm: *const ::std::os::raw::c_char,
wasm_len: u32,
error: *mut TSWasmError,
) -> *const TSLanguage;
}
extern "C" {
#[doc = " Check if the language came from a Wasm module. If so, then in order to use\n this langauge with a Parser, that parser must have a Wasm store assigned."]
pub fn ts_language_is_wasm(arg1: *const TSLanguage) -> bool;
}
extern "C" {
#[doc = " Assign the given Wasm store to the parser. A parser must have a Wasm store\n in order to use Wasm languages."]
pub fn ts_parser_set_wasm_store(arg1: *mut TSParser, arg2: *mut TSWasmStore);
}
extern "C" {
#[doc = " Remove the parser's current Wasm store and return it. This returns NULL if\n the parser doesn't have a Wasm store."]
pub fn ts_parser_take_wasm_store(arg1: *mut TSParser) -> *mut TSWasmStore;
}
extern "C" {
#[doc = " Set the allocation functions used by the library.\n\n By default, Tree-sitter uses the standard libc allocation functions,\n but aborts the process when an allocation fails. This function lets\n you supply alternative allocation functions at runtime.\n\n If you pass `NULL` for any parameter, Tree-sitter will switch back to\n its default implementation of that function.\n\n If you call this function after the library has already been used, then\n you must ensure that either:\n 1. All the existing objects have been freed.\n 2. The new allocator shares its state with the old one, so it is capable\n of freeing memory that was allocated by the old allocator."]
pub fn ts_set_allocator(

View file

@ -20,6 +20,13 @@ fn main() {
#[cfg(feature = "bindgen")]
generate_bindings();
let mut config = cc::Build::new();
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_WASM");
if env::var("CARGO_FEATURE_WASM").is_ok() {
config.define("TREE_SITTER_FEATURE_WASM", "");
}
let src_path = Path::new("src");
for entry in fs::read_dir(&src_path).unwrap() {
let entry = entry.unwrap();
@ -27,11 +34,13 @@ fn main() {
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
}
cc::Build::new()
config
.flag_if_supported("-std=c99")
.flag_if_supported("-fvisibility=hidden")
.flag_if_supported("-Wshadow")
.flag_if_supported("-Wno-unused-parameter")
.include(src_path)
.include(src_path.join("wasm"))
.include("include")
.file(src_path.join("lib.c"))
.compile("tree-sitter");

View file

@ -21,6 +21,11 @@ use std::{
u16,
};
#[cfg(feature = "wasm")]
mod wasm_language;
#[cfg(feature = "wasm")]
pub use wasm_language::*;
/// The latest ABI version that is supported by the current version of the
/// library.
///

View file

@ -0,0 +1,134 @@
use crate::{ffi, Language, LanguageError, Parser};
use std::{
error,
ffi::CString,
fmt,
mem::{self, MaybeUninit},
os::raw::c_char,
};
pub use wasmtime;
#[cfg(feature = "wasm")]
pub fn test() {
// Force Cargo to include wasmtime-c-api as a dependency of this crate,
// even though it is only used by the C code.
wasmtime_c_api::wasm_engine_new();
}
#[repr(C)]
#[derive(Clone)]
#[allow(non_camel_case_types)]
pub struct wasm_engine_t {
pub(crate) engine: wasmtime::Engine,
}
pub struct WasmStore(*mut ffi::TSWasmStore);
#[derive(Debug, PartialEq, Eq)]
pub struct WasmError {
pub kind: WasmErrorKind,
pub message: String,
}
#[derive(Debug, PartialEq, Eq)]
pub enum WasmErrorKind {
Parse,
Compile,
Instantiate,
Other,
}
impl WasmStore {
pub fn new(engine: wasmtime::Engine) -> Result<Self, WasmError> {
unsafe {
let mut error = MaybeUninit::<ffi::TSWasmError>::uninit();
let engine = Box::new(wasm_engine_t { engine });
let store = ffi::ts_wasm_store_new(
Box::leak(engine) as *mut wasm_engine_t as *mut _,
error.as_mut_ptr(),
);
if store.is_null() {
Err(WasmError::new(error.assume_init()))
} else {
Ok(WasmStore(store))
}
}
}
pub fn load_language(&mut self, name: &str, bytes: &[u8]) -> Result<Language, WasmError> {
let name = CString::new(name).unwrap();
unsafe {
let mut error = MaybeUninit::<ffi::TSWasmError>::uninit();
let language = ffi::ts_wasm_store_load_language(
self.0,
name.as_ptr(),
bytes.as_ptr() as *const c_char,
bytes.len() as u32,
error.as_mut_ptr(),
);
if language.is_null() {
Err(WasmError::new(error.assume_init()))
} else {
Ok(Language(language))
}
}
}
}
impl WasmError {
unsafe fn new(error: ffi::TSWasmError) -> Self {
let message = CString::from_raw(error.message);
Self {
kind: match error.kind {
ffi::TSWasmErrorKindParse => WasmErrorKind::Parse,
ffi::TSWasmErrorKindCompile => WasmErrorKind::Compile,
ffi::TSWasmErrorKindInstantiate => WasmErrorKind::Instantiate,
_ => WasmErrorKind::Other,
},
message: message.into_string().unwrap(),
}
}
}
impl Language {
pub fn is_wasm(&self) -> bool {
unsafe { ffi::ts_language_is_wasm(self.0) }
}
}
impl Parser {
pub fn set_wasm_store(&mut self, store: WasmStore) -> Result<(), LanguageError> {
unsafe { ffi::ts_parser_set_wasm_store(self.0.as_ptr(), store.0) };
mem::forget(store);
Ok(())
}
pub fn take_wasm_store(&mut self) -> Option<WasmStore> {
let ptr = unsafe { ffi::ts_parser_take_wasm_store(self.0.as_ptr()) };
if ptr.is_null() {
None
} else {
Some(WasmStore(ptr))
}
}
}
impl Drop for WasmStore {
fn drop(&mut self) {
unsafe { ffi::ts_wasm_store_delete(self.0) };
}
}
impl fmt::Display for WasmError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let kind = match self.kind {
WasmErrorKind::Parse => "Failed to parse wasm",
WasmErrorKind::Compile => "Failed to compile wasm",
WasmErrorKind::Instantiate => "Failed to instantiate wasm module",
WasmErrorKind::Other => "Unknown error",
};
write!(f, "{kind} {}", self.message)
}
}
impl error::Error for WasmError {}

View file

@ -1020,14 +1020,8 @@ class Language {
}
}
// emscripten-core/emscripten#12969
const loadModule =
typeof loadSideModule === 'function'
? loadSideModule
: loadWebAssemblyModule;
return bytes
.then(bytes => loadModule(bytes, {loadAsync: true}))
.then(bytes => loadWebAssemblyModule(bytes, {loadAsync: true}))
.then(mod => {
const symbolNames = Object.keys(mod)
const functionName = symbolNames.find(key =>

View file

@ -1,3 +1,4 @@
-std=c99
-Isrc
-Iinclude
-Isrc/wasm
-Iinclude
-D TREE_SITTER_FEATURE_WASM

View file

@ -1142,6 +1142,72 @@ TSSymbol ts_lookahead_iterator_current_symbol(const TSLookaheadIterator *self);
*/
const char *ts_lookahead_iterator_current_symbol_name(const TSLookaheadIterator *self);
/*************************************/
/* Section - WebAssembly Integration */
/************************************/
typedef struct wasm_engine_t TSWasmEngine;
typedef struct TSWasmStore TSWasmStore;
typedef enum {
TSWasmErrorKindNone = 0,
TSWasmErrorKindParse,
TSWasmErrorKindCompile,
TSWasmErrorKindInstantiate,
TSWasmErrorKindAllocate,
} TSWasmErrorKind;
typedef struct {
TSWasmErrorKind kind;
char *message;
} TSWasmError;
/**
* Create a Wasm store.
*/
TSWasmStore *ts_wasm_store_new(
TSWasmEngine *engine,
TSWasmError *error
);
/**
* Free the memory associated with the given Wasm store.
*/
void ts_wasm_store_delete(TSWasmStore *);
/**
* Create a language from a buffer of Wasm. The resulting language behaves
* like any other Tree-sitter language, except that in order to use it with
* a parser, that parser must have a Wasm store. Note that the language
* can be used with any Wasm store, it doesn't need to be the same store that
* was used to originally load it.
*/
const TSLanguage *ts_wasm_store_load_language(
TSWasmStore *,
const char *name,
const char *wasm,
uint32_t wasm_len,
TSWasmError *error
);
/**
* Check if the language came from a Wasm module. If so, then in order to use
* this langauge with a Parser, that parser must have a Wasm store assigned.
*/
bool ts_language_is_wasm(const TSLanguage *);
/**
* Assign the given Wasm store to the parser. A parser must have a Wasm store
* in order to use Wasm languages.
*/
void ts_parser_set_wasm_store(TSParser *, TSWasmStore *);
/**
* Remove the parser's current Wasm store and return it. This returns NULL if
* the parser doesn't have a Wasm store.
*/
TSWasmStore *ts_parser_take_wasm_store(TSParser *);
/**********************************/
/* Section - Global Configuration */
/**********************************/

View file

@ -3,6 +3,7 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#ifdef __TINYC__

View file

@ -1,6 +1,4 @@
#include "./language.h"
#include "./subtree.h"
#include "./error_costs.h"
#include <string.h>
uint32_t ts_language_symbol_count(const TSLanguage *self) {

View file

@ -1,8 +1,3 @@
// The Tree-sitter library can be built by compiling this one source file.
//
// The following directories must be added to the include path:
// - include
#define _POSIX_C_SOURCE 200112L
#include "./alloc.c"
@ -16,3 +11,4 @@
#include "./subtree.c"
#include "./tree_cursor.c"
#include "./tree.c"
#include "./wasm.c"

View file

@ -18,6 +18,7 @@
#include "./stack.h"
#include "./subtree.h"
#include "./tree.h"
#include "./wasm.h"
#define LOG(...) \
if (self->lexer.logger.log || self->dot_graph_file) { \
@ -88,6 +89,7 @@ struct TSParser {
Stack *stack;
SubtreePool tree_pool;
const TSLanguage *language;
TSWasmStore *wasm_store;
ReduceActionSet reduce_actions;
Subtree finished_tree;
SubtreeArray trailing_extras;
@ -332,18 +334,105 @@ static bool ts_parser__better_version_exists(
return false;
}
static void ts_parser__restore_external_scanner(
static void ts_parser__external_scanner_create(
TSParser *self
) {
if (self->language && self->language->external_scanner.states) {
if (ts_language_is_wasm(self->language)) {
self->external_scanner_payload = (void *)(uintptr_t)ts_wasm_store_call_scanner_create(
self->wasm_store
);
} else if (self->language->external_scanner.create) {
self->external_scanner_payload = self->language->external_scanner.create();
}
}
}
static void ts_parser__external_scanner_destroy(
TSParser *self
) {
if (self->language && self->external_scanner_payload) {
if (ts_language_is_wasm(self->language)) {
if (self->wasm_store) {
ts_wasm_store_call_scanner_destroy(
self->wasm_store,
(uintptr_t)self->external_scanner_payload
);
}
} else if (self->language->external_scanner.destroy) {
self->language->external_scanner.destroy(
self->external_scanner_payload
);
}
self->external_scanner_payload = NULL;
}
}
static unsigned ts_parser__external_scanner_serialize(
TSParser *self
) {
if (ts_language_is_wasm(self->language)) {
return ts_wasm_store_call_scanner_serialize(
self->wasm_store,
(uintptr_t)self->external_scanner_payload,
self->lexer.debug_buffer
);
} else {
return self->language->external_scanner.serialize(
self->external_scanner_payload,
self->lexer.debug_buffer
);
}
}
static void ts_parser__external_scanner_deserialize(
TSParser *self,
Subtree external_token
) {
const char *data = NULL;
uint32_t length = 0;
if (external_token.ptr) {
self->language->external_scanner.deserialize(
self->external_scanner_payload,
ts_external_scanner_state_data(&external_token.ptr->external_scanner_state),
external_token.ptr->external_scanner_state.length
data = ts_external_scanner_state_data(&external_token.ptr->external_scanner_state);
length = external_token.ptr->external_scanner_state.length;
}
if (ts_language_is_wasm(self->language)) {
ts_wasm_store_call_scanner_deserialize(
self->wasm_store,
(uintptr_t)self->external_scanner_payload,
data,
length
);
} else {
self->language->external_scanner.deserialize(self->external_scanner_payload, NULL, 0);
self->language->external_scanner.deserialize(
self->external_scanner_payload,
data,
length
);
}
}
static bool ts_parser__external_scanner_scan(
TSParser *self,
TSStateId external_lex_state
) {
if (ts_language_is_wasm(self->language)) {
return ts_wasm_store_call_scanner_scan(
self->wasm_store,
(uintptr_t)self->external_scanner_payload,
external_lex_state * self->language->external_token_count
);
} else {
const bool *valid_external_tokens = ts_language_enabled_external_tokens(
self->language,
external_lex_state
);
return self->language->external_scanner.scan(
self->external_scanner_payload,
&self->lexer.data,
valid_external_tokens
);
}
}
@ -395,10 +484,6 @@ static Subtree ts_parser__lex(
const Length start_position = ts_stack_position(self->stack, version);
const Subtree external_token = ts_stack_last_external_token(self->stack, version);
const bool *valid_external_tokens = ts_language_enabled_external_tokens(
self->language,
lex_mode.external_lex_state
);
bool found_external_token = false;
bool error_mode = parse_state == ERROR_STATE;
@ -413,9 +498,10 @@ static Subtree ts_parser__lex(
ts_lexer_reset(&self->lexer, start_position);
for (;;) {
bool found_token = false;
Length current_position = self->lexer.current_position;
if (valid_external_tokens) {
if (lex_mode.external_lex_state != 0) {
LOG(
"lex_external state:%d, row:%u, column:%u",
lex_mode.external_lex_state,
@ -423,19 +509,12 @@ static Subtree ts_parser__lex(
current_position.extent.column
);
ts_lexer_start(&self->lexer);
ts_parser__restore_external_scanner(self, external_token);
bool found_token = self->language->external_scanner.scan(
self->external_scanner_payload,
&self->lexer.data,
valid_external_tokens
);
ts_parser__external_scanner_deserialize(self, external_token);
found_token = ts_parser__external_scanner_scan(self, lex_mode.external_lex_state);
ts_lexer_finish(&self->lexer, &lookahead_end_byte);
if (found_token) {
external_scanner_state_len = self->language->external_scanner.serialize(
self->external_scanner_payload,
self->lexer.debug_buffer
);
external_scanner_state_len = ts_parser__external_scanner_serialize(self);
external_scanner_state_changed = !ts_external_scanner_state_eq(
ts_subtree_external_scanner_state(external_token),
self->lexer.debug_buffer,
@ -482,17 +561,18 @@ static Subtree ts_parser__lex(
current_position.extent.column
);
ts_lexer_start(&self->lexer);
bool found_token = self->language->lex_fn(&self->lexer.data, lex_mode.lex_state);
found_token = false;
if (ts_language_is_wasm(self->language)) {
found_token = ts_wasm_store_call_lex_main(self->wasm_store, lex_mode.lex_state);
} else {
found_token = self->language->lex_fn(&self->lexer.data, lex_mode.lex_state);
}
ts_lexer_finish(&self->lexer, &lookahead_end_byte);
if (found_token) break;
if (!error_mode) {
error_mode = true;
lex_mode = self->language->lex_modes[ERROR_STATE];
valid_external_tokens = ts_language_enabled_external_tokens(
self->language,
lex_mode.external_lex_state
);
ts_lexer_reset(&self->lexer, start_position);
continue;
}
@ -543,12 +623,18 @@ static Subtree ts_parser__lex(
uint32_t end_byte = self->lexer.token_end_position.bytes;
ts_lexer_reset(&self->lexer, self->lexer.token_start_position);
ts_lexer_start(&self->lexer);
if (ts_language_is_wasm(self->language)) {
is_keyword = ts_wasm_store_call_lex_keyword(self->wasm_store, 0);
} else {
is_keyword = self->language->keyword_lex_fn(&self->lexer.data, 0);
}
if (
self->language->keyword_lex_fn(&self->lexer.data, 0) &&
is_keyword &&
self->lexer.token_end_position.bytes == end_byte &&
ts_language_has_actions(self->language, parse_state, self->lexer.data.result_symbol)
) {
is_keyword = true;
symbol = self->lexer.data.result_symbol;
}
}
@ -1766,6 +1852,7 @@ void ts_parser_delete(TSParser *self) {
ts_subtree_release(&self->tree_pool, self->old_tree);
self->old_tree = NULL_SUBTREE;
}
ts_wasm_store_delete(self->wasm_store);
ts_lexer_delete(&self->lexer);
ts_parser__set_cached_token(self, 0, NULL_SUBTREE, NULL_SUBTREE);
ts_subtree_pool_delete(&self->tree_pool);
@ -1781,22 +1868,25 @@ const TSLanguage *ts_parser_language(const TSParser *self) {
}
bool ts_parser_set_language(TSParser *self, const TSLanguage *language) {
ts_parser__external_scanner_destroy(self);
self->language = NULL;
if (language) {
if (language->version > TREE_SITTER_LANGUAGE_VERSION) return false;
if (language->version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION) return false;
}
if (
language->version > TREE_SITTER_LANGUAGE_VERSION ||
language->version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION
) return false;
if (self->external_scanner_payload && self->language->external_scanner.destroy) {
self->language->external_scanner.destroy(self->external_scanner_payload);
}
if (language && language->external_scanner.create) {
self->external_scanner_payload = language->external_scanner.create();
} else {
self->external_scanner_payload = NULL;
if (ts_language_is_wasm(language)) {
if (
!self->wasm_store ||
!ts_wasm_store_start(self->wasm_store, &self->lexer.data, language)
) return false;
}
}
self->language = language;
ts_parser__external_scanner_create(self);
ts_parser_reset(self);
return true;
}
@ -1881,6 +1971,14 @@ TSTree *ts_parser_parse(
) {
if (!self->language || !input.read) return NULL;
if (ts_language_is_wasm(self->language)) {
if (self->wasm_store) {
ts_wasm_store_start(self->wasm_store, &self->lexer.data, self->language);
} else {
return NULL;
}
}
ts_lexer_set_input(&self->lexer, input);
array_clear(&self->included_range_differences);
@ -2008,4 +2106,15 @@ TSTree *ts_parser_parse_string_encoding(
});
}
void ts_parser_set_wasm_store(TSParser *self, TSWasmStore *store) {
ts_wasm_store_delete(self->wasm_store);
self->wasm_store = store;
}
TSWasmStore *ts_parser_take_wasm_store(TSParser *self) {
TSWasmStore *result = self->wasm_store;
self->wasm_store = NULL;
return result;
}
#undef LOG

1527
lib/src/wasm.c Normal file

File diff suppressed because it is too large Load diff

27
lib/src/wasm.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef TREE_SITTER_WASM_H_
#define TREE_SITTER_WASM_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "tree_sitter/api.h"
#include "tree_sitter/parser.h"
bool ts_wasm_store_start(TSWasmStore *, TSLexer *, const TSLanguage *);
void ts_wasm_store_stop(TSWasmStore *);
bool ts_wasm_store_call_lex_main(TSWasmStore *, TSStateId);
bool ts_wasm_store_call_lex_keyword(TSWasmStore *, TSStateId);
uint32_t ts_wasm_store_call_scanner_create(TSWasmStore *);
void ts_wasm_store_call_scanner_destroy(TSWasmStore *, uint32_t);
bool ts_wasm_store_call_scanner_scan(TSWasmStore *, uint32_t, uint32_t);
uint32_t ts_wasm_store_call_scanner_serialize(TSWasmStore *, uint32_t, char *);
void ts_wasm_store_call_scanner_deserialize(TSWasmStore *, uint32_t, const char *, unsigned);
#ifdef __cplusplus
}
#endif
#endif // TREE_SITTER_WASM_H_

9
lib/src/wasm/README.md Normal file
View file

@ -0,0 +1,9 @@
## WASM
The `wasm.h` header file contains a standard interface implemented by multiple WASM implementations. It was taken from here:
https://github.com/WebAssembly/wasm-c-api/blob/c9d31284651b975f05ac27cee0bab1377560b87e/include/wasm.h
The `wasmtime` headers were taken from here:
https://github.com/bytecodealliance/wasmtime/tree/main/crates/c-api/include/wasmtime

View file

@ -0,0 +1,36 @@
[
"__ZNKSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE17__assign_externalEPKcm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE17__assign_no_aliasILb0EEERS5_PKcm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE17__assign_no_aliasILb1EEERS5_PKcm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE25__init_copy_ctor_externalEPKcm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEmmmmmm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev",
"__ZNSt3__212basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9push_backEw",
"__ZNSt3__212basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED2Ev",
"__ZdlPv",
"__Znwm",
"_calloc",
"_free",
"_iswalnum",
"_iswalpha",
"_iswdigit",
"_iswlower",
"_iswspace",
"_iswupper",
"_malloc",
"_memchr",
"_memcmp",
"_memcpy",
"_memmove",
"_memset",
"_realloc",
"_strcmp",
"_strlen",
"_strncpy",
"_towlower",
"_towupper"
]

155
lib/src/wasm/wasi.h Normal file
View file

@ -0,0 +1,155 @@
/**
* \file wasi.h
*
* C API for WASI
*/
#ifndef WASI_H
#define WASI_H
#include "wasm.h"
#ifndef WASI_API_EXTERN
#ifdef _WIN32
#define WASI_API_EXTERN __declspec(dllimport)
#else
#define WASI_API_EXTERN
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define own
#define WASI_DECLARE_OWN(name) \
typedef struct wasi_##name##_t wasi_##name##_t; \
WASI_API_EXTERN void wasi_##name##_delete(own wasi_##name##_t*);
/**
* \typedef wasi_config_t
* \brief Convenience alias for #wasi_config_t
*
* \struct wasi_config_t
* \brief TODO
*
* \fn void wasi_config_delete(wasi_config_t *);
* \brief Deletes a configuration object.
*/
WASI_DECLARE_OWN(config)
/**
* \brief Creates a new empty configuration object.
*
* The caller is expected to deallocate the returned configuration
*/
WASI_API_EXTERN own wasi_config_t* wasi_config_new();
/**
* \brief Sets the argv list for this configuration object.
*
* By default WASI programs have an empty argv list, but this can be used to
* explicitly specify what the argv list for the program is.
*
* The arguments are copied into the `config` object as part of this function
* call, so the `argv` pointer only needs to stay alive for this function call.
*/
WASI_API_EXTERN void wasi_config_set_argv(wasi_config_t* config, int argc, const char* argv[]);
/**
* \brief Indicates that the argv list should be inherited from this process's
* argv list.
*/
WASI_API_EXTERN void wasi_config_inherit_argv(wasi_config_t* config);
/**
* \brief Sets the list of environment variables available to the WASI instance.
*
* By default WASI programs have a blank environment, but this can be used to
* define some environment variables for them.
*
* It is required that the `names` and `values` lists both have `envc` entries.
*
* The env vars are copied into the `config` object as part of this function
* call, so the `names` and `values` pointers only need to stay alive for this
* function call.
*/
WASI_API_EXTERN void wasi_config_set_env(wasi_config_t* config, int envc, const char* names[], const char* values[]);
/**
* \brief Indicates that the entire environment of the calling process should be
* inherited by this WASI configuration.
*/
WASI_API_EXTERN void wasi_config_inherit_env(wasi_config_t* config);
/**
* \brief Configures standard input to be taken from the specified file.
*
* By default WASI programs have no stdin, but this configures the specified
* file to be used as stdin for this configuration.
*
* If the stdin location does not exist or it cannot be opened for reading then
* `false` is returned. Otherwise `true` is returned.
*/
WASI_API_EXTERN bool wasi_config_set_stdin_file(wasi_config_t* config, const char* path);
/**
* \brief Configures this process's own stdin stream to be used as stdin for
* this WASI configuration.
*/
WASI_API_EXTERN void wasi_config_inherit_stdin(wasi_config_t* config);
/**
* \brief Configures standard output to be written to the specified file.
*
* By default WASI programs have no stdout, but this configures the specified
* file to be used as stdout.
*
* If the stdout location could not be opened for writing then `false` is
* returned. Otherwise `true` is returned.
*/
WASI_API_EXTERN bool wasi_config_set_stdout_file(wasi_config_t* config, const char* path);
/**
* \brief Configures this process's own stdout stream to be used as stdout for
* this WASI configuration.
*/
WASI_API_EXTERN void wasi_config_inherit_stdout(wasi_config_t* config);
/**
* \brief Configures standard output to be written to the specified file.
*
* By default WASI programs have no stderr, but this configures the specified
* file to be used as stderr.
*
* If the stderr location could not be opened for writing then `false` is
* returned. Otherwise `true` is returned.
*/
WASI_API_EXTERN bool wasi_config_set_stderr_file(wasi_config_t* config, const char* path);
/**
* \brief Configures this process's own stderr stream to be used as stderr for
* this WASI configuration.
*/
WASI_API_EXTERN void wasi_config_inherit_stderr(wasi_config_t* config);
/**
* \brief Configures a "preopened directory" to be available to WASI APIs.
*
* By default WASI programs do not have access to anything on the filesystem.
* This API can be used to grant WASI programs access to a directory on the
* filesystem, but only that directory (its whole contents but nothing above it).
*
* The `path` argument here is a path name on the host filesystem, and
* `guest_path` is the name by which it will be known in wasm.
*/
WASI_API_EXTERN bool wasi_config_preopen_dir(wasi_config_t* config, const char* path, const char* guest_path);
#undef own
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifdef WASI_H

2245
lib/src/wasm/wasm-stdlib.h Normal file

File diff suppressed because it is too large Load diff

714
lib/src/wasm/wasm.h Normal file
View file

@ -0,0 +1,714 @@
// WebAssembly C API
#ifndef WASM_H
#define WASM_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#ifndef WASM_API_EXTERN
#ifdef _WIN32
#define WASM_API_EXTERN __declspec(dllimport)
#else
#define WASM_API_EXTERN
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
///////////////////////////////////////////////////////////////////////////////
// Auxiliaries
// Machine types
typedef char byte_t;
typedef float float32_t;
typedef double float64_t;
// Ownership
#define own
// The qualifier `own` is used to indicate ownership of data in this API.
// It is intended to be interpreted similar to a `const` qualifier:
//
// - `own wasm_xxx_t*` owns the pointed-to data
// - `own wasm_xxx_t` distributes to all fields of a struct or union `xxx`
// - `own wasm_xxx_vec_t` owns the vector as well as its elements(!)
// - an `own` function parameter passes ownership from caller to callee
// - an `own` function result passes ownership from callee to caller
// - an exception are `own` pointer parameters named `out`, which are copy-back
// output parameters passing back ownership from callee to caller
//
// Own data is created by `wasm_xxx_new` functions and some others.
// It must be released with the corresponding `wasm_xxx_delete` function.
//
// Deleting a reference does not necessarily delete the underlying object,
// it merely indicates that this owner no longer uses it.
//
// For vectors, `const wasm_xxx_vec_t` is used informally to indicate that
// neither the vector nor its elements should be modified.
// TODO: introduce proper `wasm_xxx_const_vec_t`?
#define WASM_DECLARE_OWN(name) \
typedef struct wasm_##name##_t wasm_##name##_t; \
\
WASM_API_EXTERN void wasm_##name##_delete(own wasm_##name##_t*);
// Vectors
#define WASM_DECLARE_VEC(name, ptr_or_none) \
typedef struct wasm_##name##_vec_t { \
size_t size; \
wasm_##name##_t ptr_or_none* data; \
} wasm_##name##_vec_t; \
\
WASM_API_EXTERN void wasm_##name##_vec_new_empty(own wasm_##name##_vec_t* out); \
WASM_API_EXTERN void wasm_##name##_vec_new_uninitialized( \
own wasm_##name##_vec_t* out, size_t); \
WASM_API_EXTERN void wasm_##name##_vec_new( \
own wasm_##name##_vec_t* out, \
size_t, own wasm_##name##_t ptr_or_none const[]); \
WASM_API_EXTERN void wasm_##name##_vec_copy( \
own wasm_##name##_vec_t* out, const wasm_##name##_vec_t*); \
WASM_API_EXTERN void wasm_##name##_vec_delete(own wasm_##name##_vec_t*);
// Byte vectors
typedef byte_t wasm_byte_t;
WASM_DECLARE_VEC(byte, )
typedef wasm_byte_vec_t wasm_name_t;
#define wasm_name wasm_byte_vec
#define wasm_name_new wasm_byte_vec_new
#define wasm_name_new_empty wasm_byte_vec_new_empty
#define wasm_name_new_new_uninitialized wasm_byte_vec_new_uninitialized
#define wasm_name_copy wasm_byte_vec_copy
#define wasm_name_delete wasm_byte_vec_delete
static inline void wasm_name_new_from_string(
own wasm_name_t* out, const char* s
) {
wasm_name_new(out, strlen(s), s);
}
static inline void wasm_name_new_from_string_nt(
own wasm_name_t* out, const char* s
) {
wasm_name_new(out, strlen(s) + 1, s);
}
///////////////////////////////////////////////////////////////////////////////
// Runtime Environment
// Configuration
WASM_DECLARE_OWN(config)
WASM_API_EXTERN own wasm_config_t* wasm_config_new(void);
// Embedders may provide custom functions for manipulating configs.
// Engine
WASM_DECLARE_OWN(engine)
WASM_API_EXTERN own wasm_engine_t* wasm_engine_new(void);
WASM_API_EXTERN own wasm_engine_t* wasm_engine_new_with_config(own wasm_config_t*);
// Store
WASM_DECLARE_OWN(store)
WASM_API_EXTERN own wasm_store_t* wasm_store_new(wasm_engine_t*);
///////////////////////////////////////////////////////////////////////////////
// Type Representations
// Type attributes
typedef uint8_t wasm_mutability_t;
enum wasm_mutability_enum {
WASM_CONST,
WASM_VAR,
};
typedef struct wasm_limits_t {
uint32_t min;
uint32_t max;
} wasm_limits_t;
static const uint32_t wasm_limits_max_default = 0xffffffff;
// Generic
#define WASM_DECLARE_TYPE(name) \
WASM_DECLARE_OWN(name) \
WASM_DECLARE_VEC(name, *) \
\
WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*);
// Value Types
WASM_DECLARE_TYPE(valtype)
typedef uint8_t wasm_valkind_t;
enum wasm_valkind_enum {
WASM_I32,
WASM_I64,
WASM_F32,
WASM_F64,
WASM_ANYREF = 128,
WASM_FUNCREF,
};
WASM_API_EXTERN own wasm_valtype_t* wasm_valtype_new(wasm_valkind_t);
WASM_API_EXTERN wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t*);
static inline bool wasm_valkind_is_num(wasm_valkind_t k) {
return k < WASM_ANYREF;
}
static inline bool wasm_valkind_is_ref(wasm_valkind_t k) {
return k >= WASM_ANYREF;
}
static inline bool wasm_valtype_is_num(const wasm_valtype_t* t) {
return wasm_valkind_is_num(wasm_valtype_kind(t));
}
static inline bool wasm_valtype_is_ref(const wasm_valtype_t* t) {
return wasm_valkind_is_ref(wasm_valtype_kind(t));
}
// Function Types
WASM_DECLARE_TYPE(functype)
WASM_API_EXTERN own wasm_functype_t* wasm_functype_new(
own wasm_valtype_vec_t* params, own wasm_valtype_vec_t* results);
WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t*);
WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t*);
// Global Types
WASM_DECLARE_TYPE(globaltype)
WASM_API_EXTERN own wasm_globaltype_t* wasm_globaltype_new(
own wasm_valtype_t*, wasm_mutability_t);
WASM_API_EXTERN const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t*);
WASM_API_EXTERN wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t*);
// Table Types
WASM_DECLARE_TYPE(tabletype)
WASM_API_EXTERN own wasm_tabletype_t* wasm_tabletype_new(
own wasm_valtype_t*, const wasm_limits_t*);
WASM_API_EXTERN const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t*);
WASM_API_EXTERN const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t*);
// Memory Types
WASM_DECLARE_TYPE(memorytype)
WASM_API_EXTERN own wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t*);
WASM_API_EXTERN const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t*);
// Extern Types
WASM_DECLARE_TYPE(externtype)
typedef uint8_t wasm_externkind_t;
enum wasm_externkind_enum {
WASM_EXTERN_FUNC,
WASM_EXTERN_GLOBAL,
WASM_EXTERN_TABLE,
WASM_EXTERN_MEMORY,
};
WASM_API_EXTERN wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t*);
WASM_API_EXTERN wasm_externtype_t* wasm_functype_as_externtype(wasm_functype_t*);
WASM_API_EXTERN wasm_externtype_t* wasm_globaltype_as_externtype(wasm_globaltype_t*);
WASM_API_EXTERN wasm_externtype_t* wasm_tabletype_as_externtype(wasm_tabletype_t*);
WASM_API_EXTERN wasm_externtype_t* wasm_memorytype_as_externtype(wasm_memorytype_t*);
WASM_API_EXTERN wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t*);
WASM_API_EXTERN wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t*);
WASM_API_EXTERN wasm_tabletype_t* wasm_externtype_as_tabletype(wasm_externtype_t*);
WASM_API_EXTERN wasm_memorytype_t* wasm_externtype_as_memorytype(wasm_externtype_t*);
WASM_API_EXTERN const wasm_externtype_t* wasm_functype_as_externtype_const(const wasm_functype_t*);
WASM_API_EXTERN const wasm_externtype_t* wasm_globaltype_as_externtype_const(const wasm_globaltype_t*);
WASM_API_EXTERN const wasm_externtype_t* wasm_tabletype_as_externtype_const(const wasm_tabletype_t*);
WASM_API_EXTERN const wasm_externtype_t* wasm_memorytype_as_externtype_const(const wasm_memorytype_t*);
WASM_API_EXTERN const wasm_functype_t* wasm_externtype_as_functype_const(const wasm_externtype_t*);
WASM_API_EXTERN const wasm_globaltype_t* wasm_externtype_as_globaltype_const(const wasm_externtype_t*);
WASM_API_EXTERN const wasm_tabletype_t* wasm_externtype_as_tabletype_const(const wasm_externtype_t*);
WASM_API_EXTERN const wasm_memorytype_t* wasm_externtype_as_memorytype_const(const wasm_externtype_t*);
// Import Types
WASM_DECLARE_TYPE(importtype)
WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new(
own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*);
WASM_API_EXTERN const wasm_name_t* wasm_importtype_module(const wasm_importtype_t*);
WASM_API_EXTERN const wasm_name_t* wasm_importtype_name(const wasm_importtype_t*);
WASM_API_EXTERN const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t*);
// Export Types
WASM_DECLARE_TYPE(exporttype)
WASM_API_EXTERN own wasm_exporttype_t* wasm_exporttype_new(
own wasm_name_t*, own wasm_externtype_t*);
WASM_API_EXTERN const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t*);
WASM_API_EXTERN const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t*);
///////////////////////////////////////////////////////////////////////////////
// Runtime Objects
// Values
struct wasm_ref_t;
typedef struct wasm_val_t {
wasm_valkind_t kind;
union {
int32_t i32;
int64_t i64;
float32_t f32;
float64_t f64;
struct wasm_ref_t* ref;
} of;
} wasm_val_t;
WASM_API_EXTERN void wasm_val_delete(own wasm_val_t* v);
WASM_API_EXTERN void wasm_val_copy(own wasm_val_t* out, const wasm_val_t*);
WASM_DECLARE_VEC(val, )
// References
#define WASM_DECLARE_REF_BASE(name) \
WASM_DECLARE_OWN(name) \
\
WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*); \
WASM_API_EXTERN bool wasm_##name##_same(const wasm_##name##_t*, const wasm_##name##_t*); \
\
WASM_API_EXTERN void* wasm_##name##_get_host_info(const wasm_##name##_t*); \
WASM_API_EXTERN void wasm_##name##_set_host_info(wasm_##name##_t*, void*); \
WASM_API_EXTERN void wasm_##name##_set_host_info_with_finalizer( \
wasm_##name##_t*, void*, void (*)(void*));
#define WASM_DECLARE_REF(name) \
WASM_DECLARE_REF_BASE(name) \
\
WASM_API_EXTERN wasm_ref_t* wasm_##name##_as_ref(wasm_##name##_t*); \
WASM_API_EXTERN wasm_##name##_t* wasm_ref_as_##name(wasm_ref_t*); \
WASM_API_EXTERN const wasm_ref_t* wasm_##name##_as_ref_const(const wasm_##name##_t*); \
WASM_API_EXTERN const wasm_##name##_t* wasm_ref_as_##name##_const(const wasm_ref_t*);
#define WASM_DECLARE_SHARABLE_REF(name) \
WASM_DECLARE_REF(name) \
WASM_DECLARE_OWN(shared_##name) \
\
WASM_API_EXTERN own wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t*); \
WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, const wasm_shared_##name##_t*);
WASM_DECLARE_REF_BASE(ref)
// Frames
WASM_DECLARE_OWN(frame)
WASM_DECLARE_VEC(frame, *)
WASM_API_EXTERN own wasm_frame_t* wasm_frame_copy(const wasm_frame_t*);
WASM_API_EXTERN struct wasm_instance_t* wasm_frame_instance(const wasm_frame_t*);
WASM_API_EXTERN uint32_t wasm_frame_func_index(const wasm_frame_t*);
WASM_API_EXTERN size_t wasm_frame_func_offset(const wasm_frame_t*);
WASM_API_EXTERN size_t wasm_frame_module_offset(const wasm_frame_t*);
// Traps
typedef wasm_name_t wasm_message_t; // null terminated
WASM_DECLARE_REF(trap)
WASM_API_EXTERN own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t*);
WASM_API_EXTERN void wasm_trap_message(const wasm_trap_t*, own wasm_message_t* out);
WASM_API_EXTERN own wasm_frame_t* wasm_trap_origin(const wasm_trap_t*);
WASM_API_EXTERN void wasm_trap_trace(const wasm_trap_t*, own wasm_frame_vec_t* out);
// Foreign Objects
WASM_DECLARE_REF(foreign)
WASM_API_EXTERN own wasm_foreign_t* wasm_foreign_new(wasm_store_t*);
// Modules
WASM_DECLARE_SHARABLE_REF(module)
WASM_API_EXTERN own wasm_module_t* wasm_module_new(
wasm_store_t*, const wasm_byte_vec_t* binary);
WASM_API_EXTERN bool wasm_module_validate(wasm_store_t*, const wasm_byte_vec_t* binary);
WASM_API_EXTERN void wasm_module_imports(const wasm_module_t*, own wasm_importtype_vec_t* out);
WASM_API_EXTERN void wasm_module_exports(const wasm_module_t*, own wasm_exporttype_vec_t* out);
WASM_API_EXTERN void wasm_module_serialize(const wasm_module_t*, own wasm_byte_vec_t* out);
WASM_API_EXTERN own wasm_module_t* wasm_module_deserialize(wasm_store_t*, const wasm_byte_vec_t*);
// Function Instances
WASM_DECLARE_REF(func)
typedef own wasm_trap_t* (*wasm_func_callback_t)(
const wasm_val_vec_t* args, own wasm_val_vec_t* results);
typedef own wasm_trap_t* (*wasm_func_callback_with_env_t)(
void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results);
WASM_API_EXTERN own wasm_func_t* wasm_func_new(
wasm_store_t*, const wasm_functype_t*, wasm_func_callback_t);
WASM_API_EXTERN own wasm_func_t* wasm_func_new_with_env(
wasm_store_t*, const wasm_functype_t* type, wasm_func_callback_with_env_t,
void* env, void (*finalizer)(void*));
WASM_API_EXTERN own wasm_functype_t* wasm_func_type(const wasm_func_t*);
WASM_API_EXTERN size_t wasm_func_param_arity(const wasm_func_t*);
WASM_API_EXTERN size_t wasm_func_result_arity(const wasm_func_t*);
WASM_API_EXTERN own wasm_trap_t* wasm_func_call(
const wasm_func_t*, const wasm_val_vec_t* args, wasm_val_vec_t* results);
// Global Instances
WASM_DECLARE_REF(global)
WASM_API_EXTERN own wasm_global_t* wasm_global_new(
wasm_store_t*, const wasm_globaltype_t*, const wasm_val_t*);
WASM_API_EXTERN own wasm_globaltype_t* wasm_global_type(const wasm_global_t*);
WASM_API_EXTERN void wasm_global_get(const wasm_global_t*, own wasm_val_t* out);
WASM_API_EXTERN void wasm_global_set(wasm_global_t*, const wasm_val_t*);
// Table Instances
WASM_DECLARE_REF(table)
typedef uint32_t wasm_table_size_t;
WASM_API_EXTERN own wasm_table_t* wasm_table_new(
wasm_store_t*, const wasm_tabletype_t*, wasm_ref_t* init);
WASM_API_EXTERN own wasm_tabletype_t* wasm_table_type(const wasm_table_t*);
WASM_API_EXTERN own wasm_ref_t* wasm_table_get(const wasm_table_t*, wasm_table_size_t index);
WASM_API_EXTERN bool wasm_table_set(wasm_table_t*, wasm_table_size_t index, wasm_ref_t*);
WASM_API_EXTERN wasm_table_size_t wasm_table_size(const wasm_table_t*);
WASM_API_EXTERN bool wasm_table_grow(wasm_table_t*, wasm_table_size_t delta, wasm_ref_t* init);
// Memory Instances
WASM_DECLARE_REF(memory)
typedef uint32_t wasm_memory_pages_t;
static const size_t MEMORY_PAGE_SIZE = 0x10000;
WASM_API_EXTERN own wasm_memory_t* wasm_memory_new(wasm_store_t*, const wasm_memorytype_t*);
WASM_API_EXTERN own wasm_memorytype_t* wasm_memory_type(const wasm_memory_t*);
WASM_API_EXTERN byte_t* wasm_memory_data(wasm_memory_t*);
WASM_API_EXTERN size_t wasm_memory_data_size(const wasm_memory_t*);
WASM_API_EXTERN wasm_memory_pages_t wasm_memory_size(const wasm_memory_t*);
WASM_API_EXTERN bool wasm_memory_grow(wasm_memory_t*, wasm_memory_pages_t delta);
// Externals
WASM_DECLARE_REF(extern)
WASM_DECLARE_VEC(extern, *)
WASM_API_EXTERN wasm_externkind_t wasm_extern_kind(const wasm_extern_t*);
WASM_API_EXTERN own wasm_externtype_t* wasm_extern_type(const wasm_extern_t*);
WASM_API_EXTERN wasm_extern_t* wasm_func_as_extern(wasm_func_t*);
WASM_API_EXTERN wasm_extern_t* wasm_global_as_extern(wasm_global_t*);
WASM_API_EXTERN wasm_extern_t* wasm_table_as_extern(wasm_table_t*);
WASM_API_EXTERN wasm_extern_t* wasm_memory_as_extern(wasm_memory_t*);
WASM_API_EXTERN wasm_func_t* wasm_extern_as_func(wasm_extern_t*);
WASM_API_EXTERN wasm_global_t* wasm_extern_as_global(wasm_extern_t*);
WASM_API_EXTERN wasm_table_t* wasm_extern_as_table(wasm_extern_t*);
WASM_API_EXTERN wasm_memory_t* wasm_extern_as_memory(wasm_extern_t*);
WASM_API_EXTERN const wasm_extern_t* wasm_func_as_extern_const(const wasm_func_t*);
WASM_API_EXTERN const wasm_extern_t* wasm_global_as_extern_const(const wasm_global_t*);
WASM_API_EXTERN const wasm_extern_t* wasm_table_as_extern_const(const wasm_table_t*);
WASM_API_EXTERN const wasm_extern_t* wasm_memory_as_extern_const(const wasm_memory_t*);
WASM_API_EXTERN const wasm_func_t* wasm_extern_as_func_const(const wasm_extern_t*);
WASM_API_EXTERN const wasm_global_t* wasm_extern_as_global_const(const wasm_extern_t*);
WASM_API_EXTERN const wasm_table_t* wasm_extern_as_table_const(const wasm_extern_t*);
WASM_API_EXTERN const wasm_memory_t* wasm_extern_as_memory_const(const wasm_extern_t*);
// Module Instances
WASM_DECLARE_REF(instance)
WASM_API_EXTERN own wasm_instance_t* wasm_instance_new(
wasm_store_t*, const wasm_module_t*, const wasm_extern_vec_t* imports,
own wasm_trap_t**
);
WASM_API_EXTERN void wasm_instance_exports(const wasm_instance_t*, own wasm_extern_vec_t* out);
///////////////////////////////////////////////////////////////////////////////
// Convenience
// Vectors
#define WASM_EMPTY_VEC {0, NULL}
#define WASM_ARRAY_VEC(array) {sizeof(array)/sizeof(*(array)), array}
// Value Type construction short-hands
static inline own wasm_valtype_t* wasm_valtype_new_i32(void) {
return wasm_valtype_new(WASM_I32);
}
static inline own wasm_valtype_t* wasm_valtype_new_i64(void) {
return wasm_valtype_new(WASM_I64);
}
static inline own wasm_valtype_t* wasm_valtype_new_f32(void) {
return wasm_valtype_new(WASM_F32);
}
static inline own wasm_valtype_t* wasm_valtype_new_f64(void) {
return wasm_valtype_new(WASM_F64);
}
static inline own wasm_valtype_t* wasm_valtype_new_anyref(void) {
return wasm_valtype_new(WASM_ANYREF);
}
static inline own wasm_valtype_t* wasm_valtype_new_funcref(void) {
return wasm_valtype_new(WASM_FUNCREF);
}
// Function Types construction short-hands
static inline own wasm_functype_t* wasm_functype_new_0_0(void) {
wasm_valtype_vec_t params, results;
wasm_valtype_vec_new_empty(&params);
wasm_valtype_vec_new_empty(&results);
return wasm_functype_new(&params, &results);
}
static inline own wasm_functype_t* wasm_functype_new_1_0(
own wasm_valtype_t* p
) {
wasm_valtype_t* ps[1] = {p};
wasm_valtype_vec_t params, results;
wasm_valtype_vec_new(&params, 1, ps);
wasm_valtype_vec_new_empty(&results);
return wasm_functype_new(&params, &results);
}
static inline own wasm_functype_t* wasm_functype_new_2_0(
own wasm_valtype_t* p1, own wasm_valtype_t* p2
) {
wasm_valtype_t* ps[2] = {p1, p2};
wasm_valtype_vec_t params, results;
wasm_valtype_vec_new(&params, 2, ps);
wasm_valtype_vec_new_empty(&results);
return wasm_functype_new(&params, &results);
}
static inline own wasm_functype_t* wasm_functype_new_3_0(
own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3
) {
wasm_valtype_t* ps[3] = {p1, p2, p3};
wasm_valtype_vec_t params, results;
wasm_valtype_vec_new(&params, 3, ps);
wasm_valtype_vec_new_empty(&results);
return wasm_functype_new(&params, &results);
}
static inline own wasm_functype_t* wasm_functype_new_0_1(
own wasm_valtype_t* r
) {
wasm_valtype_t* rs[1] = {r};
wasm_valtype_vec_t params, results;
wasm_valtype_vec_new_empty(&params);
wasm_valtype_vec_new(&results, 1, rs);
return wasm_functype_new(&params, &results);
}
static inline own wasm_functype_t* wasm_functype_new_1_1(
own wasm_valtype_t* p, own wasm_valtype_t* r
) {
wasm_valtype_t* ps[1] = {p};
wasm_valtype_t* rs[1] = {r};
wasm_valtype_vec_t params, results;
wasm_valtype_vec_new(&params, 1, ps);
wasm_valtype_vec_new(&results, 1, rs);
return wasm_functype_new(&params, &results);
}
static inline own wasm_functype_t* wasm_functype_new_2_1(
own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* r
) {
wasm_valtype_t* ps[2] = {p1, p2};
wasm_valtype_t* rs[1] = {r};
wasm_valtype_vec_t params, results;
wasm_valtype_vec_new(&params, 2, ps);
wasm_valtype_vec_new(&results, 1, rs);
return wasm_functype_new(&params, &results);
}
static inline own wasm_functype_t* wasm_functype_new_3_1(
own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3,
own wasm_valtype_t* r
) {
wasm_valtype_t* ps[3] = {p1, p2, p3};
wasm_valtype_t* rs[1] = {r};
wasm_valtype_vec_t params, results;
wasm_valtype_vec_new(&params, 3, ps);
wasm_valtype_vec_new(&results, 1, rs);
return wasm_functype_new(&params, &results);
}
static inline own wasm_functype_t* wasm_functype_new_0_2(
own wasm_valtype_t* r1, own wasm_valtype_t* r2
) {
wasm_valtype_t* rs[2] = {r1, r2};
wasm_valtype_vec_t params, results;
wasm_valtype_vec_new_empty(&params);
wasm_valtype_vec_new(&results, 2, rs);
return wasm_functype_new(&params, &results);
}
static inline own wasm_functype_t* wasm_functype_new_1_2(
own wasm_valtype_t* p, own wasm_valtype_t* r1, own wasm_valtype_t* r2
) {
wasm_valtype_t* ps[1] = {p};
wasm_valtype_t* rs[2] = {r1, r2};
wasm_valtype_vec_t params, results;
wasm_valtype_vec_new(&params, 1, ps);
wasm_valtype_vec_new(&results, 2, rs);
return wasm_functype_new(&params, &results);
}
static inline own wasm_functype_t* wasm_functype_new_2_2(
own wasm_valtype_t* p1, own wasm_valtype_t* p2,
own wasm_valtype_t* r1, own wasm_valtype_t* r2
) {
wasm_valtype_t* ps[2] = {p1, p2};
wasm_valtype_t* rs[2] = {r1, r2};
wasm_valtype_vec_t params, results;
wasm_valtype_vec_new(&params, 2, ps);
wasm_valtype_vec_new(&results, 2, rs);
return wasm_functype_new(&params, &results);
}
static inline own wasm_functype_t* wasm_functype_new_3_2(
own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3,
own wasm_valtype_t* r1, own wasm_valtype_t* r2
) {
wasm_valtype_t* ps[3] = {p1, p2, p3};
wasm_valtype_t* rs[2] = {r1, r2};
wasm_valtype_vec_t params, results;
wasm_valtype_vec_new(&params, 3, ps);
wasm_valtype_vec_new(&results, 2, rs);
return wasm_functype_new(&params, &results);
}
// Value construction short-hands
static inline void wasm_val_init_ptr(own wasm_val_t* out, void* p) {
#if UINTPTR_MAX == UINT32_MAX
out->kind = WASM_I32;
out->of.i32 = (intptr_t)p;
#elif UINTPTR_MAX == UINT64_MAX
out->kind = WASM_I64;
out->of.i64 = (intptr_t)p;
#endif
}
static inline void* wasm_val_ptr(const wasm_val_t* val) {
#if UINTPTR_MAX == UINT32_MAX
return (void*)(intptr_t)val->of.i32;
#elif UINTPTR_MAX == UINT64_MAX
return (void*)(intptr_t)val->of.i64;
#endif
}
#define WASM_I32_VAL(i) {.kind = WASM_I32, .of = {.i32 = i}}
#define WASM_I64_VAL(i) {.kind = WASM_I64, .of = {.i64 = i}}
#define WASM_F32_VAL(z) {.kind = WASM_F32, .of = {.f32 = z}}
#define WASM_F64_VAL(z) {.kind = WASM_F64, .of = {.f64 = z}}
#define WASM_REF_VAL(r) {.kind = WASM_ANYREF, .of = {.ref = r}}
#define WASM_INIT_VAL {.kind = WASM_ANYREF, .of = {.ref = NULL}}
///////////////////////////////////////////////////////////////////////////////
#undef own
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifdef WASM_H

228
lib/src/wasm/wasmtime.h Normal file
View file

@ -0,0 +1,228 @@
/**
* \mainpage Wasmtime C API
*
* This documentation is an overview and API reference for the C API of
* Wasmtime. The C API is spread between three different header files:
*
* * \ref wasmtime.h
* * \ref wasi.h
* * \ref wasm.h
*
* The \ref wasmtime.h header file includes all the other header files and is
* the main header file you'll likely be using. The \ref wasm.h header file
* comes directly from the
* [WebAssembly/wasm-c-api](https://github.com/WebAssembly/wasm-c-api)
* repository, and at this time the upstream header file does not have
* documentation so Wasmtime provides documentation here. It should be noted
* some semantics may be Wasmtime-specific and may not be portable to other
* engines.
*
* ## Installing the C API
*
* To install the C API from precompiled binaries you can download the
* appropriate binary from the [releases page of
* Wasmtime](https://github.com/bytecodealliance/wasmtime/releases). Artifacts
* for the C API all end in "-c-api" for the filename.
*
* Each archive contains an `include` directory with necessary headers, as well
* as a `lib` directory with both a static archive and a dynamic library of
* Wasmtime. You can link to either of them as you see fit.
*
* ## Installing the C API through CMake
*
* CMake can be used to make the process of linking and compiling easier. An
* example of this if you have wasmtime as a git submodule at
* `third_party/wasmtime`:
* ```
* add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/third_party/wasmtime/crates/c-api
* ${CMAKE_CURRENT_BINARY_DIR}/wasmtime)
* ...
* target_include_directories(YourProject PUBLIC wasmtime)
* target_link_libraries(YourProject PUBLIC wasmtime)
* ```
* `BUILD_SHARED_LIBS` is provided as a define if you would like to build a
* shared library instead. You must distribute the appropriate shared library
* for your platform if you do this.
*
* ## Linking against the C API
*
* You'll want to arrange the `include` directory of the C API to be in your
* compiler's header path (e.g. the `-I` flag). If you're compiling for Windows
* and you're using the static library then you'll also need to pass
* `-DWASM_API_EXTERN=` and `-DWASI_API_EXTERN=` to disable dllimport.
*
* Your final artifact can then be linked with `-lwasmtime`. If you're linking
* against the static library you may need to pass other system libraries
* depending on your platform:
*
* * Linux - `-lpthread -ldl -lm`
* * macOS - no extra flags needed
* * Windows - `ws2_32.lib advapi32.lib userenv.lib ntdll.lib shell32.lib ole32.lib bcrypt.lib`
*
* ## Building from Source
*
* The C API is located in the
* [`crates/c-api`](https://github.com/bytecodealliance/wasmtime/tree/main/crates/c-api)
* directory of the [Wasmtime
* repository](https://github.com/bytecodealliance/wasmtime). To build from
* source you'll need a Rust compiler and a checkout of the `wasmtime` project.
* Afterwards you can execute:
*
* ```
* $ cargo build --release -p wasmtime-c-api
* ```
*
* This will place the final artifacts in `target/release`, with names depending
* on what platform you're compiling for.
*
* ## Other resources
*
* Some other handy resources you might find useful when exploring the C API
* documentation are:
*
* * [Rust `wasmtime` crate
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/) -
* although this documentation is for Rust and not C, you'll find that many
* functions mirror one another and there may be extra documentation in Rust
* you find helpful. If you find yourself having to frequently do this,
* though, please feel free to [file an
* issue](https://github.com/bytecodealliance/wasmtime/issues/new).
*
* * [C embedding
* examples](https://bytecodealliance.github.io/wasmtime/examples-c-embed.html)
* are available online and are tested from the Wasmtime repository itself.
*
* * [Contribution documentation for
* Wasmtime](https://bytecodealliance.github.io/wasmtime/contributing.html) in
* case you're interested in helping out!
*/
/**
* \file wasmtime.h
*
* \brief Wasmtime's C API
*
* This file is the central inclusion point for Wasmtime's C API. There are a
* number of sub-header files but this file includes them all. The C API is
* based on \ref wasm.h but there are many Wasmtime-specific APIs which are
* tailored to Wasmtime's implementation.
*
* The #wasm_config_t and #wasm_engine_t types are used from \ref wasm.h.
* Additionally all type-level information (like #wasm_functype_t) is also
* used from \ref wasm.h. Otherwise, though, all wasm objects (like
* #wasmtime_store_t or #wasmtime_func_t) are used from this header file.
*
* ### Thread Safety
*
* The multithreading story of the C API very closely follows the
* multithreading story of the Rust API for Wasmtime. All objects are safe to
* send to other threads so long as user-specific data is also safe to send to
* other threads. Functions are safe to call from any thread but some functions
* cannot be called concurrently. For example, functions which correspond to
* `&T` in Rust can be called concurrently with any other methods that take
* `&T`. Functions that take `&mut T` in Rust, however, cannot be called
* concurrently with any other function (but can still be invoked on any
* thread).
*
* This generally equates to mutation of internal state. Functions which don't
* mutate anything, such as learning type information through
* #wasmtime_func_type, can be called concurrently. Functions which do require
* mutation, for example #wasmtime_func_call, cannot be called concurrently.
* This is conveyed in the C API with either `const wasmtime_context_t*`
* (concurrency is ok as it's read-only) or `wasmtime_context_t*` (concurrency
* is not ok, mutation may happen).
*
* When in doubt assume that functions cannot be called concurrently with
* aliasing objects.
*
* ### Aliasing
*
* The C API for Wasmtime is intended to be a relatively thin layer over the
* Rust API for Wasmtime. Rust has much more strict rules about aliasing than C
* does, and the Rust API for Wasmtime is designed around these rules to be
* used safely. These same rules must be upheld when using the C API of
* Wasmtime.
*
* The main consequence of this is that the #wasmtime_context_t pointer into
* the #wasmtime_store_t must be carefully used. Since the context is an
* internal pointer into the store it must be used carefully to ensure you're
* not doing something that Rust would otherwise forbid at compile time. A
* #wasmtime_context_t can only be used when you would otherwise have been
* provided access to it. For example in a host function created with
* #wasmtime_func_new you can use #wasmtime_context_t in the host function
* callback. This is because an argument, a #wasmtime_caller_t, provides access
* to #wasmtime_context_t. On the other hand a destructor passed to
* #wasmtime_externref_new, however, cannot use a #wasmtime_context_t because
* it was not provided access to one. Doing so may lead to memory unsafety.
*
* ### Stores
*
* A foundational construct in this API is the #wasmtime_store_t. A store is a
* collection of host-provided objects and instantiated wasm modules. Stores are
* often treated as a "single unit" and items within a store are all allowed to
* reference one another. References across stores cannot currently be created.
* For example you cannot pass a function from one store into another store.
*
* A store is not intended to be a global long-lived object. Stores provide no
* means of internal garbage collections of wasm objects (such as instances),
* meaning that no memory from a store will be deallocated until you call
* #wasmtime_store_delete. If you're working with a web server, for example,
* then it's recommended to think of a store as a "one per request" sort of
* construct. Globally you'd have one #wasm_engine_t and a cache of
* #wasmtime_module_t instances compiled into that engine. Each request would
* create a new #wasmtime_store_t and then instantiate a #wasmtime_module_t
* into the store. This process of creating a store and instantiating a module
* is expected to be quite fast. When the request is finished you'd delete the
* #wasmtime_store_t keeping memory usage reasonable for the lifetime of the
* server.
*/
#ifndef WASMTIME_API_H
#define WASMTIME_API_H
#include <wasi.h>
#include <wasmtime/config.h>
#include <wasmtime/engine.h>
#include <wasmtime/error.h>
#include <wasmtime/extern.h>
#include <wasmtime/func.h>
#include <wasmtime/global.h>
#include <wasmtime/instance.h>
#include <wasmtime/linker.h>
#include <wasmtime/memory.h>
#include <wasmtime/module.h>
#include <wasmtime/store.h>
#include <wasmtime/table.h>
#include <wasmtime/trap.h>
#include <wasmtime/val.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Converts from the text format of WebAssembly to to the binary format.
*
* \param wat this it the input pointer with the WebAssembly Text Format inside of
* it. This will be parsed and converted to the binary format.
* \param wat_len this it the length of `wat`, in bytes.
* \param ret if the conversion is successful, this byte vector is filled in with
* the WebAssembly binary format.
*
* \return a non-null error if parsing fails, or returns `NULL`. If parsing
* fails then `ret` isn't touched.
*
* This function does not take ownership of `wat`, and the caller is expected to
* deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t.
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_wat2wasm(
const char *wat,
size_t wat_len,
wasm_byte_vec_t *ret
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_API_H

View file

@ -0,0 +1,369 @@
/**
* \file wasmtime/async.h
*
* \brief Wasmtime async functionality
*
* Async functionality in Wasmtime is well documented here:
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support
*
* All WebAssembly executes synchronously, but an async support enables the Wasm code
* be executed on a seperate stack, so it can be paused and resumed. There are three
* mechanisms for yielding control from wasm to the caller: fuel, epochs, and async host functions.
*
* When WebAssembly is executed, a #wasmtime_call_future_t is returned. This struct represents the
* state of the execution and each call to #wasmtime_call_future_poll will execute the WebAssembly
* code on a seperate stack until the function returns or yields control back to the caller.
*
* It's expected these futures are pulled in a loop until completed, at which point the future
* should be deleted. Functions that return a #wasmtime_call_future_t are special in that all
* parameters to that function should not be modified in any way and must be kept alive until
* the future is deleted. This includes concurrent calls for a single store - another function
* on a store should not be called while there is a #wasmtime_call_future_t alive.
*
* As for asynchronous host calls - the reverse contract is upheld. Wasmtime will keep all parameters
* to the function alive and unmodified until the #wasmtime_func_async_continuation_callback_t returns
* true.
*
*/
#ifndef WASMTIME_ASYNC_H
#define WASMTIME_ASYNC_H
#include <wasm.h>
#include <wasmtime/error.h>
#include <wasmtime/config.h>
#include <wasmtime/store.h>
#include <wasmtime/func.h>
#include <wasmtime/linker.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Whether or not to enable support for asynchronous functions in Wasmtime.
*
* When enabled, the config can optionally define host functions with async.
* Instances created and functions called with this Config must be called through their asynchronous APIs, however.
* For example using wasmtime_func_call will panic when used with this config.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support
*/
WASMTIME_CONFIG_PROP(void, async_support, bool)
/**
* \brief Configures the size of the stacks used for asynchronous execution.
*
* This setting configures the size of the stacks that are allocated for asynchronous execution.
*
* The value cannot be less than max_wasm_stack.
*
* The amount of stack space guaranteed for host functions is async_stack_size - max_wasm_stack, so take care
* not to set these two values close to one another; doing so may cause host functions to overflow the stack
* and abort the process.
*
* By default this option is 2 MiB.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_stack_size
*/
WASMTIME_CONFIG_PROP(void, async_stack_size, uint64_t)
/**
* \brief Configures a Store to yield execution of async WebAssembly code
* periodically.
*
* When a Store is configured to consume fuel with
* `wasmtime_config_consume_fuel` this method will configure what happens when
* fuel runs out. Specifically executing WebAssembly will be suspended and
* control will be yielded back to the caller.
*
* This is only suitable with use of a store associated with an async config
* because only then are futures used and yields are possible.
*
* \param context the context for the store to configure.
* \param interval the amount of fuel at which to yield. A value of 0 will
* disable yielding.
*/
WASM_API_EXTERN void
wasmtime_context_fuel_async_yield_interval(wasmtime_context_t *context,
uint64_t interval);
/**
* \brief Configures epoch-deadline expiration to yield to the async caller and the update the deadline.
*
* This is only suitable with use of a store associated with an async config because
* only then are futures used and yields are possible.
*
* See the Rust documentation for more:
* https://docs.wasmtime.dev/api/wasmtime/struct.Store.html#method.epoch_deadline_async_yield_and_update
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_context_epoch_deadline_async_yield_and_update(
wasmtime_context_t *context,
uint64_t delta);
/**
* The callback to determine a continuation's current state.
*
* Return true if the host call has completed, otherwise false will
* continue to yield WebAssembly execution.
*/
typedef bool (*wasmtime_func_async_continuation_callback_t)(void *env);
/**
* A continuation for the current state of the host function's execution.
*/
typedef struct wasmtime_async_continuation_t {
/// Callback for if the async function has completed.
wasmtime_func_async_continuation_callback_t callback;
/// User-provided argument to pass to the callback.
void *env;
/// A finalizer for the user-provided *env
void (*finalizer)(void *);
} wasmtime_async_continuation_t;
/**
* \brief Callback signature for #wasmtime_linker_define_async_func.
*
* This is a host function that returns a continuation to be called later.
*
* All the arguments to this function will be kept alive until the continuation
* returns that it has errored or has completed.
*
* \param env user-provided argument passed to #wasmtime_linker_define_async_func
* \param caller a temporary object that can only be used during this function
* call. Used to acquire #wasmtime_context_t or caller's state
* \param args the arguments provided to this function invocation
* \param nargs how many arguments are provided
* \param results where to write the results of this function
* \param nresults how many results must be produced
* \param trap_ret if assigned a not `NULL` value then the called function will
* trap with the returned error. Note that ownership of trap is transferred
* to wasmtime.
* \param continuation_ret the returned continuation that determines when the
* async function has completed executing.
*
* Only supported for async stores.
*
* See #wasmtime_func_callback_t for more information.
*/
typedef void (*wasmtime_func_async_callback_t)(
void *env,
wasmtime_caller_t *caller,
const wasmtime_val_t *args,
size_t nargs,
wasmtime_val_t *results,
size_t nresults,
wasm_trap_t** trap_ret,
wasmtime_async_continuation_t * continuation_ret);
/**
* \brief The structure representing a asynchronously running function.
*
* This structure is always owned by the caller and must be deleted using #wasmtime_call_future_delete.
*
* Functions that return this type require that the parameters to the function are unmodified until
* this future is destroyed.
*/
typedef struct wasmtime_call_future wasmtime_call_future_t;
/**
* \brief Executes WebAssembly in the function.
*
* Returns true if the function call has completed. After this function returns true, it should *not* be
* called again for a given future.
*
* This function returns false if execution has yielded either due to being out of fuel
* (see wasmtime_context_fuel_async_yield_interval), or the epoch has been incremented enough
* (see wasmtime_context_epoch_deadline_async_yield_and_update). The function may also return false if
* asynchronous host functions have been called, which then calling this function will call the
* continuation from the async host function.
*
* For more see the information at
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#asynchronous-wasm
*
*/
WASM_API_EXTERN bool wasmtime_call_future_poll(wasmtime_call_future_t *future);
/**
* /brief Frees the underlying memory for a future.
*
* All wasmtime_call_future_t are owned by the caller and should be deleted using this function.
*/
WASM_API_EXTERN void wasmtime_call_future_delete(wasmtime_call_future_t *future);
/**
* \brief Invokes this function with the params given, returning the results asynchronously.
*
* This function is the same as wasmtime_func_call except that it is asynchronous.
* This is only compatible with stores associated with an asynchronous config.
*
* The result is a future that is owned by the caller and must be deleted via #wasmtime_call_future_delete.
*
* The `args` and `results` pointers may be `NULL` if the corresponding length is zero.
* The `trap_ret` and `error_ret` pointers may *not* be `NULL`.
*
* Does not take ownership of #wasmtime_val_t arguments or #wasmtime_val_t results,
* and all parameters to this function must be kept alive and not modified until the
* returned #wasmtime_call_future_t is deleted. This includes the context and store
* parameters. Only a single future can be alive for a given store at a single time
* (meaning only call this function after the previous call's future was deleted).
*
* See the header documentation for for more information.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/struct.Func.html#method.call_async
*/
WASM_API_EXTERN wasmtime_call_future_t* wasmtime_func_call_async(
wasmtime_context_t *context,
const wasmtime_func_t *func,
const wasmtime_val_t *args,
size_t nargs,
wasmtime_val_t *results,
size_t nresults,
wasm_trap_t** trap_ret,
wasmtime_error_t** error_ret);
/**
* \brief Defines a new async function in this linker.
*
* This function behaves similar to #wasmtime_linker_define_func, except it supports async
* callbacks.
*
* The callback `cb` will be invoked on another stack (fiber for Windows).
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_linker_define_async_func(
wasmtime_linker_t *linker,
const char *module,
size_t module_len,
const char *name,
size_t name_len,
const wasm_functype_t *ty,
wasmtime_func_async_callback_t cb,
void *data,
void (*finalizer)(void *));
/**
* \brief Instantiates a #wasm_module_t with the items defined in this linker for an async store.
*
* This is the same as #wasmtime_linker_instantiate but used for async stores
* (which requires functions are called asynchronously). The returning #wasmtime_call_future_t
* must be polled using #wasmtime_call_future_poll, and is owned and must be deleted using #wasmtime_call_future_delete.
*
* The `trap_ret` and `error_ret` pointers may *not* be `NULL` and the returned memory is owned by the caller.
*
* All arguments to this function must outlive the returned future and be unmodified until the future is deleted.
*/
WASM_API_EXTERN wasmtime_call_future_t *wasmtime_linker_instantiate_async(
const wasmtime_linker_t *linker,
wasmtime_context_t *store,
const wasmtime_module_t *module,
wasmtime_instance_t *instance,
wasm_trap_t** trap_ret,
wasmtime_error_t** error_ret);
/**
* \brief Instantiates instance within the given store.
*
* This will also run the function's startup function, if there is one.
*
* For more information on async instantiation see #wasmtime_linker_instantiate_async.
*
* \param instance_pre the pre-initialized instance
* \param store the store in which to create the instance
* \param instance where to store the returned instance
* \param trap_ret where to store the returned trap
* \param error_ret where to store the returned trap
*
* The `trap_ret` and `error_ret` pointers may *not* be `NULL` and the returned memory is owned by the caller.
*
* All arguments to this function must outlive the returned future and be unmodified until the future is deleted.
*/
WASM_API_EXTERN wasmtime_call_future_t *wasmtime_instance_pre_instantiate_async(
const wasmtime_instance_pre_t* instance_pre,
wasmtime_context_t *store,
wasmtime_instance_t *instance,
wasm_trap_t** trap_ret,
wasmtime_error_t** error_ret);
/**
* A callback to get the top of the stack address and the length of the stack,
* excluding guard pages.
*
* For more information about the parameters see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/trait.StackMemory.html
*/
typedef uint8_t *(*wasmtime_stack_memory_get_callback_t)(
void *env,
size_t *out_len);
/**
* A Stack instance created from a #wasmtime_new_stack_memory_callback_t.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/trait.StackMemory.html
*/
typedef struct {
/// User provided value to be passed to get_memory and grow_memory
void *env;
/// Callback to get the memory and size of this LinearMemory
wasmtime_stack_memory_get_callback_t get_stack_memory;
/// An optional finalizer for env
void (*finalizer)(void*);
} wasmtime_stack_memory_t;
/**
* A callback to create a new StackMemory from the specified parameters.
*
* The result should be written to `stack_ret` and wasmtime will own the values written
* into that struct.
*
* This callback must be thread-safe.
*
* For more information about the parameters see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/trait.StackCreator.html#tymethod.new_stack
*/
typedef wasmtime_error_t *(*wasmtime_new_stack_memory_callback_t)(
void *env,
size_t size,
wasmtime_stack_memory_t *stack_ret);
/**
* A representation of custom stack creator.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/trait.StackCreator.html
*/
typedef struct {
/// User provided value to be passed to new_stack
void* env;
/// The callback to create a new stack, must be thread safe
wasmtime_new_stack_memory_callback_t new_stack;
/// An optional finalizer for env.
void (*finalizer)(void*);
} wasmtime_stack_creator_t;
/**
* Sets a custom stack creator.
*
* Custom memory creators are used when creating creating async instance stacks for
* the on-demand instance allocation strategy.
*
* The config does **not** take ownership of the #wasmtime_stack_creator_t passed in, but
* instead copies all the values in the struct.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.with_host_stack
*/
WASM_API_EXTERN void wasmtime_config_host_stack_creator_set(
wasm_config_t*,
wasmtime_stack_creator_t*);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_ASYNC_H

View file

@ -0,0 +1,484 @@
/**
* \file wasmtime/config.h
*
* \brief Wasmtime-specific extensions to #wasm_config_t
*/
#ifndef WASMTIME_CONFIG_H
#define WASMTIME_CONFIG_H
#include <wasm.h>
#include <wasmtime/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Specifier for how Wasmtime will compile code, values are in
* #wasmtime_strategy_enum
*/
typedef uint8_t wasmtime_strategy_t;
/**
* \brief Different ways that Wasmtime can compile WebAssembly
*
* The default value is #WASMTIME_STRATEGY_AUTO.
*/
enum wasmtime_strategy_enum { // Strategy
/// Automatically picks the compilation backend, currently always defaulting
/// to Cranelift.
WASMTIME_STRATEGY_AUTO,
/// Indicates that Wasmtime will unconditionally use Cranelift to compile
/// WebAssembly code.
WASMTIME_STRATEGY_CRANELIFT,
};
/**
* \brief Specifier of what optimization level to use for generated JIT code.
*
* See #wasmtime_opt_level_enum for possible values.
*/
typedef uint8_t wasmtime_opt_level_t;
/**
* \brief Different ways Wasmtime can optimize generated code.
*
* The default value is #WASMTIME_OPT_LEVEL_SPEED.
*/
enum wasmtime_opt_level_enum { // OptLevel
/// Generated code will not be optimized at all.
WASMTIME_OPT_LEVEL_NONE,
/// Generated code will be optimized purely for speed.
WASMTIME_OPT_LEVEL_SPEED,
/// Generated code will be optimized, but some speed optimizations are
/// disabled if they cause the generated code to be significantly larger.
WASMTIME_OPT_LEVEL_SPEED_AND_SIZE,
};
/**
* \brief Different ways wasmtime can enable profiling JIT code.
*
* See #wasmtime_profiling_strategy_enum for possible values.
*/
typedef uint8_t wasmtime_profiling_strategy_t;
/**
* \brief Different ways to profile JIT code.
*
* The default is #WASMTIME_PROFILING_STRATEGY_NONE.
*/
enum wasmtime_profiling_strategy_enum { // ProfilingStrategy
/// No profiling is enabled at runtime.
WASMTIME_PROFILING_STRATEGY_NONE,
/// Linux's "jitdump" support in `perf` is enabled and when Wasmtime is run
/// under `perf` necessary calls will be made to profile generated JIT code.
WASMTIME_PROFILING_STRATEGY_JITDUMP,
/// Support for VTune will be enabled and the VTune runtime will be informed,
/// at runtime, about JIT code.
///
/// Note that this isn't always enabled at build time.
WASMTIME_PROFILING_STRATEGY_VTUNE,
/// Linux's simple "perfmap" support in `perf` is enabled and when Wasmtime is
/// run under `perf` necessary calls will be made to profile generated JIT
/// code.
WASMTIME_PROFILING_STRATEGY_PERFMAP,
};
#define WASMTIME_CONFIG_PROP(ret, name, ty) \
WASM_API_EXTERN ret wasmtime_config_##name##_set(wasm_config_t*, ty);
/**
* \brief Configures whether DWARF debug information is constructed at runtime
* to describe JIT code.
*
* This setting is `false` by default. When enabled it will attempt to inform
* native debuggers about DWARF debugging information for JIT code to more
* easily debug compiled WebAssembly via native debuggers. This can also
* sometimes improve the quality of output when profiling is enabled.
*/
WASMTIME_CONFIG_PROP(void, debug_info, bool)
/**
* \brief Whether or not fuel is enabled for generated code.
*
* This setting is `false` by default. When enabled it will enable fuel counting
* meaning that fuel will be consumed every time a wasm instruction is executed,
* and trap when reaching zero.
*/
WASMTIME_CONFIG_PROP(void, consume_fuel, bool)
/**
* \brief Whether or not epoch-based interruption is enabled for generated code.
*
* This setting is `false` by default. When enabled wasm code will check the
* current epoch periodically and abort if the current epoch is beyond a
* store-configured limit.
*
* Note that when this setting is enabled all stores will immediately trap and
* need to have their epoch deadline otherwise configured with
* #wasmtime_context_set_epoch_deadline.
*
* Note that the current epoch is engine-local and can be incremented with
* #wasmtime_engine_increment_epoch.
*/
WASMTIME_CONFIG_PROP(void, epoch_interruption, bool)
/**
* \brief Configures the maximum stack size, in bytes, that JIT code can use.
*
* This setting is 2MB by default. Configuring this setting will limit the
* amount of native stack space that JIT code can use while it is executing. If
* you're hitting stack overflow you can try making this setting larger, or if
* you'd like to limit wasm programs to less stack you can also configure this.
*
* Note that this setting is not interpreted with 100% precision. Additionally
* the amount of stack space that wasm takes is always relative to the first
* invocation of wasm on the stack, so recursive calls with host frames in the
* middle will all need to fit within this setting.
*/
WASMTIME_CONFIG_PROP(void, max_wasm_stack, size_t)
/**
* \brief Configures whether the WebAssembly threading proposal is enabled.
*
* This setting is `false` by default.
*
* Note that threads are largely unimplemented in Wasmtime at this time.
*/
WASMTIME_CONFIG_PROP(void, wasm_threads, bool)
/**
* \brief Configures whether the WebAssembly reference types proposal is
* enabled.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_reference_types, bool)
/**
* \brief Configures whether the WebAssembly SIMD proposal is
* enabled.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_simd, bool)
/**
* \brief Configures whether the WebAssembly relaxed SIMD proposal is
* enabled.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_relaxed_simd, bool)
/**
* \brief Configures whether the WebAssembly relaxed SIMD proposal is
* in deterministic mode.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_relaxed_simd_deterministic, bool)
/**
* \brief Configures whether the WebAssembly bulk memory proposal is
* enabled.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_bulk_memory, bool)
/**
* \brief Configures whether the WebAssembly multi value proposal is
* enabled.
*
* This setting is `true` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_multi_value, bool)
/**
* \brief Configures whether the WebAssembly multi-memory proposal is
* enabled.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_multi_memory, bool)
/**
* \brief Configures whether the WebAssembly memory64 proposal is
* enabled.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_memory64, bool)
/**
* \brief Configures how JIT code will be compiled.
*
* This setting is #WASMTIME_STRATEGY_AUTO by default.
*/
WASMTIME_CONFIG_PROP(void, strategy, wasmtime_strategy_t)
/**
* \brief Configure whether wasmtime should compile a module using multiple threads.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.parallel_compilation.
*/
WASMTIME_CONFIG_PROP(void, parallel_compilation, bool)
/**
* \brief Configures whether Cranelift's debug verifier is enabled.
*
* This setting in `false` by default.
*
* When cranelift is used for compilation this enables expensive debug checks
* within Cranelift itself to verify it's correct.
*/
WASMTIME_CONFIG_PROP(void, cranelift_debug_verifier, bool)
/**
* \brief Configures whether Cranelift should perform a NaN-canonicalization pass.
*
* When Cranelift is used as a code generation backend this will configure
* it to replace NaNs with a single canonical value. This is useful for users
* requiring entirely deterministic WebAssembly computation.
*
* This is not required by the WebAssembly spec, so it is not enabled by default.
*
* The default value for this is `false`
*/
WASMTIME_CONFIG_PROP(void, cranelift_nan_canonicalization, bool)
/**
* \brief Configures Cranelift's optimization level for JIT code.
*
* This setting in #WASMTIME_OPT_LEVEL_SPEED by default.
*/
WASMTIME_CONFIG_PROP(void, cranelift_opt_level, wasmtime_opt_level_t)
/**
* \brief Configures the profiling strategy used for JIT code.
*
* This setting in #WASMTIME_PROFILING_STRATEGY_NONE by default.
*/
WASMTIME_CONFIG_PROP(void, profiler, wasmtime_profiling_strategy_t)
/**
* \brief Configures the static style of memory to always be used.
*
* This setting is `false` by default.
*
* For more information see the Rust documentation at
* https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_forced.
*/
WASMTIME_CONFIG_PROP(void, static_memory_forced, bool)
/**
* \brief Configures the maximum size for memory to be considered "static"
*
* For more information see the Rust documentation at
* https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_maximum_size.
*/
WASMTIME_CONFIG_PROP(void, static_memory_maximum_size, uint64_t)
/**
* \brief Configures the guard region size for "static" memory.
*
* For more information see the Rust documentation at
* https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_guard_size.
*/
WASMTIME_CONFIG_PROP(void, static_memory_guard_size, uint64_t)
/**
* \brief Configures the guard region size for "dynamic" memory.
*
* For more information see the Rust documentation at
* https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.dynamic_memory_guard_size.
*/
WASMTIME_CONFIG_PROP(void, dynamic_memory_guard_size, uint64_t)
/**
* \brief Configures the size, in bytes, of the extra virtual memory space reserved after a dynamic memory for growing into.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.dynamic_memory_reserved_for_growth
*/
WASMTIME_CONFIG_PROP(void, dynamic_memory_reserved_for_growth, uint64_t)
/**
* \brief Configures whether to generate native unwind information (e.g. .eh_frame on Linux).
*
* This option defaults to true.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.native_unwind_info
*/
WASMTIME_CONFIG_PROP(void, native_unwind_info, bool)
/**
* \brief Enables Wasmtime's cache and loads configuration from the specified
* path.
*
* By default the Wasmtime compilation cache is disabled. The configuration path
* here can be `NULL` to use the default settings, and otherwise the argument
* here must be a file on the filesystem with TOML configuration -
* https://bytecodealliance.github.io/wasmtime/cli-cache.html.
*
* An error is returned if the cache configuration could not be loaded or if the
* cache could not be enabled.
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_config_cache_config_load(wasm_config_t*, const char*);
/**
* \brief Configures the target triple that this configuration will produce
* machine code for.
*
* This option defaults to the native host. Calling this method will
* additionally disable inference of the native features of the host (e.g.
* detection of SSE4.2 on x86_64 hosts). Native features can be reenabled with
* the `cranelift_flag_{set,enable}` properties.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.config
*/
WASMTIME_CONFIG_PROP(wasmtime_error_t*, target, const char*)
/**
* \brief Enables a target-specific flag in Cranelift.
*
* This can be used, for example, to enable SSE4.2 on x86_64 hosts. Settings can
* be explored with `wasmtime settings` on the CLI.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.cranelift_flag_enable
*/
WASM_API_EXTERN void wasmtime_config_cranelift_flag_enable(wasm_config_t*, const char*);
/**
* \brief Sets a target-specific flag in Cranelift to the specified value.
*
* This can be used, for example, to enable SSE4.2 on x86_64 hosts. Settings can
* be explored with `wasmtime settings` on the CLI.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.cranelift_flag_set
*/
WASM_API_EXTERN void wasmtime_config_cranelift_flag_set(wasm_config_t*, const char *key, const char *value);
/**
* Return the data from a LinearMemory instance.
*
* The size in bytes as well as the maximum number of bytes that can be allocated should be
* returned as well.
*
* For more information about see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html
*/
typedef uint8_t *(*wasmtime_memory_get_callback_t)(
void *env,
size_t *byte_size,
size_t *maximum_byte_size);
/**
* Grow the memory to the `new_size` in bytes.
*
* For more information about the parameters see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html#tymethod.grow_to
*/
typedef wasmtime_error_t *(*wasmtime_memory_grow_callback_t)(
void *env,
size_t new_size);
/**
* A LinearMemory instance created from a #wasmtime_new_memory_callback_t.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html
*/
typedef struct wasmtime_linear_memory {
/// User provided value to be passed to get_memory and grow_memory
void *env;
/// Callback to get the memory and size of this LinearMemory
wasmtime_memory_get_callback_t get_memory;
/// Callback to request growing the memory
wasmtime_memory_grow_callback_t grow_memory;
/// An optional finalizer for env
void (*finalizer)(void*);
} wasmtime_linear_memory_t;
/**
* A callback to create a new LinearMemory from the specified parameters.
*
* The result should be written to `memory_ret` and wasmtime will own the values written
* into that struct.
*
* This callback must be thread-safe.
*
* For more information about the parameters see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/trait.MemoryCreator.html#tymethod.new_memory
*/
typedef wasmtime_error_t *(*wasmtime_new_memory_callback_t)(
void *env,
const wasm_memorytype_t *ty,
size_t minimum,
size_t maximum,
size_t reserved_size_in_bytes,
size_t guard_size_in_bytes,
wasmtime_linear_memory_t *memory_ret);
/**
* A representation of custom memory creator and methods for an instance of LinearMemory.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/trait.MemoryCreator.html
*/
typedef struct wasmtime_memory_creator {
/// User provided value to be passed to new_memory
void* env;
/// The callback to create new memory, must be thread safe
wasmtime_new_memory_callback_t new_memory;
/// An optional finalizer for env.
void (*finalizer)(void*);
} wasmtime_memory_creator_t;
/**
* Sets a custom memory creator.
*
* Custom memory creators are used when creating host Memory objects or when creating instance
* linear memories for the on-demand instance allocation strategy.
*
* The config does **not** take ownership of the #wasmtime_memory_creator_t passed in, but
* instead copies all the values in the struct.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.with_host_memory
*/
WASM_API_EXTERN void wasmtime_config_host_memory_creator_set(
wasm_config_t*,
wasmtime_memory_creator_t*);
/**
* \brief Configures whether copy-on-write memory-mapped data is used to initialize a linear memory.
*
* Initializing linear memory via a copy-on-write mapping can drastically improve instantiation costs
* of a WebAssembly module because copying memory is deferred. Additionally if a page of memory is
* only ever read from WebAssembly and never written too then the same underlying page of data will
* be reused between all instantiations of a module meaning that if a module is instantiated many
* times this can lower the overall memory required needed to run that module.
*
* This option defaults to true.
*
* For more information see the Rust documentation at
* https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.memory_init_cow
*/
WASMTIME_CONFIG_PROP(void, memory_init_cow, bool)
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_CONFIG_H

View file

@ -0,0 +1,36 @@
/**
* \file wasmtime/engine.h
*
* Wasmtime-specific extensions to #wasm_engine_t.
*/
#ifndef WASMTIME_ENGINE_H
#define WASMTIME_ENGINE_H
#include <wasm.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Increments the engine-local epoch variable.
*
* This function will increment the engine's current epoch which can be used to
* force WebAssembly code to trap if the current epoch goes beyond the
* #wasmtime_store_t configured epoch deadline.
*
* This function is safe to call from any thread, and it is also
* async-signal-safe.
*
* See also #wasmtime_config_epoch_interruption_set.
*/
WASM_API_EXTERN void wasmtime_engine_increment_epoch(wasm_engine_t *engine);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_ENGINE_H

View file

@ -0,0 +1,78 @@
/**
* \file wasmtime/error.h
*
* \brief Definition and accessors of #wasmtime_error_t
*/
#ifndef WASMTIME_ERROR_H
#define WASMTIME_ERROR_H
#include <wasm.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \typedef wasmtime_error_t
* \brief Convenience alias for #wasmtime_error
*
* \struct wasmtime_error
* \brief Errors generated by Wasmtime.
* \headerfile wasmtime/error.h
*
* This opaque type represents an error that happened as part of one of the
* functions below. Errors primarily have an error message associated with them
* at this time, which you can acquire by calling #wasmtime_error_message.
*
* Errors are safe to share across threads and must be deleted with
* #wasmtime_error_delete.
*/
typedef struct wasmtime_error wasmtime_error_t;
/**
* \brief Creates a new error with the provided message.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_error_new(const char*);
/**
* \brief Deletes an error.
*/
WASM_API_EXTERN void wasmtime_error_delete(wasmtime_error_t *error);
/**
* \brief Returns the string description of this error.
*
* This will "render" the error to a string and then return the string
* representation of the error to the caller. The `message` argument should be
* uninitialized before this function is called and the caller is responsible
* for deallocating it with #wasm_byte_vec_delete afterwards.
*/
WASM_API_EXTERN void wasmtime_error_message(
const wasmtime_error_t *error,
wasm_name_t *message
);
/**
* \brief Attempts to extract a WASI-specific exit status from this error.
*
* Returns `true` if the error is a WASI "exit" trap and has a return status.
* If `true` is returned then the exit status is returned through the `status`
* pointer. If `false` is returned then this is not a wasi exit trap.
*/
WASM_API_EXTERN bool wasmtime_error_exit_status(const wasmtime_error_t*, int *status);
/**
* \brief Attempts to extract a WebAssembly trace from this error.
*
* This is similar to #wasm_trap_trace except that it takes a #wasmtime_error_t
* as input. The `out` argument will be filled in with the wasm trace, if
* present.
*/
WASM_API_EXTERN void wasmtime_error_wasm_trace(const wasmtime_error_t*, wasm_frame_vec_t *out);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_ERROR_H

View file

@ -0,0 +1,145 @@
/**
* \file wasmtime/extern.h
*
* \brief Definition of #wasmtime_extern_t and external items.
*/
#ifndef WASMTIME_EXTERN_H
#define WASMTIME_EXTERN_H
#include <wasmtime/module.h>
#include <wasmtime/store.h>
#ifdef __cplusplus
extern "C" {
#endif
/// \brief Representation of a function in Wasmtime.
///
/// Functions are represented with a 64-bit identifying integer in Wasmtime.
/// They do not have any destructor associated with them. Functions cannot
/// interoperate between #wasmtime_store_t instances and if the wrong function
/// is passed to the wrong store then it may trigger an assertion to abort the
/// process.
typedef struct wasmtime_func {
/// Internal identifier of what store this belongs to, never zero.
uint64_t store_id;
/// Internal index within the store.
size_t index;
} wasmtime_func_t;
/// \brief Representation of a table in Wasmtime.
///
/// Tables are represented with a 64-bit identifying integer in Wasmtime.
/// They do not have any destructor associated with them. Tables cannot
/// interoperate between #wasmtime_store_t instances and if the wrong table
/// is passed to the wrong store then it may trigger an assertion to abort the
/// process.
typedef struct wasmtime_table {
/// Internal identifier of what store this belongs to, never zero.
uint64_t store_id;
/// Internal index within the store.
size_t index;
} wasmtime_table_t;
/// \brief Representation of a memory in Wasmtime.
///
/// Memories are represented with a 64-bit identifying integer in Wasmtime.
/// They do not have any destructor associated with them. Memories cannot
/// interoperate between #wasmtime_store_t instances and if the wrong memory
/// is passed to the wrong store then it may trigger an assertion to abort the
/// process.
typedef struct wasmtime_memory {
/// Internal identifier of what store this belongs to, never zero.
uint64_t store_id;
/// Internal index within the store.
size_t index;
} wasmtime_memory_t;
/// \brief Representation of a global in Wasmtime.
///
/// Globals are represented with a 64-bit identifying integer in Wasmtime.
/// They do not have any destructor associated with them. Globals cannot
/// interoperate between #wasmtime_store_t instances and if the wrong global
/// is passed to the wrong store then it may trigger an assertion to abort the
/// process.
typedef struct wasmtime_global {
/// Internal identifier of what store this belongs to, never zero.
uint64_t store_id;
/// Internal index within the store.
size_t index;
} wasmtime_global_t;
/// \brief Discriminant of #wasmtime_extern_t
typedef uint8_t wasmtime_extern_kind_t;
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
/// function
#define WASMTIME_EXTERN_FUNC 0
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
/// global
#define WASMTIME_EXTERN_GLOBAL 1
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
/// table
#define WASMTIME_EXTERN_TABLE 2
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
/// memory
#define WASMTIME_EXTERN_MEMORY 3
/**
* \typedef wasmtime_extern_union_t
* \brief Convenience alias for #wasmtime_extern_union
*
* \union wasmtime_extern_union
* \brief Container for different kinds of extern items.
*
* This type is contained in #wasmtime_extern_t and contains the payload for the
* various kinds of items an extern wasm item can be.
*/
typedef union wasmtime_extern_union {
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_FUNC
wasmtime_func_t func;
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_GLOBAL
wasmtime_global_t global;
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_TABLE
wasmtime_table_t table;
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_MEMORY
wasmtime_memory_t memory;
} wasmtime_extern_union_t;
/**
* \typedef wasmtime_extern_t
* \brief Convenience alias for #wasmtime_extern_t
*
* \union wasmtime_extern
* \brief Container for different kinds of extern items.
*
* Note that this structure may contain an owned value, namely
* #wasmtime_module_t, depending on the context in which this is used. APIs
* which consume a #wasmtime_extern_t do not take ownership, but APIs that
* return #wasmtime_extern_t require that #wasmtime_extern_delete is called to
* deallocate the value.
*/
typedef struct wasmtime_extern {
/// Discriminant of which field of #of is valid.
wasmtime_extern_kind_t kind;
/// Container for the extern item's value.
wasmtime_extern_union_t of;
} wasmtime_extern_t;
/// \brief Deletes a #wasmtime_extern_t.
void wasmtime_extern_delete(wasmtime_extern_t *val);
/// \brief Returns the type of the #wasmtime_extern_t defined within the given
/// store.
///
/// Does not take ownership of `context` or `val`, but the returned
/// #wasm_externtype_t is an owned value that needs to be deleted.
wasm_externtype_t *wasmtime_extern_type(wasmtime_context_t *context, wasmtime_extern_t *val);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_EXTERN_H

View file

@ -0,0 +1,314 @@
/**
* \file wasmtime/func.h
*
* Wasmtime definitions of how to interact with host and wasm functions.
*/
#ifndef WASMTIME_FUNC_H
#define WASMTIME_FUNC_H
#include <wasm.h>
#include <wasmtime/val.h>
#include <wasmtime/store.h>
#include <wasmtime/extern.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \typedef wasmtime_caller_t
* \brief Alias to #wasmtime_caller
*
* \brief Structure used to learn about the caller of a host-defined function.
* \struct wasmtime_caller
*
* This structure is an argument to #wasmtime_func_callback_t. The purpose
* of this structure is acquire a #wasmtime_context_t pointer to interact with
* objects, but it can also be used for inspect the state of the caller (such as
* getting memories and functions) with #wasmtime_caller_export_get.
*
* This object is never owned and does not need to be deleted.
*/
typedef struct wasmtime_caller wasmtime_caller_t;
/**
* \brief Callback signature for #wasmtime_func_new.
*
* This is the function signature for host functions that can be made accessible
* to WebAssembly. The arguments to this function are:
*
* \param env user-provided argument passed to #wasmtime_func_new
* \param caller a temporary object that can only be used during this function
* call. Used to acquire #wasmtime_context_t or caller's state
* \param args the arguments provided to this function invocation
* \param nargs how many arguments are provided
* \param results where to write the results of this function
* \param nresults how many results must be produced
*
* Callbacks are guaranteed to get called with the right types of arguments, but
* they must produce the correct number and types of results. Failure to do so
* will cause traps to get raised on the wasm side.
*
* This callback can optionally return a #wasm_trap_t indicating that a trap
* should be raised in WebAssembly. It's expected that in this case the caller
* relinquishes ownership of the trap and it is passed back to the engine.
*/
typedef wasm_trap_t* (*wasmtime_func_callback_t)(
void *env,
wasmtime_caller_t* caller,
const wasmtime_val_t *args,
size_t nargs,
wasmtime_val_t *results,
size_t nresults);
/**
* \brief Creates a new host-defined function.
*
* Inserts a host-defined function into the `store` provided which can be used
* to then instantiate a module with or define within a #wasmtime_linker_t.
*
* \param store the store in which to create the function
* \param type the wasm type of the function that's being created
* \param callback the host-defined callback to invoke
* \param env host-specific data passed to the callback invocation, can be
* `NULL`
* \param finalizer optional finalizer for `env`, can be `NULL`
* \param ret the #wasmtime_func_t return value to be filled in.
*
* The returned function can only be used with the specified `store`.
*/
WASM_API_EXTERN void wasmtime_func_new(
wasmtime_context_t *store,
const wasm_functype_t* type,
wasmtime_func_callback_t callback,
void *env,
void (*finalizer)(void*),
wasmtime_func_t *ret
);
/**
* \brief Callback signature for #wasmtime_func_new_unchecked.
*
* This is the function signature for host functions that can be made accessible
* to WebAssembly. The arguments to this function are:
*
* \param env user-provided argument passed to #wasmtime_func_new_unchecked
* \param caller a temporary object that can only be used during this function
* call. Used to acquire #wasmtime_context_t or caller's state
* \param args_and_results storage space for both the parameters to the
* function as well as the results of the function. The size of this
* array depends on the function type that the host function is created
* with, but it will be the maximum of the number of parameters and
* number of results.
* \param num_args_and_results the size of the `args_and_results` parameter in
* units of #wasmtime_val_raw_t.
*
* This callback can optionally return a #wasm_trap_t indicating that a trap
* should be raised in WebAssembly. It's expected that in this case the caller
* relinquishes ownership of the trap and it is passed back to the engine.
*
* This differs from #wasmtime_func_callback_t in that the payload of
* `args_and_results` does not have type information, nor does it have sizing
* information. This is especially unsafe because it's only valid within the
* particular #wasm_functype_t that the function was created with. The onus is
* on the embedder to ensure that `args_and_results` are all read correctly
* for parameters and all written for results within the execution of a
* function.
*
* Parameters will be listed starting at index 0 in the `args_and_results`
* array. Results are also written starting at index 0, which will overwrite
* the arguments.
*/
typedef wasm_trap_t* (*wasmtime_func_unchecked_callback_t)(
void *env,
wasmtime_caller_t* caller,
wasmtime_val_raw_t *args_and_results,
size_t num_args_and_results);
/**
* \brief Creates a new host function in the same manner of #wasmtime_func_new,
* but the function-to-call has no type information available at runtime.
*
* This function is very similar to #wasmtime_func_new. The difference is that
* this version is "more unsafe" in that when the host callback is invoked there
* is no type information and no checks that the right types of values are
* produced. The onus is on the consumer of this API to ensure that all
* invariants are upheld such as:
*
* * The host callback reads parameters correctly and interprets their types
* correctly.
* * If a trap doesn't happen then all results are written to the results
* pointer. All results must have the correct type.
* * Types such as `funcref` cannot cross stores.
* * Types such as `externref` have valid reference counts.
*
* It's generally only recommended to use this if your application can wrap
* this in a safe embedding. This should not be frequently used due to the
* number of invariants that must be upheld on the wasm<->host boundary. On the
* upside, though, this flavor of host function will be faster to call than
* those created by #wasmtime_func_new (hence the reason for this function's
* existence).
*/
WASM_API_EXTERN void wasmtime_func_new_unchecked(
wasmtime_context_t *store,
const wasm_functype_t* type,
wasmtime_func_unchecked_callback_t callback,
void *env,
void (*finalizer)(void*),
wasmtime_func_t *ret
);
/**
* \brief Returns the type of the function specified
*
* The returned #wasm_functype_t is owned by the caller.
*/
WASM_API_EXTERN wasm_functype_t* wasmtime_func_type(
const wasmtime_context_t *store,
const wasmtime_func_t *func
);
/**
* \brief Call a WebAssembly function.
*
* This function is used to invoke a function defined within a store. For
* example this might be used after extracting a function from a
* #wasmtime_instance_t.
*
* \param store the store which owns `func`
* \param func the function to call
* \param args the arguments to the function call
* \param nargs the number of arguments provided
* \param results where to write the results of the function call
* \param nresults the number of results expected
* \param trap where to store a trap, if one happens.
*
* There are three possible return states from this function:
*
* 1. The returned error is non-null. This means `results`
* wasn't written to and `trap` will have `NULL` written to it. This state
* means that programmer error happened when calling the function, for
* example when the size of the arguments/results was wrong, the types of the
* arguments were wrong, or arguments may come from the wrong store.
* 2. The trap pointer is filled in. This means the returned error is `NULL` and
* `results` was not written to. This state means that the function was
* executing but hit a wasm trap while executing.
* 3. The error and trap returned are both `NULL` and `results` are written to.
* This means that the function call succeeded and the specified results were
* produced.
*
* The `trap` pointer cannot be `NULL`. The `args` and `results` pointers may be
* `NULL` if the corresponding length is zero.
*
* Does not take ownership of #wasmtime_val_t arguments. Gives ownership of
* #wasmtime_val_t results.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_func_call(
wasmtime_context_t *store,
const wasmtime_func_t *func,
const wasmtime_val_t *args,
size_t nargs,
wasmtime_val_t *results,
size_t nresults,
wasm_trap_t **trap
);
/**
* \brief Call a WebAssembly function in an "unchecked" fashion.
*
* This function is similar to #wasmtime_func_call except that there is no type
* information provided with the arguments (or sizing information). Consequently
* this is less safe to call since it's up to the caller to ensure that `args`
* has an appropriate size and all the parameters are configured with their
* appropriate values/types. Additionally all the results must be interpreted
* correctly if this function returns successfully.
*
* Parameters must be specified starting at index 0 in the `args_and_results`
* array. Results are written starting at index 0, which will overwrite
* the arguments.
*
* Callers must ensure that various correctness variants are upheld when this
* API is called such as:
*
* * The `args_and_results` pointer has enough space to hold all the parameters
* and all the results (but not at the same time).
* * The `args_and_results_len` contains the length of the `args_and_results`
* buffer.
* * Parameters must all be configured as if they were the correct type.
* * Values such as `externref` and `funcref` are valid within the store being
* called.
*
* When in doubt it's much safer to call #wasmtime_func_call. This function is
* faster than that function, but the tradeoff is that embeddings must uphold
* more invariants rather than relying on Wasmtime to check them for you.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_func_call_unchecked(
wasmtime_context_t *store,
const wasmtime_func_t *func,
wasmtime_val_raw_t *args_and_results,
size_t args_and_results_len,
wasm_trap_t **trap
);
/**
* \brief Loads a #wasmtime_extern_t from the caller's context
*
* This function will attempt to look up the export named `name` on the caller
* instance provided. If it is found then the #wasmtime_extern_t for that is
* returned, otherwise `NULL` is returned.
*
* Note that this only works for exported memories right now for WASI
* compatibility.
*
* \param caller the caller object to look up the export from
* \param name the name that's being looked up
* \param name_len the byte length of `name`
* \param item where to store the return value
*
* Returns a nonzero value if the export was found, or 0 if the export wasn't
* found. If the export wasn't found then `item` isn't written to.
*/
WASM_API_EXTERN bool wasmtime_caller_export_get(
wasmtime_caller_t *caller,
const char *name,
size_t name_len,
wasmtime_extern_t *item
);
/**
* \brief Returns the store context of the caller object.
*/
WASM_API_EXTERN wasmtime_context_t* wasmtime_caller_context(wasmtime_caller_t* caller);
/**
* \brief Converts a `raw` nonzero `funcref` value from #wasmtime_val_raw_t
* into a #wasmtime_func_t.
*
* This function can be used to interpret nonzero values of the `funcref` field
* of the #wasmtime_val_raw_t structure. It is assumed that `raw` does not have
* a value of 0, or otherwise the program will abort.
*
* Note that this function is unchecked and unsafe. It's only safe to pass
* values learned from #wasmtime_val_raw_t with the same corresponding
* #wasmtime_context_t that they were produced from. Providing arbitrary values
* to `raw` here or cross-context values with `context` is UB.
*/
WASM_API_EXTERN void wasmtime_func_from_raw(
wasmtime_context_t* context,
void *raw,
wasmtime_func_t *ret);
/**
* \brief Converts a `func` which belongs to `context` into a `usize`
* parameter that is suitable for insertion into a #wasmtime_val_raw_t.
*/
WASM_API_EXTERN void *wasmtime_func_to_raw(
wasmtime_context_t* context,
const wasmtime_func_t *func);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_FUNC_H

View file

@ -0,0 +1,91 @@
/**
* \file wasmtime/global.h
*
* Wasmtime APIs for interacting with WebAssembly globals.
*/
#ifndef WASMTIME_GLOBAL_H
#define WASMTIME_GLOBAL_H
#include <wasm.h>
#include <wasmtime/extern.h>
#include <wasmtime/store.h>
#include <wasmtime/val.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Creates a new global value.
*
* Creates a new host-defined global value within the provided `store`
*
* \param store the store in which to create the global
* \param type the wasm type of the global being created
* \param val the initial value of the global
* \param ret a return pointer for the created global.
*
* This function may return an error if the `val` argument does not match the
* specified type of the global, or if `val` comes from a different store than
* the one provided.
*
* This function does not take ownership of any of its arguments but error is
* owned by the caller.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_global_new(
wasmtime_context_t *store,
const wasm_globaltype_t *type,
const wasmtime_val_t *val,
wasmtime_global_t *ret
);
/**
* \brief Returns the wasm type of the specified global.
*
* The returned #wasm_globaltype_t is owned by the caller.
*/
WASM_API_EXTERN wasm_globaltype_t* wasmtime_global_type(
const wasmtime_context_t *store,
const wasmtime_global_t *global
);
/**
* \brief Get the value of the specified global.
*
* \param store the store that owns `global`
* \param global the global to get
* \param out where to store the value in this global.
*
* This function returns ownership of the contents of `out`, so
* #wasmtime_val_delete may need to be called on the value.
*/
WASM_API_EXTERN void wasmtime_global_get(
wasmtime_context_t *store,
const wasmtime_global_t *global,
wasmtime_val_t *out
);
/**
* \brief Sets a global to a new value.
*
* \param store the store that owns `global`
* \param global the global to set
* \param val the value to store in the global
*
* This function may return an error if `global` is not mutable or if `val` has
* the wrong type for `global`.
*
* THis does not take ownership of any argument but returns ownership of the error.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_global_set(
wasmtime_context_t *store,
const wasmtime_global_t *global,
const wasmtime_val_t *val
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_GLOBAL_H

View file

@ -0,0 +1,173 @@
/**
* \file wasmtime/instance.h
*
* Wasmtime APIs for interacting with wasm instances.
*/
#ifndef WASMTIME_INSTANCE_H
#define WASMTIME_INSTANCE_H
#include <wasm.h>
#include <wasmtime/extern.h>
#include <wasmtime/module.h>
#include <wasmtime/store.h>
#ifdef __cplusplus
extern "C" {
#endif
/// \brief Representation of a instance in Wasmtime.
///
/// Instances are represented with a 64-bit identifying integer in Wasmtime.
/// They do not have any destructor associated with them. Instances cannot
/// interoperate between #wasmtime_store_t instances and if the wrong instance
/// is passed to the wrong store then it may trigger an assertion to abort the
/// process.
typedef struct wasmtime_instance {
/// Internal identifier of what store this belongs to, never zero.
uint64_t store_id;
/// Internal index within the store.
size_t index;
} wasmtime_instance_t;
/**
* \brief Instantiate a wasm module.
*
* This function will instantiate a WebAssembly module with the provided
* imports, creating a WebAssembly instance. The returned instance can then
* afterwards be inspected for exports.
*
* \param store the store in which to create the instance
* \param module the module that's being instantiated
* \param imports the imports provided to the module
* \param nimports the size of `imports`
* \param instance where to store the returned instance
* \param trap where to store the returned trap
*
* This function requires that `imports` is the same size as the imports that
* `module` has. Additionally the `imports` array must be 1:1 lined up with the
* imports of the `module` specified. This is intended to be relatively low
* level, and #wasmtime_linker_instantiate is provided for a more ergonomic
* name-based resolution API.
*
* The states of return values from this function are similar to
* #wasmtime_func_call where an error can be returned meaning something like a
* link error in this context. A trap can be returned (meaning no error or
* instance is returned), or an instance can be returned (meaning no error or
* trap is returned).
*
* Note that this function requires that all `imports` specified must be owned
* by the `store` provided as well.
*
* This function does not take ownership of any of its arguments, but all return
* values are owned by the caller.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_instance_new(
wasmtime_context_t *store,
const wasmtime_module_t *module,
const wasmtime_extern_t* imports,
size_t nimports,
wasmtime_instance_t *instance,
wasm_trap_t **trap
);
/**
* \brief Get an export by name from an instance.
*
* \param store the store that owns `instance`
* \param instance the instance to lookup within
* \param name the export name to lookup
* \param name_len the byte length of `name`
* \param item where to store the returned value
*
* Returns nonzero if the export was found, and `item` is filled in. Otherwise
* returns 0.
*
* Doesn't take ownership of any arguments but does return ownership of the
* #wasmtime_extern_t.
*/
WASM_API_EXTERN bool wasmtime_instance_export_get(
wasmtime_context_t *store,
const wasmtime_instance_t *instance,
const char *name,
size_t name_len,
wasmtime_extern_t *item
);
/**
* \brief Get an export by index from an instance.
*
* \param store the store that owns `instance`
* \param instance the instance to lookup within
* \param index the index to lookup
* \param name where to store the name of the export
* \param name_len where to store the byte length of the name
* \param item where to store the export itself
*
* Returns nonzero if the export was found, and `name`, `name_len`, and `item`
* are filled in. Otherwise returns 0.
*
* Doesn't take ownership of any arguments but does return ownership of the
* #wasmtime_extern_t. The `name` pointer return value is owned by the `store`
* and must be immediately used before calling any other APIs on
* #wasmtime_context_t.
*/
WASM_API_EXTERN bool wasmtime_instance_export_nth(
wasmtime_context_t *store,
const wasmtime_instance_t *instance,
size_t index,
char **name,
size_t *name_len,
wasmtime_extern_t *item
);
/**
* \brief A #wasmtime_instance_t, pre-instantiation, that is ready to be instantiated.
*
* Must be deleted using #wasmtime_instance_pre_delete.
*
* For more information see the Rust documentation:
* https://docs.wasmtime.dev/api/wasmtime/struct.InstancePre.html
*/
typedef struct wasmtime_instance_pre wasmtime_instance_pre_t;
/**
* \brief Delete a previously created wasmtime_instance_pre_t.
*/
WASM_API_EXTERN void
wasmtime_instance_pre_delete(wasmtime_instance_pre_t *instance);
/**
* \brief Instantiates instance within the given store.
*
* This will also run the function's startup function, if there is one.
*
* For more information on instantiation see #wasmtime_instance_new.
*
* \param instance_pre the pre-initialized instance
* \param store the store in which to create the instance
* \param instance where to store the returned instance
* \param trap_ptr where to store the returned trap
*
* \return One of three things can happen as a result of this function. First
* the module could be successfully instantiated and returned through
* `instance`, meaning the return value and `trap` are both set to `NULL`.
* Second the start function may trap, meaning the return value and `instance`
* are set to `NULL` and `trap` describes the trap that happens. Finally
* instantiation may fail for another reason, in which case an error is returned
* and `trap` and `instance` are set to `NULL`.
*
* This function does not take ownership of any of its arguments, and all return
* values are owned by the caller.
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_instance_pre_instantiate(
const wasmtime_instance_pre_t* instance_pre,
wasmtime_store_t *store,
wasmtime_instance_t* instance,
wasm_trap_t **trap_ptr);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_INSTANCE_H

View file

@ -0,0 +1,317 @@
/**
* \file wasmtime/linker.h
*
* Wasmtime API for a name-based linker used to instantiate modules.
*/
#ifndef WASMTIME_LINKER_H
#define WASMTIME_LINKER_H
#include <wasm.h>
#include <wasmtime/error.h>
#include <wasmtime/store.h>
#include <wasmtime/extern.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \typedef wasmtime_linker_t
* \brief Alias to #wasmtime_linker
*
* \struct #wasmtime_linker
* \brief Object used to conveniently link together and instantiate wasm
* modules.
*
* This type corresponds to the `wasmtime::Linker` type in Rust. This
* type is intended to make it easier to manage a set of modules that link
* together, or to make it easier to link WebAssembly modules to WASI.
*
* A #wasmtime_linker_t is a higher level way to instantiate a module than
* #wasm_instance_new since it works at the "string" level of imports rather
* than requiring 1:1 mappings.
*/
typedef struct wasmtime_linker wasmtime_linker_t;
/**
* \brief Creates a new linker for the specified engine.
*
* This function does not take ownership of the engine argument, and the caller
* is expected to delete the returned linker.
*/
WASM_API_EXTERN wasmtime_linker_t* wasmtime_linker_new(wasm_engine_t* engine);
/**
* \brief Deletes a linker
*/
WASM_API_EXTERN void wasmtime_linker_delete(wasmtime_linker_t* linker);
/**
* \brief Configures whether this linker allows later definitions to shadow
* previous definitions.
*
* By default this setting is `false`.
*/
WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t* linker, bool allow_shadowing);
/**
* \brief Defines a new item in this linker.
*
* \param linker the linker the name is being defined in.
* \param store the store that the `item` is owned by.
* \param module the module name the item is defined under.
* \param module_len the byte length of `module`
* \param name the field name the item is defined under
* \param name_len the byte length of `name`
* \param item the item that is being defined in this linker.
*
* \return On success `NULL` is returned, otherwise an error is returned which
* describes why the definition failed.
*
* For more information about name resolution consult the [Rust
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution).
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define(
wasmtime_linker_t *linker,
wasmtime_context_t *store,
const char *module,
size_t module_len,
const char *name,
size_t name_len,
const wasmtime_extern_t *item
);
/**
* \brief Defines a new function in this linker.
*
* \param linker the linker the name is being defined in.
* \param module the module name the item is defined under.
* \param module_len the byte length of `module`
* \param name the field name the item is defined under
* \param name_len the byte length of `name`
* \param ty the type of the function that's being defined
* \param cb the host callback to invoke when the function is called
* \param data the host-provided data to provide as the first argument to the callback
* \param finalizer an optional finalizer for the `data` argument.
*
* \return On success `NULL` is returned, otherwise an error is returned which
* describes why the definition failed.
*
* For more information about name resolution consult the [Rust
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution).
*
* Note that this function does not create a #wasmtime_func_t. This creates a
* store-independent function within the linker, allowing this function
* definition to be used with multiple stores.
*
* For more information about host callbacks see #wasmtime_func_new.
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_func(
wasmtime_linker_t *linker,
const char *module,
size_t module_len,
const char *name,
size_t name_len,
const wasm_functype_t *ty,
wasmtime_func_callback_t cb,
void *data,
void (*finalizer)(void*)
);
/**
* \brief Defines a new function in this linker.
*
* This is the same as #wasmtime_linker_define_func except that it's the analog
* of #wasmtime_func_new_unchecked instead of #wasmtime_func_new. Be sure to
* consult the documentation of #wasmtime_linker_define_func for argument
* information as well as #wasmtime_func_new_unchecked for why this is an
* unsafe API.
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_func_unchecked(
wasmtime_linker_t *linker,
const char *module,
size_t module_len,
const char *name,
size_t name_len,
const wasm_functype_t *ty,
wasmtime_func_unchecked_callback_t cb,
void *data,
void (*finalizer)(void*)
);
/**
* \brief Defines WASI functions in this linker.
*
* \param linker the linker the name is being defined in.
*
* \return On success `NULL` is returned, otherwise an error is returned which
* describes why the definition failed.
*
* This function will provide WASI function names in the specified linker. Note
* that when an instance is created within a store then the store also needs to
* have its WASI settings configured with #wasmtime_context_set_wasi for WASI
* functions to work, otherwise an assert will be tripped that will abort the
* process.
*
* For more information about name resolution consult the [Rust
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution).
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_wasi(
wasmtime_linker_t *linker
);
/**
* \brief Defines an instance under the specified name in this linker.
*
* \param linker the linker the name is being defined in.
* \param store the store that owns `instance`
* \param name the module name to define `instance` under.
* \param name_len the byte length of `name`
* \param instance a previously-created instance.
*
* \return On success `NULL` is returned, otherwise an error is returned which
* describes why the definition failed.
*
* This function will take all of the exports of the `instance` provided and
* defined them under a module called `name` with a field name as the export's
* own name.
*
* For more information about name resolution consult the [Rust
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution).
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_instance(
wasmtime_linker_t *linker,
wasmtime_context_t *store,
const char *name,
size_t name_len,
const wasmtime_instance_t *instance
);
/**
* \brief Instantiates a #wasm_module_t with the items defined in this linker.
*
* \param linker the linker used to instantiate the provided module.
* \param store the store that is used to instantiate within
* \param module the module that is being instantiated.
* \param instance the returned instance, if successful.
* \param trap a trap returned, if the start function traps.
*
* \return One of three things can happen as a result of this function. First
* the module could be successfully instantiated and returned through
* `instance`, meaning the return value and `trap` are both set to `NULL`.
* Second the start function may trap, meaning the return value and `instance`
* are set to `NULL` and `trap` describes the trap that happens. Finally
* instantiation may fail for another reason, in which case an error is returned
* and `trap` and `instance` are set to `NULL`.
*
* This function will attempt to satisfy all of the imports of the `module`
* provided with items previously defined in this linker. If any name isn't
* defined in the linker than an error is returned. (or if the previously
* defined item is of the wrong type).
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_instantiate(
const wasmtime_linker_t *linker,
wasmtime_context_t *store,
const wasmtime_module_t *module,
wasmtime_instance_t *instance,
wasm_trap_t **trap
);
/**
* \brief Defines automatic instantiations of a #wasm_module_t in this linker.
*
* \param linker the linker the module is being added to
* \param store the store that is used to instantiate `module`
* \param name the name of the module within the linker
* \param name_len the byte length of `name`
* \param module the module that's being instantiated
*
* \return An error if the module could not be instantiated or added or `NULL`
* on success.
*
* This function automatically handles [Commands and
* Reactors](https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi)
* instantiation and initialization.
*
* For more information see the [Rust
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#method.module).
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_module(
wasmtime_linker_t *linker,
wasmtime_context_t *store,
const char *name,
size_t name_len,
const wasmtime_module_t *module
);
/**
* \brief Acquires the "default export" of the named module in this linker.
*
* \param linker the linker to load from
* \param store the store to load a function into
* \param name the name of the module to get the default export for
* \param name_len the byte length of `name`
* \param func where to store the extracted default function.
*
* \return An error is returned if the default export could not be found, or
* `NULL` is returned and `func` is filled in otherwise.
*
* For more information see the [Rust
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#method.get_default).
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get_default(
const wasmtime_linker_t *linker,
wasmtime_context_t *store,
const char *name,
size_t name_len,
wasmtime_func_t *func
);
/**
* \brief Loads an item by name from this linker.
*
* \param linker the linker to load from
* \param store the store to load the item into
* \param module the name of the module to get
* \param module_len the byte length of `module`
* \param name the name of the field to get
* \param name_len the byte length of `name`
* \param item where to store the extracted item
*
* \return A nonzero value if the item is defined, in which case `item` is also
* filled in. Otherwise zero is returned.
*/
WASM_API_EXTERN bool wasmtime_linker_get(
const wasmtime_linker_t *linker,
wasmtime_context_t *store,
const char *module,
size_t module_len,
const char *name,
size_t name_len,
wasmtime_extern_t *item
);
/**
* \brief Preform all the checks for instantiating `module` with the linker,
* except that instantiation doesn't actually finish.
*
* \param linker the linker used to instantiate the provided module.
* \param module the module that is being instantiated.
* \param instance_pre the returned instance_pre, if successful.
*
* \return An error or `NULL` if successful.
*
* For more information see the Rust documentation at:
* https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.instantiate_pre
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_instantiate_pre(
const wasmtime_linker_t *linker,
const wasmtime_module_t *module,
wasmtime_instance_pre_t **instance_pre);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_LINKER_H

View file

@ -0,0 +1,123 @@
/**
* \file wasmtime/memory.h
*
* Wasmtime API for interacting with wasm memories.
*/
#ifndef WASMTIME_MEMORY_H
#define WASMTIME_MEMORY_H
#include <wasm.h>
#include <wasmtime/extern.h>
#include <wasmtime/store.h>
#include <wasmtime/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Creates a new memory type from the specified parameters.
*
* Note that this function is preferred over #wasm_memorytype_new for
* compatibility with the memory64 proposal.
*/
WASM_API_EXTERN wasm_memorytype_t *wasmtime_memorytype_new(uint64_t min, bool max_present, uint64_t max, bool is_64);
/**
* \brief Returns the minimum size, in pages, of the specified memory type.
*
* Note that this function is preferred over #wasm_memorytype_limits for
* compatibility with the memory64 proposal.
*/
WASM_API_EXTERN uint64_t wasmtime_memorytype_minimum(const wasm_memorytype_t *ty);
/**
* \brief Returns the maximum size, in pages, of the specified memory type.
*
* If this memory type doesn't have a maximum size listed then `false` is
* returned. Otherwise `true` is returned and the `max` pointer is filled in
* with the specified maximum size, in pages.
*
* Note that this function is preferred over #wasm_memorytype_limits for
* compatibility with the memory64 proposal.
*/
WASM_API_EXTERN bool wasmtime_memorytype_maximum(const wasm_memorytype_t *ty, uint64_t *max);
/**
* \brief Returns whether this type of memory represents a 64-bit memory.
*/
WASM_API_EXTERN bool wasmtime_memorytype_is64(const wasm_memorytype_t *ty);
/**
* \brief Creates a new WebAssembly linear memory
*
* \param store the store to create the memory within
* \param ty the type of the memory to create
* \param ret where to store the returned memory
*
* If an error happens when creating the memory it's returned and owned by the
* caller. If an error happens then `ret` is not filled in.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_new(
wasmtime_context_t *store,
const wasm_memorytype_t* ty,
wasmtime_memory_t *ret
);
/**
* \brief Returns the type of the memory specified
*/
WASM_API_EXTERN wasm_memorytype_t* wasmtime_memory_type(
const wasmtime_context_t *store,
const wasmtime_memory_t *memory
);
/**
* \brief Returns the base pointer in memory where the linear memory starts.
*/
WASM_API_EXTERN uint8_t *wasmtime_memory_data(
const wasmtime_context_t *store,
const wasmtime_memory_t *memory
);
/**
* \brief Returns the byte length of this linear memory.
*/
WASM_API_EXTERN size_t wasmtime_memory_data_size(
const wasmtime_context_t *store,
const wasmtime_memory_t *memory
);
/**
* \brief Returns the length, in WebAssembly pages, of this linear memory
*/
WASM_API_EXTERN uint64_t wasmtime_memory_size(
const wasmtime_context_t *store,
const wasmtime_memory_t *memory
);
/**
* \brief Attempts to grow the specified memory by `delta` pages.
*
* \param store the store that owns `memory`
* \param memory the memory to grow
* \param delta the number of pages to grow by
* \param prev_size where to store the previous size of memory
*
* If memory cannot be grown then `prev_size` is left unchanged and an error is
* returned. Otherwise `prev_size` is set to the previous size of the memory, in
* WebAssembly pages, and `NULL` is returned.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_grow(
wasmtime_context_t *store,
const wasmtime_memory_t *memory,
uint64_t delta,
uint64_t *prev_size
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_MEMORY_H

View file

@ -0,0 +1,170 @@
/**
* \file wasmtime/module.h
*
* APIs for interacting with modules in Wasmtime
*/
#ifndef WASMTIME_MODULE_H
#define WASMTIME_MODULE_H
#include <wasm.h>
#include <wasmtime/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \typedef wasmtime_module_t
* \brief Convenience alias for #wasmtime_module
*
* \struct wasmtime_module
* \brief A compiled Wasmtime module.
*
* This type represents a compiled WebAssembly module. The compiled module is
* ready to be instantiated and can be inspected for imports/exports. It is safe
* to use a module across multiple threads simultaneously.
*/
typedef struct wasmtime_module wasmtime_module_t;
/**
* \brief Compiles a WebAssembly binary into a #wasmtime_module_t
*
* This function will compile a WebAssembly binary into an owned #wasm_module_t.
* This performs the same as #wasm_module_new except that it returns a
* #wasmtime_error_t type to get richer error information.
*
* On success the returned #wasmtime_error_t is `NULL` and the `ret` pointer is
* filled in with a #wasm_module_t. On failure the #wasmtime_error_t is
* non-`NULL` and the `ret` pointer is unmodified.
*
* This function does not take ownership of any of its arguments, but the
* returned error and module are owned by the caller.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new(
wasm_engine_t *engine,
const uint8_t *wasm,
size_t wasm_len,
wasmtime_module_t **ret
);
/**
* \brief Deletes a module.
*/
WASM_API_EXTERN void wasmtime_module_delete(wasmtime_module_t *m);
/**
* \brief Creates a shallow clone of the specified module, increasing the
* internal reference count.
*/
WASM_API_EXTERN wasmtime_module_t *wasmtime_module_clone(wasmtime_module_t *m);
/**
* \brief Same as #wasm_module_imports, but for #wasmtime_module_t.
*/
WASM_API_EXTERN void wasmtime_module_imports(
const wasmtime_module_t *module,
wasm_importtype_vec_t *out
);
/**
* \brief Same as #wasm_module_exports, but for #wasmtime_module_t.
*/
WASM_API_EXTERN void wasmtime_module_exports(
const wasmtime_module_t *module,
wasm_exporttype_vec_t *out
);
/**
* \brief Validate a WebAssembly binary.
*
* This function will validate the provided byte sequence to determine if it is
* a valid WebAssembly binary within the context of the engine provided.
*
* This function does not take ownership of its arguments but the caller is
* expected to deallocate the returned error if it is non-`NULL`.
*
* If the binary validates then `NULL` is returned, otherwise the error returned
* describes why the binary did not validate.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_module_validate(
wasm_engine_t *engine,
const uint8_t *wasm,
size_t wasm_len
);
/**
* \brief This function serializes compiled module artifacts as blob data.
*
* \param module the module
* \param ret if the conversion is successful, this byte vector is filled in with
* the serialized compiled module.
*
* \return a non-null error if parsing fails, or returns `NULL`. If parsing
* fails then `ret` isn't touched.
*
* This function does not take ownership of `module`, and the caller is
* expected to deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t.
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_module_serialize(
wasmtime_module_t* module,
wasm_byte_vec_t *ret
);
/**
* \brief Build a module from serialized data.
*
* This function does not take ownership of any of its arguments, but the
* returned error and module are owned by the caller.
*
* This function is not safe to receive arbitrary user input. See the Rust
* documentation for more information on what inputs are safe to pass in here
* (e.g. only that of #wasmtime_module_serialize)
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_module_deserialize(
wasm_engine_t *engine,
const uint8_t *bytes,
size_t bytes_len,
wasmtime_module_t **ret
);
/**
* \brief Deserialize a module from an on-disk file.
*
* This function is the same as #wasmtime_module_deserialize except that it
* reads the data for the serialized module from the path on disk. This can be
* faster than the alternative which may require copying the data around.
*
* This function does not take ownership of any of its arguments, but the
* returned error and module are owned by the caller.
*
* This function is not safe to receive arbitrary user input. See the Rust
* documentation for more information on what inputs are safe to pass in here
* (e.g. only that of #wasmtime_module_serialize)
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_module_deserialize_file(
wasm_engine_t *engine,
const char *path,
wasmtime_module_t **ret
);
/**
* \brief Returns the range of bytes in memory where this modules compilation image resides.
*
* The compilation image for a module contains executable code, data, debug information, etc.
* This is roughly the same as the wasmtime_module_serialize but not the exact same.
*
* For more details see: https://docs.wasmtime.dev/api/wasmtime/struct.Module.html#method.image_range
*/
WASM_API_EXTERN void wasmtime_module_image_range(
const wasmtime_module_t *module,
size_t *start,
size_t *end
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_MODULE_H

View file

@ -0,0 +1,222 @@
/**
* \file wasmtime/store.h
*
* Wasmtime definition of a "store".
*/
#ifndef WASMTIME_STORE_H
#define WASMTIME_STORE_H
#include <wasm.h>
#include <wasi.h>
#include <wasmtime/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \typedef wasmtime_store_t
* \brief Convenience alias for #wasmtime_store_t
*
* \struct wasmtime_store
* \brief Storage of WebAssembly objects
*
* A store is the unit of isolation between WebAssembly instances in an
* embedding of Wasmtime. Values in one #wasmtime_store_t cannot flow into
* another #wasmtime_store_t. Stores are cheap to create and cheap to dispose.
* It's expected that one-off stores are common in embeddings.
*
* Objects stored within a #wasmtime_store_t are referenced with integer handles
* rather than interior pointers. This means that most APIs require that the
* store be explicitly passed in, which is done via #wasmtime_context_t. It is
* safe to move a #wasmtime_store_t to any thread at any time. A store generally
* cannot be concurrently used, however.
*/
typedef struct wasmtime_store wasmtime_store_t;
/**
* \typedef wasmtime_context_t
* \brief Convenience alias for #wasmtime_context
*
* \struct wasmtime_context
* \brief An interior pointer into a #wasmtime_store_t which is used as
* "context" for many functions.
*
* This context pointer is used pervasively throught Wasmtime's API. This can be
* acquired from #wasmtime_store_context or #wasmtime_caller_context. The
* context pointer for a store is the same for the entire lifetime of a store,
* so it can safely be stored adjacent to a #wasmtime_store_t itself.
*
* Usage of a #wasmtime_context_t must not outlive the original
* #wasmtime_store_t. Additionally #wasmtime_context_t can only be used in
* situations where it has explicitly been granted access to doing so. For
* example finalizers cannot use #wasmtime_context_t because they are not given
* access to it.
*/
typedef struct wasmtime_context wasmtime_context_t;
/**
* \brief Creates a new store within the specified engine.
*
* \param engine the compilation environment with configuration this store is
* connected to
* \param data user-provided data to store, can later be acquired with
* #wasmtime_context_get_data.
* \param finalizer an optional finalizer for `data`
*
* This function creates a fresh store with the provided configuration settings.
* The returned store must be deleted with #wasmtime_store_delete.
*/
WASM_API_EXTERN wasmtime_store_t *wasmtime_store_new(
wasm_engine_t *engine,
void *data,
void (*finalizer)(void*)
);
/**
* \brief Returns the interior #wasmtime_context_t pointer to this store
*/
WASM_API_EXTERN wasmtime_context_t *wasmtime_store_context(wasmtime_store_t *store);
/**
* \brief Provides limits for a store. Used by hosts to limit resource
* consumption of instances. Use negative value to keep the default value
* for the limit.
*
* \param store store where the limits should be set.
* \param memory_size the maximum number of bytes a linear memory can grow to.
* Growing a linear memory beyond this limit will fail. By default,
* linear memory will not be limited.
* \param table_elements the maximum number of elements in a table.
* Growing a table beyond this limit will fail. By default, table elements
* will not be limited.
* \param instances the maximum number of instances that can be created
* for a Store. Module instantiation will fail if this limit is exceeded.
* This value defaults to 10,000.
* \param tables the maximum number of tables that can be created for a Store.
* Module instantiation will fail if this limit is exceeded. This value
* defaults to 10,000.
* \param memories the maximum number of linear memories that can be created
* for a Store. Instantiation will fail with an error if this limit is exceeded.
* This value defaults to 10,000.
*
* Use any negative value for the parameters that should be kept on
* the default values.
*
* Note that the limits are only used to limit the creation/growth of
* resources in the future, this does not retroactively attempt to apply
* limits to the store.
*/
WASM_API_EXTERN void wasmtime_store_limiter(
wasmtime_store_t *store,
int64_t memory_size,
int64_t table_elements,
int64_t instances,
int64_t tables,
int64_t memories
);
/**
* \brief Deletes a store.
*/
WASM_API_EXTERN void wasmtime_store_delete(wasmtime_store_t *store);
/**
* \brief Returns the user-specified data associated with the specified store
*/
WASM_API_EXTERN void *wasmtime_context_get_data(const wasmtime_context_t* context);
/**
* \brief Overwrites the user-specified data associated with this store.
*
* Note that this does not execute the original finalizer for the provided data,
* and the original finalizer will be executed for the provided data when the
* store is deleted.
*/
WASM_API_EXTERN void wasmtime_context_set_data(wasmtime_context_t* context, void *data);
/**
* \brief Perform garbage collection within the given context.
*
* Garbage collects `externref`s that are used within this store. Any
* `externref`s that are discovered to be unreachable by other code or objects
* will have their finalizers run.
*
* The `context` argument must not be NULL.
*/
WASM_API_EXTERN void wasmtime_context_gc(wasmtime_context_t* context);
/**
* \brief Set fuel to this context's store for wasm to consume while executing.
*
* For this method to work fuel consumption must be enabled via
* #wasmtime_config_consume_fuel_set. By default a store starts with 0 fuel
* for wasm to execute with (meaning it will immediately trap).
* This function must be called for the store to have
* some fuel to allow WebAssembly to execute.
*
* Note that when fuel is entirely consumed it will cause wasm to trap.
*
* If fuel is not enabled within this store then an error is returned. If fuel
* is successfully added then NULL is returned.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_context_set_fuel(wasmtime_context_t *store, uint64_t fuel);
/**
* \brief Returns the amount of fuel remaining in this context's store.
*
* If fuel consumption is not enabled via #wasmtime_config_consume_fuel_set
* then this function will return an error. Otherwise `NULL` is returned and the
* fuel parameter is filled in with fuel consumed so far.
*
* Also note that fuel, if enabled, must be originally configured via
* #wasmtime_context_set_fuel.
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_context_get_fuel(const wasmtime_context_t *context, uint64_t *fuel);
/**
* \brief Configures WASI state within the specified store.
*
* This function is required if #wasmtime_linker_define_wasi is called. This
* will configure the WASI state for instances defined within this store to the
* configuration specified.
*
* This function does not take ownership of `context` but it does take ownership
* of `wasi`. The caller should no longer use `wasi` after calling this function
* (even if an error is returned).
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_context_set_wasi(wasmtime_context_t *context, wasi_config_t *wasi);
/**
* \brief Configures the relative deadline at which point WebAssembly code will
* trap or invoke the callback function.
*
* This function configures the store-local epoch deadline after which point
* WebAssembly code will trap or invoke the callback function.
*
* See also #wasmtime_config_epoch_interruption_set and
* #wasmtime_store_epoch_deadline_callback.
*/
WASM_API_EXTERN void wasmtime_context_set_epoch_deadline(wasmtime_context_t *context, uint64_t ticks_beyond_current);
/**
* \brief Configures epoch deadline callback to C function.
*
* This function configures a store-local callback function that will be
* called when the running WebAssembly function has exceeded its epoch
* deadline. That function can return a #wasmtime_error_t to terminate
* the function, or set the delta argument and return NULL to update the
* epoch deadline and resume function execution.
*
* See also #wasmtime_config_epoch_interruption_set and
* #wasmtime_context_set_epoch_deadline.
*/
WASM_API_EXTERN void wasmtime_store_epoch_deadline_callback(wasmtime_store_t *store, wasmtime_error_t* (*func)(wasmtime_context_t*, void*, uint64_t*), void *data);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_STORE_H

View file

@ -0,0 +1,126 @@
/**
* \file wasmtime/table.h
*
* Wasmtime APIs for interacting with WebAssembly tables.
*/
#ifndef WASMTIME_TABLE_H
#define WASMTIME_TABLE_H
#include <wasm.h>
#include <wasmtime/extern.h>
#include <wasmtime/store.h>
#include <wasmtime/error.h>
#include <wasmtime/val.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Creates a new host-defined wasm table.
*
* \param store the store to create the table within
* \param ty the type of the table to create
* \param init the initial value for this table's elements
* \param table where to store the returned table
*
* This function does not take ownership of any of its parameters, but yields
* ownership of returned error. This function may return an error if the `init`
* value does not match `ty`, for example.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_table_new(
wasmtime_context_t *store,
const wasm_tabletype_t *ty,
const wasmtime_val_t *init,
wasmtime_table_t *table
);
/**
* \brief Returns the type of this table.
*
* The caller has ownership of the returned #wasm_tabletype_t
*/
WASM_API_EXTERN wasm_tabletype_t* wasmtime_table_type(
const wasmtime_context_t *store,
const wasmtime_table_t *table
);
/**
* \brief Gets a value in a table.
*
* \param store the store that owns `table`
* \param table the table to access
* \param index the table index to access
* \param val where to store the table's value
*
* This function will attempt to access a table element. If a nonzero value is
* returned then `val` is filled in and is owned by the caller. Otherwise zero
* is returned because the `index` is out-of-bounds.
*/
WASM_API_EXTERN bool wasmtime_table_get(
wasmtime_context_t *store,
const wasmtime_table_t *table,
uint32_t index,
wasmtime_val_t *val
);
/**
* \brief Sets a value in a table.
*
* \param store the store that owns `table`
* \param table the table to write to
* \param index the table index to write
* \param value the value to store.
*
* This function will store `value` into the specified index in the table. This
* does not take ownership of any argument but yields ownership of the error.
* This function can fail if `value` has the wrong type for the table, or if
* `index` is out of bounds.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_table_set(
wasmtime_context_t *store,
const wasmtime_table_t *table,
uint32_t index,
const wasmtime_val_t *value
);
/**
* \brief Returns the size, in elements, of the specified table
*/
WASM_API_EXTERN uint32_t wasmtime_table_size(
const wasmtime_context_t *store,
const wasmtime_table_t *table
);
/**
* \brief Grows a table.
*
* \param store the store that owns `table`
* \param table the table to grow
* \param delta the number of elements to grow the table by
* \param init the initial value for new table element slots
* \param prev_size where to store the previous size of the table before growth
*
* This function will attempt to grow the table by `delta` table elements. This
* can fail if `delta` would exceed the maximum size of the table or if `init`
* is the wrong type for this table. If growth is successful then `NULL` is
* returned and `prev_size` is filled in with the previous size of the table, in
* elements, before the growth happened.
*
* This function does not take ownership of any of its arguments.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_table_grow(
wasmtime_context_t *store,
const wasmtime_table_t *table,
uint32_t delta,
const wasmtime_val_t *init,
uint32_t *prev_size
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_TABLE_H

View file

@ -0,0 +1,99 @@
/**
* \file wasmtime/trap.h
*
* Wasmtime APIs for interacting with traps and extensions to #wasm_trap_t.
*/
#ifndef WASMTIME_TRAP_H
#define WASMTIME_TRAP_H
#include <wasm.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Code of an instruction trap.
*
* See #wasmtime_trap_code_enum for possible values.
*/
typedef uint8_t wasmtime_trap_code_t;
/**
* \brief Trap codes for instruction traps.
*/
enum wasmtime_trap_code_enum {
/// The current stack space was exhausted.
WASMTIME_TRAP_CODE_STACK_OVERFLOW,
/// An out-of-bounds memory access.
WASMTIME_TRAP_CODE_MEMORY_OUT_OF_BOUNDS,
/// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address.
WASMTIME_TRAP_CODE_HEAP_MISALIGNED,
/// An out-of-bounds access to a table.
WASMTIME_TRAP_CODE_TABLE_OUT_OF_BOUNDS,
/// Indirect call to a null table entry.
WASMTIME_TRAP_CODE_INDIRECT_CALL_TO_NULL,
/// Signature mismatch on indirect call.
WASMTIME_TRAP_CODE_BAD_SIGNATURE,
/// An integer arithmetic operation caused an overflow.
WASMTIME_TRAP_CODE_INTEGER_OVERFLOW,
/// An integer division by zero.
WASMTIME_TRAP_CODE_INTEGER_DIVISION_BY_ZERO,
/// Failed float-to-int conversion.
WASMTIME_TRAP_CODE_BAD_CONVERSION_TO_INTEGER,
/// Code that was supposed to have been unreachable was reached.
WASMTIME_TRAP_CODE_UNREACHABLE_CODE_REACHED,
/// Execution has potentially run too long and may be interrupted.
WASMTIME_TRAP_CODE_INTERRUPT,
/// Execution has run out of the configured fuel amount.
WASMTIME_TRAP_CODE_OUT_OF_FUEL,
};
/**
* \brief Creates a new trap.
*
* \param msg the message to associate with this trap
* \param msg_len the byte length of `msg`
*
* The #wasm_trap_t returned is owned by the caller.
*/
WASM_API_EXTERN wasm_trap_t *wasmtime_trap_new(const char *msg, size_t msg_len);
/**
* \brief Attempts to extract the trap code from this trap.
*
* Returns `true` if the trap is an instruction trap triggered while
* executing Wasm. If `true` is returned then the trap code is returned
* through the `code` pointer. If `false` is returned then this is not
* an instruction trap -- traps can also be created using wasm_trap_new,
* or occur with WASI modules exiting with a certain exit code.
*/
WASM_API_EXTERN bool wasmtime_trap_code(const wasm_trap_t*, wasmtime_trap_code_t *code);
/**
* \brief Returns a human-readable name for this frame's function.
*
* This function will attempt to load a human-readable name for function this
* frame points to. This function may return `NULL`.
*
* The lifetime of the returned name is the same as the #wasm_frame_t itself.
*/
WASM_API_EXTERN const wasm_name_t *wasmtime_frame_func_name(const wasm_frame_t*);
/**
* \brief Returns a human-readable name for this frame's module.
*
* This function will attempt to load a human-readable name for module this
* frame points to. This function may return `NULL`.
*
* The lifetime of the returned name is the same as the #wasm_frame_t itself.
*/
WASM_API_EXTERN const wasm_name_t *wasmtime_frame_module_name(const wasm_frame_t*);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_TRAP_H

232
lib/src/wasm/wasmtime/val.h Normal file
View file

@ -0,0 +1,232 @@
/**
* \file wasmtime/val.h
*
* APIs for interacting with WebAssembly values in Wasmtime.
*/
#ifndef WASMTIME_VAL_H
#define WASMTIME_VAL_H
#include <wasm.h>
#include <wasmtime/extern.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \typedef wasmtime_externref_t
* \brief Convenience alias for #wasmtime_externref
*
* \struct wasmtime_externref
* \brief A host-defined un-forgeable reference to pass into WebAssembly.
*
* This structure represents an `externref` that can be passed to WebAssembly.
* It cannot be forged by WebAssembly itself and is guaranteed to have been
* created by the host.
*/
typedef struct wasmtime_externref wasmtime_externref_t;
/**
* \brief Create a new `externref` value.
*
* Creates a new `externref` value wrapping the provided data, returning the
* pointer to the externref.
*
* \param data the host-specific data to wrap
* \param finalizer an optional finalizer for `data`
*
* When the reference is reclaimed, the wrapped data is cleaned up with the
* provided `finalizer`.
*
* The returned value must be deleted with #wasmtime_externref_delete
*/
WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_new(void *data, void (*finalizer)(void*));
/**
* \brief Get an `externref`'s wrapped data
*
* Returns the original `data` passed to #wasmtime_externref_new. It is required
* that `data` is not `NULL`.
*/
WASM_API_EXTERN void *wasmtime_externref_data(wasmtime_externref_t *data);
/**
* \brief Creates a shallow copy of the `externref` argument, returning a
* separately owned pointer (increases the reference count).
*/
WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_clone(wasmtime_externref_t *ref);
/**
* \brief Decrements the reference count of the `ref`, deleting it if it's the
* last reference.
*/
WASM_API_EXTERN void wasmtime_externref_delete(wasmtime_externref_t *ref);
/**
* \brief Converts a raw `externref` value coming from #wasmtime_val_raw_t into
* a #wasmtime_externref_t.
*
* Note that the returned #wasmtime_externref_t is an owned value that must be
* deleted via #wasmtime_externref_delete by the caller if it is non-null.
*/
WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_from_raw(wasmtime_context_t *context, void *raw);
/**
* \brief Converts a #wasmtime_externref_t to a raw value suitable for storing
* into a #wasmtime_val_raw_t.
*
* Note that the returned underlying value is not tracked by Wasmtime's garbage
* collector until it enters WebAssembly. This means that a GC may release the
* context's reference to the raw value, making the raw value invalid within the
* context of the store. Do not perform a GC between calling this function and
* passing it to WebAssembly.
*/
WASM_API_EXTERN void *wasmtime_externref_to_raw(
wasmtime_context_t *context,
const wasmtime_externref_t *ref);
/// \brief Discriminant stored in #wasmtime_val::kind
typedef uint8_t wasmtime_valkind_t;
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an i32
#define WASMTIME_I32 0
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an i64
#define WASMTIME_I64 1
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a f32
#define WASMTIME_F32 2
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a f64
#define WASMTIME_F64 3
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a v128
#define WASMTIME_V128 4
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a funcref
#define WASMTIME_FUNCREF 5
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an externref
#define WASMTIME_EXTERNREF 6
/// \brief A 128-bit value representing the WebAssembly `v128` type. Bytes are
/// stored in little-endian order.
typedef uint8_t wasmtime_v128[16];
/**
* \typedef wasmtime_valunion_t
* \brief Convenience alias for #wasmtime_valunion
*
* \union wasmtime_valunion
* \brief Container for different kinds of wasm values.
*
* This type is contained in #wasmtime_val_t and contains the payload for the
* various kinds of items a value can be.
*/
typedef union wasmtime_valunion {
/// Field used if #wasmtime_val_t::kind is #WASMTIME_I32
int32_t i32;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_I64
int64_t i64;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_F32
float32_t f32;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_F64
float64_t f64;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_FUNCREF
///
/// If this value represents a `ref.null func` value then the `store_id` field
/// is set to zero.
wasmtime_func_t funcref;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_EXTERNREF
///
/// If this value represents a `ref.null extern` value then this pointer will
/// be `NULL`.
wasmtime_externref_t *externref;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_V128
wasmtime_v128 v128;
} wasmtime_valunion_t;
/**
* \typedef wasmtime_val_raw_t
* \brief Convenience alias for #wasmtime_val_raw
*
* \union wasmtime_val_raw
* \brief Container for possible wasm values.
*
* This type is used on conjunction with #wasmtime_func_new_unchecked as well
* as #wasmtime_func_call_unchecked. Instances of this type do not have type
* information associated with them, it's up to the embedder to figure out
* how to interpret the bits contained within, often using some other channel
* to determine the type.
*/
typedef union wasmtime_val_raw {
/// Field for when this val is a WebAssembly `i32` value.
///
/// Note that this field is always stored in a little-endian format.
int32_t i32;
/// Field for when this val is a WebAssembly `i64` value.
///
/// Note that this field is always stored in a little-endian format.
int64_t i64;
/// Field for when this val is a WebAssembly `f32` value.
///
/// Note that this field is always stored in a little-endian format.
float32_t f32;
/// Field for when this val is a WebAssembly `f64` value.
///
/// Note that this field is always stored in a little-endian format.
float64_t f64;
/// Field for when this val is a WebAssembly `v128` value.
///
/// Note that this field is always stored in a little-endian format.
wasmtime_v128 v128;
/// Field for when this val is a WebAssembly `funcref` value.
///
/// If this is set to 0 then it's a null funcref, otherwise this must be
/// passed to `wasmtime_func_from_raw` to determine the `wasmtime_func_t`.
///
/// Note that this field is always stored in a little-endian format.
void *funcref;
/// Field for when this val is a WebAssembly `externref` value.
///
/// If this is set to 0 then it's a null externref, otherwise this must be
/// passed to `wasmtime_externref_from_raw` to determine the
/// `wasmtime_externref_t`.
///
/// Note that this field is always stored in a little-endian format.
void *externref;
} wasmtime_val_raw_t;
/**
* \typedef wasmtime_val_t
* \brief Convenience alias for #wasmtime_val_t
*
* \union wasmtime_val
* \brief Container for different kinds of wasm values.
*
* Note that this structure may contain an owned value, namely
* #wasmtime_externref_t, depending on the context in which this is used. APIs
* which consume a #wasmtime_val_t do not take ownership, but APIs that return
* #wasmtime_val_t require that #wasmtime_val_delete is called to deallocate
* the value.
*/
typedef struct wasmtime_val {
/// Discriminant of which field of #of is valid.
wasmtime_valkind_t kind;
/// Container for the extern item's value.
wasmtime_valunion_t of;
} wasmtime_val_t;
/**
* \brief Deletes an owned #wasmtime_val_t.
*
* Note that this only deletes the contents, not the memory that `val` points to
* itself (which is owned by the caller).
*/
WASM_API_EXTERN void wasmtime_val_delete(wasmtime_val_t *val);
/**
* \brief Copies `src` into `dst`.
*/
WASM_API_EXTERN void wasmtime_val_copy(wasmtime_val_t *dst, const wasmtime_val_t *src);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_VAL_H

View file

@ -33,7 +33,7 @@ web_dir=lib/binding_web
emscripten_flags="-O3"
minify_js=1
force_docker=0
emscripen_version=$(cat "$(dirname "$0")"/../cli/emscripten-version)
emscripen_version=$(cat "$(dirname "$0")"/../cli/loader/emscripten-version)
while [[ $# > 0 ]]; do
case "$1" in

17
script/build-wasm-stdlib Executable file
View file

@ -0,0 +1,17 @@
#!/bin/bash
set -e
emcc \
-o stdlib.wasm \
-Os \
--no-entry \
-s MAIN_MODULE=2 \
-s 'EXPORTED_FUNCTIONS=@lib/src/wasm/stdlib-symbols.json' \
-fvisibility=hidden \
-fno-exceptions \
-xc \
/dev/null
xxd -C -i stdlib.wasm > lib/src/wasm/wasm-stdlib.h
mv stdlib.wasm target/

View file

@ -2,7 +2,7 @@
set -e
EMSCRIPTEN_VERSION=$(cat "$(dirname "$0")/../cli/emscripten-version")
EMSCRIPTEN_VERSION=$(cat "$(dirname "$0")/../cli/loader/emscripten-version")
mkdir -p target
EMSDK_DIR="./target/emsdk"
@ -28,7 +28,5 @@ EMSDK_DIR="./target/emsdk"
) >&2
(
source "$EMSDK_DIR/emsdk_env.sh" > /dev/null
emcc --version >&2
echo export $(env | egrep '^(PATH|EM.*)')
echo "source \"$EMSDK_DIR/emsdk_env.sh\""
)

View file

@ -37,4 +37,7 @@ bindgen \
--blocklist-type '^__.*' \
--no-prepend-enum-name \
--no-copy "$no_copy" \
$header_path > $output_path
$header_path \
-- \
-D TREE_SITTER_FEATURE_WASM \
> $output_path

View file

@ -43,7 +43,7 @@ pub struct NamedCapture {
}
pub struct TagsContext {
parser: Parser,
pub parser: Parser,
cursor: QueryCursor,
}

View file

@ -1,4 +1,9 @@
#include "../external_and_internal_tokens/scanner.c"
#include <tree_sitter/parser.h>
enum {
STRING,
LINE_BREAK
};
void *tree_sitter_external_and_internal_anonymous_tokens_external_scanner_create() {
return NULL;
@ -28,9 +33,38 @@ bool tree_sitter_external_and_internal_anonymous_tokens_external_scanner_scan(
TSLexer *lexer,
const bool *whitelist
) {
return tree_sitter_external_and_internal_tokens_external_scanner_scan(
payload,
lexer,
whitelist
);
// If a line-break is a valid lookahead token, only skip spaces.
if (whitelist[LINE_BREAK]) {
while (lexer->lookahead == ' ' || lexer->lookahead == '\r') {
lexer->advance(lexer, true);
}
if (lexer->lookahead == '\n') {
lexer->advance(lexer, false);
lexer->result_symbol = LINE_BREAK;
return true;
}
}
// If a line-break is not a valid lookahead token, skip line breaks as well
// as spaces.
if (whitelist[STRING]) {
while (lexer->lookahead == ' ' || lexer->lookahead == '\r' || lexer->lookahead == '\n') {
lexer->advance(lexer, true);
}
if (lexer->lookahead == '\'') {
lexer->advance(lexer, false);
while (lexer->lookahead != '\'') {
lexer->advance(lexer, false);
}
lexer->advance(lexer, false);
lexer->result_symbol = STRING;
return true;
}
}
return false;
}