Initial reorg.
This is largely the same as #305, but updated for the current tree.
This commit is contained in:
44
crates/api/Cargo.toml
Normal file
44
crates/api/Cargo.toml
Normal file
@@ -0,0 +1,44 @@
|
||||
[package]
|
||||
name = "wasmtime-api"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
version = "0.1.0"
|
||||
description = "High-level API to expose the Wasmtime runtime"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
repository = "https://github.com/CraneStation/wasmtime"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "wasmtime_api"
|
||||
crate-type = ["lib", "staticlib", "cdylib"]
|
||||
|
||||
[dependencies]
|
||||
cranelift-codegen = { version = "0.49", features = ["enable-serde"] }
|
||||
cranelift-native = { version = "0.49" }
|
||||
cranelift-entity = { version = "0.49", features = ["enable-serde"] }
|
||||
cranelift-wasm = { version = "0.49", features = ["enable-serde"] }
|
||||
cranelift-frontend = { version = "0.49" }
|
||||
wasmtime-runtime = { path="../runtime" }
|
||||
wasmtime-environ = { path="../environ" }
|
||||
wasmtime-jit = { path="../jit" }
|
||||
wasmparser = { version = "0.39.2", default-features = false }
|
||||
target-lexicon = { version = "0.9.0", default-features = false }
|
||||
anyhow = "1.0.19"
|
||||
thiserror = "1.0.4"
|
||||
region = "2.0.0"
|
||||
hashbrown = { version = "0.6.0", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["cranelift-codegen/std", "cranelift-wasm/std", "wasmtime-environ/std", "wasmparser/std"]
|
||||
core = ["hashbrown/nightly", "cranelift-codegen/core", "cranelift-wasm/core", "wasmtime-environ/core", "wasmparser/core"]
|
||||
|
||||
[dev-dependencies]
|
||||
# for wasmtime.rs
|
||||
wasi-common = { path = "../wasi-common" }
|
||||
docopt = "1.0.1"
|
||||
serde = { "version" = "1.0.94", features = ["derive"] }
|
||||
pretty_env_logger = "0.3.0"
|
||||
wasmtime-wast = { path="../wast" }
|
||||
wasmtime-wasi = { path="../wasi" }
|
||||
rayon = "1.1"
|
||||
file-per-thread-logger = "0.1.1"
|
||||
220
crates/api/LICENSE
Normal file
220
crates/api/LICENSE
Normal file
@@ -0,0 +1,220 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
--- LLVM Exceptions to the Apache 2.0 License ----
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into an Object form of such source code, you
|
||||
may redistribute such embedded portions in such Object form without complying
|
||||
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||
|
||||
In addition, if you combine or link compiled forms of this Software with
|
||||
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||
court of competent jurisdiction determines that the patent provision (Section
|
||||
3), the indemnity provision (Section 9) or other Section of the License
|
||||
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||
the License, but only in their entirety and only with respect to the Combined
|
||||
Software.
|
||||
|
||||
3
crates/api/README.md
Normal file
3
crates/api/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Implementation of wasm-c-api in Rust
|
||||
|
||||
https://github.com/WebAssembly/wasm-c-api
|
||||
155
crates/api/c-examples/Makefile
Normal file
155
crates/api/c-examples/Makefile
Normal file
@@ -0,0 +1,155 @@
|
||||
###############################################################################
|
||||
# Configuration
|
||||
|
||||
# Inherited from wasm-c-api/Makefile to just run C examples
|
||||
|
||||
WASM_FLAGS = -DWASM_API_DEBUG # -DWASM_API_DEBUG_LOG
|
||||
C_FLAGS = ${WASM_FLAGS} -Wall -Werror -ggdb -O -fsanitize=address
|
||||
CC_FLAGS = -std=c++11 ${C_FLAGS}
|
||||
LD_FLAGS = -fsanitize-memory-track-origins -fsanitize-memory-use-after-dtor
|
||||
|
||||
C_COMP = clang
|
||||
|
||||
WASMTIME_API_MODE = debug
|
||||
|
||||
|
||||
# Base directories
|
||||
WASMTIME_API_DIR = ..
|
||||
WASM_DIR = wasm-c-api
|
||||
EXAMPLE_DIR = ${WASM_DIR}/example
|
||||
OUT_DIR = ${WASM_DIR}/out
|
||||
|
||||
# Example config
|
||||
EXAMPLE_OUT = ${OUT_DIR}/example
|
||||
EXAMPLES = \
|
||||
hello \
|
||||
callback \
|
||||
trap \
|
||||
start \
|
||||
reflect \
|
||||
global \
|
||||
table \
|
||||
memory \
|
||||
hostref \
|
||||
finalize \
|
||||
serialize \
|
||||
threads \
|
||||
# multi \
|
||||
|
||||
# Wasm config
|
||||
WASM_INCLUDE = ${WASM_DIR}/include
|
||||
WASM_SRC = ${WASM_DIR}/src
|
||||
WASM_OUT = ${OUT_DIR}
|
||||
WASM_C_LIBS = wasm-bin wasm-rust-api
|
||||
WASM_CC_LIBS = $(error unsupported C++)
|
||||
|
||||
|
||||
# Compiler config
|
||||
ifeq (${WASMTIME_API_MODE},release)
|
||||
CARGO_BUILD_FLAGS = --release
|
||||
else
|
||||
CARGO_BUILD_FLAGS =
|
||||
endif
|
||||
|
||||
ifeq (${C_COMP},clang)
|
||||
CC_COMP = clang++
|
||||
LD_GROUP_START =
|
||||
LD_GROUP_END =
|
||||
else ifeq (${C_COMP},gcc)
|
||||
CC_COMP = g++
|
||||
LD_GROUP_START = -Wl,--start-group
|
||||
LD_GROUP_END = -Wl,--end-group
|
||||
else
|
||||
$(error C_COMP set to unknown compiler, must be clang or gcc)
|
||||
endif
|
||||
|
||||
WASMTIME_BIN_DIR = ${WASMTIME_API_DIR}/../target/${WASMTIME_API_MODE}
|
||||
WASMTIME_C_LIB_DIR = ${WASMTIME_BIN_DIR}
|
||||
WASMTIME_C_LIBS = wasmtime_api
|
||||
WASMTIME_CC_LIBS = $(error unsupported c++)
|
||||
|
||||
WASMTIME_C_BINS = ${WASMTIME_C_LIBS:%=${WASMTIME_C_LIB_DIR}/lib%.a}
|
||||
|
||||
###############################################################################
|
||||
# Examples
|
||||
#
|
||||
# To build Wasm APIs and run all examples:
|
||||
# make all
|
||||
#
|
||||
# To run only C examples:
|
||||
# make c
|
||||
#
|
||||
# To run only C++ examples:
|
||||
# make cc
|
||||
#
|
||||
# To run individual C example (e.g. hello):
|
||||
# make run-hello-c
|
||||
#
|
||||
# To run individual C++ example (e.g. hello):
|
||||
# make run-hello-cc
|
||||
#
|
||||
|
||||
.PHONY: all cc c
|
||||
all: cc c
|
||||
cc: ${EXAMPLES:%=run-%-cc}
|
||||
c: ${EXAMPLES:%=run-%-c}
|
||||
|
||||
# Running a C / C++ example
|
||||
run-%-c: ${EXAMPLE_OUT}/%-c ${EXAMPLE_OUT}/%.wasm
|
||||
@echo ==== C ${@:run-%-c=%} ====; \
|
||||
cd ${EXAMPLE_OUT}; ./${@:run-%=%}
|
||||
@echo ==== Done ====
|
||||
|
||||
run-%-cc: ${EXAMPLE_OUT}/%-cc ${EXAMPLE_OUT}/%.wasm
|
||||
@echo ==== C++ ${@:run-%-cc=%} ====; \
|
||||
cd ${EXAMPLE_OUT}; ./${@:run-%=%}
|
||||
@echo ==== Done ====
|
||||
|
||||
# Compiling C / C++ example
|
||||
${EXAMPLE_OUT}/%-c.o: ${EXAMPLE_DIR}/%.c ${WASM_INCLUDE}/wasm.h
|
||||
mkdir -p ${EXAMPLE_OUT}
|
||||
${C_COMP} -c ${C_FLAGS} -I. -I${WASM_INCLUDE} $< -o $@
|
||||
|
||||
${EXAMPLE_OUT}/%-cc.o: ${EXAMPLE_DIR}/%.cc ${WASM_INCLUDE}/wasm.hh
|
||||
mkdir -p ${EXAMPLE_OUT}
|
||||
${CC_COMP} -c ${CC_FLAGS} -I. -I${WASM_INCLUDE} $< -o $@
|
||||
|
||||
# Linking C / C++ example
|
||||
.PRECIOUS: ${EXAMPLES:%=${EXAMPLE_OUT}/%-c}
|
||||
${EXAMPLE_OUT}/%-c: ${EXAMPLE_OUT}/%-c.o ${WASMTIME_C_BINS}
|
||||
${CC_COMP} ${CC_FLAGS} ${LD_FLAGS} $< -o $@ \
|
||||
${LD_GROUP_START} \
|
||||
${WASMTIME_C_BINS} \
|
||||
${LD_GROUP_END} \
|
||||
-ldl -pthread
|
||||
|
||||
# Installing Wasm binaries
|
||||
.PRECIOUS: ${EXAMPLES:%=${EXAMPLE_OUT}/%.wasm}
|
||||
${EXAMPLE_OUT}/%.wasm: ${EXAMPLE_DIR}/%.wasm
|
||||
cp $< $@
|
||||
|
||||
###############################################################################
|
||||
# Wasm C / C++ API
|
||||
#
|
||||
# To build both C / C++ APIs:
|
||||
# make wasm
|
||||
|
||||
.PHONY: wasm wasm-c wasm-cc
|
||||
wasm: wasm-c wasm-cc
|
||||
wasm-c: ${WASMTIME_C_BIN}
|
||||
wasm-cc: ${WASMTIME_CC_BIN}
|
||||
|
||||
${WASMTIME_C_BINS}: CARGO_RUN
|
||||
cd ${WASMTIME_API_DIR}; cargo build --lib ${CARGO_BUILD_FLAGS}
|
||||
|
||||
.PHONY: CARGO_RUN
|
||||
CARGO_RUN:
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Clean-up
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf ${OUT_DIR}
|
||||
|
||||
1
crates/api/c-examples/wasm-c-api
Submodule
1
crates/api/c-examples/wasm-c-api
Submodule
Submodule crates/api/c-examples/wasm-c-api added at 8782d5b456
43
crates/api/examples/gcd.rs
Normal file
43
crates/api/examples/gcd.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
//! Example of instantiating of the WebAssembly module and
|
||||
//! invoking its exported function.
|
||||
|
||||
use anyhow::{format_err, Result};
|
||||
use std::fs::read;
|
||||
use wasmtime_api::*;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let wasm = read("examples/gcd.wasm")?;
|
||||
|
||||
// Instantiate engine and store.
|
||||
let engine = HostRef::new(Engine::default());
|
||||
let store = HostRef::new(Store::new(&engine));
|
||||
|
||||
// Load a module.
|
||||
let module = HostRef::new(Module::new(&store, &wasm)?);
|
||||
|
||||
// Find index of the `gcd` export.
|
||||
let gcd_index = module
|
||||
.borrow()
|
||||
.exports()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, export)| export.name().to_string() == "gcd")
|
||||
.unwrap()
|
||||
.0;
|
||||
|
||||
// Instantiate the module.
|
||||
let instance = HostRef::new(Instance::new(&store, &module, &[])?);
|
||||
|
||||
// Invoke `gcd` export
|
||||
let gcd = instance.borrow().exports()[gcd_index]
|
||||
.func()
|
||||
.expect("gcd")
|
||||
.clone();
|
||||
let result = gcd
|
||||
.borrow()
|
||||
.call(&[Val::from(6i32), Val::from(27i32)])
|
||||
.map_err(|e| format_err!("call error: {:?}", e))?;
|
||||
|
||||
println!("{:?}", result);
|
||||
Ok(())
|
||||
}
|
||||
BIN
crates/api/examples/gcd.wasm
Normal file
BIN
crates/api/examples/gcd.wasm
Normal file
Binary file not shown.
68
crates/api/examples/hello.rs
Normal file
68
crates/api/examples/hello.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
//! Translation of hello example
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use anyhow::{ensure, format_err, Context as _, Result};
|
||||
use core::cell::Ref;
|
||||
use std::fs::read;
|
||||
use wasmtime_api::*;
|
||||
|
||||
struct HelloCallback;
|
||||
|
||||
impl Callable for HelloCallback {
|
||||
fn call(&self, _params: &[Val], _results: &mut [Val]) -> Result<(), HostRef<Trap>> {
|
||||
println!("Calling back...");
|
||||
println!("> Hello World!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Initialize.
|
||||
println!("Initializing...");
|
||||
let engine = HostRef::new(Engine::default());
|
||||
let store = HostRef::new(Store::new(&engine));
|
||||
|
||||
// Load binary.
|
||||
println!("Loading binary...");
|
||||
let binary = read("examples/hello.wasm")?;
|
||||
|
||||
// Compile.
|
||||
println!("Compiling module...");
|
||||
let module = HostRef::new(Module::new(&store, &binary).context("> Error compiling module!")?);
|
||||
|
||||
// Create external print functions.
|
||||
println!("Creating callback...");
|
||||
let hello_type = FuncType::new(Box::new([]), Box::new([]));
|
||||
let hello_func = HostRef::new(Func::new(&store, hello_type, Rc::new(HelloCallback)));
|
||||
|
||||
// Instantiate.
|
||||
println!("Instantiating module...");
|
||||
let imports = vec![hello_func.into()];
|
||||
let instance = HostRef::new(
|
||||
Instance::new(&store, &module, imports.as_slice())
|
||||
.context("> Error instantiating module!")?,
|
||||
);
|
||||
|
||||
// Extract export.
|
||||
println!("Extracting export...");
|
||||
let exports = Ref::map(instance.borrow(), |instance| instance.exports());
|
||||
ensure!(!exports.is_empty(), "> Error accessing exports!");
|
||||
let run_func = exports[0].func().context("> Error accessing exports!")?;
|
||||
|
||||
// Call.
|
||||
println!("Calling export...");
|
||||
run_func
|
||||
.borrow()
|
||||
.call(&[])
|
||||
.map_err(|e| format_err!("> Error calling function: {:?}", e))?;
|
||||
|
||||
// Shut down.
|
||||
println!("Shutting down...");
|
||||
drop(store);
|
||||
|
||||
// All done.
|
||||
println!("Done.");
|
||||
Ok(())
|
||||
}
|
||||
BIN
crates/api/examples/hello.wasm
Normal file
BIN
crates/api/examples/hello.wasm
Normal file
Binary file not shown.
4
crates/api/examples/hello.wat
Normal file
4
crates/api/examples/hello.wat
Normal file
@@ -0,0 +1,4 @@
|
||||
(module
|
||||
(func $hello (import "" "hello"))
|
||||
(func (export "run") (call $hello))
|
||||
)
|
||||
153
crates/api/examples/memory.rs
Normal file
153
crates/api/examples/memory.rs
Normal file
@@ -0,0 +1,153 @@
|
||||
//! Translation of the memory example
|
||||
|
||||
use anyhow::{bail, ensure, Context as _, Error};
|
||||
use core::cell::Ref;
|
||||
use std::fs::read;
|
||||
use wasmtime_api::*;
|
||||
|
||||
fn get_export_memory(exports: &[Extern], i: usize) -> Result<HostRef<Memory>, Error> {
|
||||
if exports.len() <= i {
|
||||
bail!("> Error accessing memory export {}!", i);
|
||||
}
|
||||
Ok(exports[i]
|
||||
.memory()
|
||||
.with_context(|| format!("> Error accessing memory export {}!", i))?
|
||||
.clone())
|
||||
}
|
||||
|
||||
fn get_export_func(exports: &[Extern], i: usize) -> Result<HostRef<Func>, Error> {
|
||||
if exports.len() <= i {
|
||||
bail!("> Error accessing function export {}!", i);
|
||||
}
|
||||
Ok(exports[i]
|
||||
.func()
|
||||
.with_context(|| format!("> Error accessing function export {}!", i))?
|
||||
.clone())
|
||||
}
|
||||
|
||||
macro_rules! check {
|
||||
($actual:expr, $expected:expr) => {
|
||||
if $actual != $expected {
|
||||
bail!("> Error on result, expected {}, got {}", $expected, $actual);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! check_ok {
|
||||
($func:expr, $($p:expr),*) => {
|
||||
if let Err(_) = $func.borrow().call(&[$($p.into()),*]) {
|
||||
bail!("> Error on result, expected return");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! check_trap {
|
||||
($func:expr, $($p:expr),*) => {
|
||||
if let Ok(_) = $func.borrow().call(&[$($p.into()),*]) {
|
||||
bail!("> Error on result, expected trap");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! call {
|
||||
($func:expr, $($p:expr),*) => {
|
||||
match $func.borrow().call(&[$($p.into()),*]) {
|
||||
Ok(result) => {
|
||||
let result: i32 = result[0].clone().into();
|
||||
result
|
||||
}
|
||||
Err(_) => { bail!("> Error on result, expected return"); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
// Initialize.
|
||||
println!("Initializing...");
|
||||
let engine = HostRef::new(Engine::default());
|
||||
let store = HostRef::new(Store::new(&engine));
|
||||
|
||||
// Load binary.
|
||||
println!("Loading binary...");
|
||||
let binary = read("examples/memory.wasm")?;
|
||||
|
||||
// Compile.
|
||||
println!("Compiling module...");
|
||||
let module = HostRef::new(Module::new(&store, &binary).context("> Error compiling module!")?);
|
||||
|
||||
// Instantiate.
|
||||
println!("Instantiating module...");
|
||||
let instance =
|
||||
HostRef::new(Instance::new(&store, &module, &[]).context("> Error instantiating module!")?);
|
||||
|
||||
// Extract export.
|
||||
println!("Extracting export...");
|
||||
let exports = Ref::map(instance.borrow(), |instance| instance.exports());
|
||||
ensure!(!exports.is_empty(), "> Error accessing exports!");
|
||||
let memory = get_export_memory(&exports, 0)?;
|
||||
let size_func = get_export_func(&exports, 1)?;
|
||||
let load_func = get_export_func(&exports, 2)?;
|
||||
let store_func = get_export_func(&exports, 3)?;
|
||||
|
||||
// Try cloning.
|
||||
check!(memory.clone().ptr_eq(&memory), true);
|
||||
|
||||
// Check initial memory.
|
||||
println!("Checking memory...");
|
||||
check!(memory.borrow().size(), 2u32);
|
||||
check!(memory.borrow().data_size(), 0x20000usize);
|
||||
check!(unsafe { memory.borrow().data()[0] }, 0);
|
||||
check!(unsafe { memory.borrow().data()[0x1000] }, 1);
|
||||
check!(unsafe { memory.borrow().data()[0x1003] }, 4);
|
||||
|
||||
check!(call!(size_func,), 2);
|
||||
check!(call!(load_func, 0), 0);
|
||||
check!(call!(load_func, 0x1000), 1);
|
||||
check!(call!(load_func, 0x1003), 4);
|
||||
check!(call!(load_func, 0x1ffff), 0);
|
||||
check_trap!(load_func, 0x20000);
|
||||
|
||||
// Mutate memory.
|
||||
println!("Mutating memory...");
|
||||
unsafe {
|
||||
memory.borrow_mut().data()[0x1003] = 5;
|
||||
}
|
||||
|
||||
check_ok!(store_func, 0x1002, 6);
|
||||
check_trap!(store_func, 0x20000, 0);
|
||||
|
||||
check!(unsafe { memory.borrow().data()[0x1002] }, 6);
|
||||
check!(unsafe { memory.borrow().data()[0x1003] }, 5);
|
||||
check!(call!(load_func, 0x1002), 6);
|
||||
check!(call!(load_func, 0x1003), 5);
|
||||
|
||||
// Grow memory.
|
||||
println!("Growing memory...");
|
||||
check!(memory.borrow_mut().grow(1), true);
|
||||
check!(memory.borrow().size(), 3u32);
|
||||
check!(memory.borrow().data_size(), 0x30000usize);
|
||||
|
||||
check!(call!(load_func, 0x20000), 0);
|
||||
check_ok!(store_func, 0x20000, 0);
|
||||
check_trap!(load_func, 0x30000);
|
||||
check_trap!(store_func, 0x30000, 0);
|
||||
|
||||
check!(memory.borrow_mut().grow(1), false);
|
||||
check!(memory.borrow_mut().grow(0), true);
|
||||
|
||||
// Create stand-alone memory.
|
||||
// TODO(wasm+): Once Wasm allows multiple memories, turn this into import.
|
||||
println!("Creating stand-alone memory...");
|
||||
let memorytype = MemoryType::new(Limits::new(5, 5));
|
||||
let mut memory2 = Memory::new(&store, memorytype);
|
||||
check!(memory2.size(), 5u32);
|
||||
check!(memory2.grow(1), false);
|
||||
check!(memory2.grow(0), true);
|
||||
|
||||
// Shut down.
|
||||
println!("Shutting down...");
|
||||
drop(store);
|
||||
|
||||
println!("Done.");
|
||||
Ok(())
|
||||
}
|
||||
BIN
crates/api/examples/memory.wasm
Normal file
BIN
crates/api/examples/memory.wasm
Normal file
Binary file not shown.
11
crates/api/examples/memory.wat
Normal file
11
crates/api/examples/memory.wat
Normal file
@@ -0,0 +1,11 @@
|
||||
(module
|
||||
(memory (export "memory") 2 3)
|
||||
|
||||
(func (export "size") (result i32) (memory.size))
|
||||
(func (export "load") (param i32) (result i32) (i32.load8_s (local.get 0)))
|
||||
(func (export "store") (param i32 i32)
|
||||
(i32.store8 (local.get 0) (local.get 1))
|
||||
)
|
||||
|
||||
(data (i32.const 0x1000) "\01\02\03\04")
|
||||
)
|
||||
81
crates/api/examples/multi.rs
Normal file
81
crates/api/examples/multi.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
//! Translation of multi example
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use anyhow::{ensure, format_err, Context as _, Result};
|
||||
use core::cell::Ref;
|
||||
use std::fs::read;
|
||||
use wasmtime_api::*;
|
||||
|
||||
struct Callback;
|
||||
|
||||
impl Callable for Callback {
|
||||
fn call(&self, args: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>> {
|
||||
println!("Calling back...");
|
||||
println!("> {} {}", args[0].i32(), args[1].i64());
|
||||
|
||||
results[0] = Val::I64(args[1].i64() + 1);
|
||||
results[1] = Val::I32(args[0].i32() + 1);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Initialize.
|
||||
println!("Initializing...");
|
||||
let engine = HostRef::new(Engine::default());
|
||||
let store = HostRef::new(Store::new(&engine));
|
||||
|
||||
// Load binary.
|
||||
println!("Loading binary...");
|
||||
let binary = read("examples/multi.wasm")?;
|
||||
|
||||
// Compile.
|
||||
println!("Compiling module...");
|
||||
let module = HostRef::new(Module::new(&store, &binary).context("Error compiling module!")?);
|
||||
|
||||
// Create external print functions.
|
||||
println!("Creating callback...");
|
||||
let callback_type = FuncType::new(
|
||||
Box::new([ValType::I32, ValType::I64]),
|
||||
Box::new([ValType::I64, ValType::I32]),
|
||||
);
|
||||
let callback_func = HostRef::new(Func::new(&store, callback_type, Rc::new(Callback)));
|
||||
|
||||
// Instantiate.
|
||||
println!("Instantiating module...");
|
||||
let imports = vec![callback_func.into()];
|
||||
let instance = HostRef::new(
|
||||
Instance::new(&store, &module, imports.as_slice())
|
||||
.context("Error instantiating module!")?,
|
||||
);
|
||||
|
||||
// Extract export.
|
||||
println!("Extracting export...");
|
||||
let exports = Ref::map(instance.borrow(), |instance| instance.exports());
|
||||
ensure!(!exports.is_empty(), "Error accessing exports!");
|
||||
let run_func = exports[0].func().context("Error accessing exports!")?;
|
||||
|
||||
// Call.
|
||||
println!("Calling export...");
|
||||
let args = vec![Val::I32(1), Val::I64(3)];
|
||||
let results = run_func
|
||||
.borrow()
|
||||
.call(&args)
|
||||
.map_err(|e| format_err!("> Error calling function: {:?}", e))?;
|
||||
|
||||
println!("Printing result...");
|
||||
println!("> {} {}", results[0].i64(), results[1].i32());
|
||||
|
||||
debug_assert!(results[0].i64() == 4);
|
||||
debug_assert!(results[1].i32() == 2);
|
||||
|
||||
// Shut down.
|
||||
println!("Shutting down...");
|
||||
drop(store);
|
||||
|
||||
// All done.
|
||||
println!("Done.");
|
||||
Ok(())
|
||||
}
|
||||
BIN
crates/api/examples/multi.wasm
Normal file
BIN
crates/api/examples/multi.wasm
Normal file
Binary file not shown.
7
crates/api/examples/multi.wat
Normal file
7
crates/api/examples/multi.wat
Normal file
@@ -0,0 +1,7 @@
|
||||
(module
|
||||
(func $f (import "" "f") (param i32 i64) (result i64 i32))
|
||||
|
||||
(func $g (export "g") (param i32 i64) (result i64 i32)
|
||||
(call $f (local.get 0) (local.get 1))
|
||||
)
|
||||
)
|
||||
155
crates/api/src/callable.rs
Normal file
155
crates/api/src/callable.rs
Normal file
@@ -0,0 +1,155 @@
|
||||
use crate::r#ref::HostRef;
|
||||
use crate::runtime::Store;
|
||||
use crate::trap::Trap;
|
||||
use crate::types::FuncType;
|
||||
use crate::values::Val;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::trampoline::generate_func_export;
|
||||
use cranelift_codegen::ir;
|
||||
use wasmtime_jit::InstanceHandle;
|
||||
use wasmtime_runtime::Export;
|
||||
|
||||
pub trait Callable {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>>;
|
||||
}
|
||||
|
||||
pub(crate) trait WrappedCallable {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>>;
|
||||
fn signature(&self) -> &ir::Signature {
|
||||
match self.wasmtime_export() {
|
||||
Export::Function { signature, .. } => signature,
|
||||
_ => panic!("unexpected export type in Callable"),
|
||||
}
|
||||
}
|
||||
fn wasmtime_handle(&self) -> &InstanceHandle;
|
||||
fn wasmtime_export(&self) -> &Export;
|
||||
}
|
||||
|
||||
pub(crate) struct WasmtimeFn {
|
||||
store: HostRef<Store>,
|
||||
instance: InstanceHandle,
|
||||
export: Export,
|
||||
}
|
||||
|
||||
impl WasmtimeFn {
|
||||
pub fn new(store: &HostRef<Store>, instance: InstanceHandle, export: Export) -> WasmtimeFn {
|
||||
WasmtimeFn {
|
||||
store: store.clone(),
|
||||
instance,
|
||||
export,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WrappedCallable for WasmtimeFn {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>> {
|
||||
use core::cmp::max;
|
||||
use core::{mem, ptr};
|
||||
|
||||
let (vmctx, body, signature) = match self.wasmtime_export() {
|
||||
Export::Function {
|
||||
vmctx,
|
||||
address,
|
||||
signature,
|
||||
} => (*vmctx, *address, signature.clone()),
|
||||
_ => panic!("unexpected export type in Callable"),
|
||||
};
|
||||
|
||||
let value_size = mem::size_of::<u64>();
|
||||
let mut values_vec: Vec<u64> = vec![0; max(params.len(), results.len())];
|
||||
|
||||
// Store the argument values into `values_vec`.
|
||||
for (index, arg) in params.iter().enumerate() {
|
||||
unsafe {
|
||||
let ptr = values_vec.as_mut_ptr().add(index);
|
||||
|
||||
match arg {
|
||||
Val::I32(x) => ptr::write(ptr as *mut i32, *x),
|
||||
Val::I64(x) => ptr::write(ptr as *mut i64, *x),
|
||||
Val::F32(x) => ptr::write(ptr as *mut u32, *x),
|
||||
Val::F64(x) => ptr::write(ptr as *mut u64, *x),
|
||||
_ => unimplemented!("WasmtimeFn arg"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the trampoline to call for this function.
|
||||
let exec_code_buf = self
|
||||
.store
|
||||
.borrow_mut()
|
||||
.context()
|
||||
.compiler()
|
||||
.get_published_trampoline(body, &signature, value_size)
|
||||
.map_err(|_| HostRef::new(Trap::fake()))?; //was ActionError::Setup)?;
|
||||
|
||||
// Call the trampoline.
|
||||
if let Err(message) = unsafe {
|
||||
wasmtime_runtime::wasmtime_call_trampoline(
|
||||
vmctx,
|
||||
exec_code_buf,
|
||||
values_vec.as_mut_ptr() as *mut u8,
|
||||
)
|
||||
} {
|
||||
return Err(HostRef::new(Trap::new(message)));
|
||||
}
|
||||
|
||||
// Load the return values out of `values_vec`.
|
||||
for (index, abi_param) in signature.returns.iter().enumerate() {
|
||||
unsafe {
|
||||
let ptr = values_vec.as_ptr().add(index);
|
||||
|
||||
results[index] = match abi_param.value_type {
|
||||
ir::types::I32 => Val::I32(ptr::read(ptr as *const i32)),
|
||||
ir::types::I64 => Val::I64(ptr::read(ptr as *const i64)),
|
||||
ir::types::F32 => Val::F32(ptr::read(ptr as *const u32)),
|
||||
ir::types::F64 => Val::F64(ptr::read(ptr as *const u64)),
|
||||
other => panic!("unsupported value type {:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn wasmtime_handle(&self) -> &InstanceHandle {
|
||||
&self.instance
|
||||
}
|
||||
fn wasmtime_export(&self) -> &Export {
|
||||
&self.export
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NativeCallable {
|
||||
callable: Rc<dyn Callable + 'static>,
|
||||
instance: InstanceHandle,
|
||||
export: Export,
|
||||
}
|
||||
|
||||
impl NativeCallable {
|
||||
pub(crate) fn new(
|
||||
callable: Rc<dyn Callable + 'static>,
|
||||
ft: &FuncType,
|
||||
store: &HostRef<Store>,
|
||||
) -> Self {
|
||||
let (instance, export) =
|
||||
generate_func_export(ft, &callable, store).expect("generated func");
|
||||
NativeCallable {
|
||||
callable,
|
||||
instance,
|
||||
export,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WrappedCallable for NativeCallable {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>> {
|
||||
self.callable.call(params, results)
|
||||
}
|
||||
fn wasmtime_handle(&self) -> &InstanceHandle {
|
||||
&self.instance
|
||||
}
|
||||
fn wasmtime_export(&self) -> &Export {
|
||||
&self.export
|
||||
}
|
||||
}
|
||||
68
crates/api/src/context.rs
Normal file
68
crates/api/src/context.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::{RefCell, RefMut};
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
use wasmtime_jit::{CompilationStrategy, Compiler, Features};
|
||||
|
||||
use cranelift_codegen::settings;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Context {
|
||||
compiler: Rc<RefCell<Compiler>>,
|
||||
features: Features,
|
||||
debug_info: bool,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(compiler: Compiler, features: Features, debug_info: bool) -> Context {
|
||||
Context {
|
||||
compiler: Rc::new(RefCell::new(compiler)),
|
||||
features,
|
||||
debug_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(
|
||||
flags: settings::Flags,
|
||||
features: Features,
|
||||
debug_info: bool,
|
||||
strategy: CompilationStrategy,
|
||||
) -> Context {
|
||||
Context::new(create_compiler(flags, strategy), features, debug_info)
|
||||
}
|
||||
|
||||
pub(crate) fn debug_info(&self) -> bool {
|
||||
self.debug_info
|
||||
}
|
||||
|
||||
pub(crate) fn compiler(&mut self) -> RefMut<Compiler> {
|
||||
self.compiler.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Context {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
self.compiler.as_ptr().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Context {}
|
||||
|
||||
impl PartialEq for Context {
|
||||
fn eq(&self, other: &Context) -> bool {
|
||||
Rc::ptr_eq(&self.compiler, &other.compiler)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_compiler(flags: settings::Flags, strategy: CompilationStrategy) -> Compiler {
|
||||
let isa = {
|
||||
let isa_builder =
|
||||
cranelift_native::builder().expect("host machine is not a supported target");
|
||||
isa_builder.finish(flags)
|
||||
};
|
||||
|
||||
Compiler::new(isa, strategy)
|
||||
}
|
||||
490
crates/api/src/externals.rs
Normal file
490
crates/api/src/externals.rs
Normal file
@@ -0,0 +1,490 @@
|
||||
use crate::callable::{Callable, NativeCallable, WasmtimeFn, WrappedCallable};
|
||||
use crate::r#ref::{AnyRef, HostRef};
|
||||
use crate::runtime::Store;
|
||||
use crate::trampoline::{generate_global_export, generate_memory_export, generate_table_export};
|
||||
use crate::trap::Trap;
|
||||
use crate::types::{ExternType, FuncType, GlobalType, MemoryType, TableType, ValType};
|
||||
use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use core::result::Result;
|
||||
use core::slice;
|
||||
use wasmtime_runtime::InstanceHandle;
|
||||
|
||||
// Externals
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Extern {
|
||||
Func(HostRef<Func>),
|
||||
Global(HostRef<Global>),
|
||||
Table(HostRef<Table>),
|
||||
Memory(HostRef<Memory>),
|
||||
}
|
||||
|
||||
impl Extern {
|
||||
pub fn func(&self) -> Option<&HostRef<Func>> {
|
||||
match self {
|
||||
Extern::Func(func) => Some(func),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn global(&self) -> Option<&HostRef<Global>> {
|
||||
match self {
|
||||
Extern::Global(global) => Some(global),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn table(&self) -> Option<&HostRef<Table>> {
|
||||
match self {
|
||||
Extern::Table(table) => Some(table),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn memory(&self) -> Option<&HostRef<Memory>> {
|
||||
match self {
|
||||
Extern::Memory(memory) => Some(memory),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> ExternType {
|
||||
match self {
|
||||
Extern::Func(ft) => ExternType::ExternFunc(ft.borrow().r#type().clone()),
|
||||
Extern::Memory(ft) => ExternType::ExternMemory(ft.borrow().r#type().clone()),
|
||||
Extern::Table(tt) => ExternType::ExternTable(tt.borrow().r#type().clone()),
|
||||
Extern::Global(gt) => ExternType::ExternGlobal(gt.borrow().r#type().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_wasmtime_export(&mut self) -> wasmtime_runtime::Export {
|
||||
match self {
|
||||
Extern::Func(f) => f.borrow().wasmtime_export().clone(),
|
||||
Extern::Global(g) => g.borrow().wasmtime_export().clone(),
|
||||
Extern::Memory(m) => m.borrow().wasmtime_export().clone(),
|
||||
Extern::Table(t) => t.borrow().wasmtime_export().clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_export(
|
||||
store: &HostRef<Store>,
|
||||
instance_handle: InstanceHandle,
|
||||
export: wasmtime_runtime::Export,
|
||||
) -> Extern {
|
||||
match export {
|
||||
wasmtime_runtime::Export::Function { .. } => Extern::Func(HostRef::new(
|
||||
Func::from_wasmtime_function(export, store, instance_handle),
|
||||
)),
|
||||
wasmtime_runtime::Export::Memory { .. } => Extern::Memory(HostRef::new(
|
||||
Memory::from_wasmtime_memory(export, store, instance_handle),
|
||||
)),
|
||||
wasmtime_runtime::Export::Global { .. } => {
|
||||
Extern::Global(HostRef::new(Global::from_wasmtime_global(export, store)))
|
||||
}
|
||||
wasmtime_runtime::Export::Table { .. } => Extern::Table(HostRef::new(
|
||||
Table::from_wasmtime_table(export, store, instance_handle),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Func>> for Extern {
|
||||
fn from(r: HostRef<Func>) -> Self {
|
||||
Extern::Func(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Global>> for Extern {
|
||||
fn from(r: HostRef<Global>) -> Self {
|
||||
Extern::Global(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Memory>> for Extern {
|
||||
fn from(r: HostRef<Memory>) -> Self {
|
||||
Extern::Memory(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Table>> for Extern {
|
||||
fn from(r: HostRef<Table>) -> Self {
|
||||
Extern::Table(r)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Func {
|
||||
_store: HostRef<Store>,
|
||||
callable: Rc<dyn WrappedCallable + 'static>,
|
||||
r#type: FuncType,
|
||||
}
|
||||
|
||||
impl Func {
|
||||
pub fn new(store: &HostRef<Store>, ty: FuncType, callable: Rc<dyn Callable + 'static>) -> Self {
|
||||
let callable = Rc::new(NativeCallable::new(callable, &ty, &store));
|
||||
Func::from_wrapped(store, ty, callable)
|
||||
}
|
||||
|
||||
fn from_wrapped(
|
||||
store: &HostRef<Store>,
|
||||
r#type: FuncType,
|
||||
callable: Rc<dyn WrappedCallable + 'static>,
|
||||
) -> Func {
|
||||
Func {
|
||||
_store: store.clone(),
|
||||
callable,
|
||||
r#type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &FuncType {
|
||||
&self.r#type
|
||||
}
|
||||
|
||||
pub fn param_arity(&self) -> usize {
|
||||
self.r#type.params().len()
|
||||
}
|
||||
|
||||
pub fn result_arity(&self) -> usize {
|
||||
self.r#type.results().len()
|
||||
}
|
||||
|
||||
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, HostRef<Trap>> {
|
||||
let mut results = vec![Val::default(); self.result_arity()];
|
||||
self.callable.call(params, &mut results)?;
|
||||
Ok(results.into_boxed_slice())
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
|
||||
self.callable.wasmtime_export()
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_function(
|
||||
export: wasmtime_runtime::Export,
|
||||
store: &HostRef<Store>,
|
||||
instance_handle: InstanceHandle,
|
||||
) -> Self {
|
||||
let ty = if let wasmtime_runtime::Export::Function { signature, .. } = &export {
|
||||
FuncType::from_cranelift_signature(signature.clone())
|
||||
} else {
|
||||
panic!("expected function export")
|
||||
};
|
||||
let callable = WasmtimeFn::new(store, instance_handle, export.clone());
|
||||
Func::from_wrapped(store, ty, Rc::new(callable))
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for Func {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "Func")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Global {
|
||||
_store: HostRef<Store>,
|
||||
r#type: GlobalType,
|
||||
wasmtime_export: wasmtime_runtime::Export,
|
||||
#[allow(dead_code)]
|
||||
wasmtime_state: Option<crate::trampoline::GlobalState>,
|
||||
}
|
||||
|
||||
impl Global {
|
||||
pub fn new(store: &HostRef<Store>, r#type: GlobalType, val: Val) -> Global {
|
||||
let (wasmtime_export, wasmtime_state) =
|
||||
generate_global_export(&r#type, val).expect("generated global");
|
||||
Global {
|
||||
_store: store.clone(),
|
||||
r#type,
|
||||
wasmtime_export,
|
||||
wasmtime_state: Some(wasmtime_state),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &GlobalType {
|
||||
&self.r#type
|
||||
}
|
||||
|
||||
fn wasmtime_global_definition(&self) -> *mut wasmtime_runtime::VMGlobalDefinition {
|
||||
match self.wasmtime_export {
|
||||
wasmtime_runtime::Export::Global { definition, .. } => definition,
|
||||
_ => panic!("global definition not found"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Val {
|
||||
let definition = unsafe { &mut *self.wasmtime_global_definition() };
|
||||
unsafe {
|
||||
match self.r#type().content() {
|
||||
ValType::I32 => Val::from(*definition.as_i32()),
|
||||
ValType::I64 => Val::from(*definition.as_i64()),
|
||||
ValType::F32 => Val::from_f32_bits(*definition.as_u32()),
|
||||
ValType::F64 => Val::from_f64_bits(*definition.as_u64()),
|
||||
_ => unimplemented!("Global::get for {:?}", self.r#type().content()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, val: Val) {
|
||||
if val.r#type() != *self.r#type().content() {
|
||||
panic!(
|
||||
"global of type {:?} cannot be set to {:?}",
|
||||
self.r#type().content(),
|
||||
val.r#type()
|
||||
);
|
||||
}
|
||||
let definition = unsafe { &mut *self.wasmtime_global_definition() };
|
||||
unsafe {
|
||||
match val {
|
||||
Val::I32(i) => *definition.as_i32_mut() = i,
|
||||
Val::I64(i) => *definition.as_i64_mut() = i,
|
||||
Val::F32(f) => *definition.as_u32_mut() = f,
|
||||
Val::F64(f) => *definition.as_u64_mut() = f,
|
||||
_ => unimplemented!("Global::set for {:?}", val.r#type()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
|
||||
&self.wasmtime_export
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_global(
|
||||
export: wasmtime_runtime::Export,
|
||||
store: &HostRef<Store>,
|
||||
) -> Global {
|
||||
let global = if let wasmtime_runtime::Export::Global { ref global, .. } = export {
|
||||
global
|
||||
} else {
|
||||
panic!("wasmtime export is not memory")
|
||||
};
|
||||
let ty = GlobalType::from_cranelift_global(global.clone());
|
||||
Global {
|
||||
_store: store.clone(),
|
||||
r#type: ty,
|
||||
wasmtime_export: export,
|
||||
wasmtime_state: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Table {
|
||||
store: HostRef<Store>,
|
||||
r#type: TableType,
|
||||
wasmtime_handle: InstanceHandle,
|
||||
wasmtime_export: wasmtime_runtime::Export,
|
||||
}
|
||||
|
||||
fn get_table_item(
|
||||
handle: &InstanceHandle,
|
||||
store: &HostRef<Store>,
|
||||
table_index: cranelift_wasm::DefinedTableIndex,
|
||||
item_index: u32,
|
||||
) -> Val {
|
||||
if let Some(item) = handle.table_get(table_index, item_index) {
|
||||
from_checked_anyfunc(item, store)
|
||||
} else {
|
||||
AnyRef::null().into()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_table_item(
|
||||
handle: &mut InstanceHandle,
|
||||
store: &HostRef<Store>,
|
||||
table_index: cranelift_wasm::DefinedTableIndex,
|
||||
item_index: u32,
|
||||
val: Val,
|
||||
) -> bool {
|
||||
let item = into_checked_anyfunc(val, store);
|
||||
if let Some(item_ref) = handle.table_get_mut(table_index, item_index) {
|
||||
*item_ref = item;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Table {
|
||||
pub fn new(store: &HostRef<Store>, r#type: TableType, init: Val) -> Table {
|
||||
match r#type.element() {
|
||||
ValType::FuncRef => (),
|
||||
_ => panic!("table is not for funcref"),
|
||||
}
|
||||
let (mut wasmtime_handle, wasmtime_export) =
|
||||
generate_table_export(&r#type).expect("generated table");
|
||||
|
||||
// Initialize entries with the init value.
|
||||
match wasmtime_export {
|
||||
wasmtime_runtime::Export::Table { definition, .. } => {
|
||||
let index = wasmtime_handle.table_index(unsafe { &*definition });
|
||||
let len = unsafe { (*definition).current_elements };
|
||||
for i in 0..len {
|
||||
let _success =
|
||||
set_table_item(&mut wasmtime_handle, store, index, i, init.clone());
|
||||
assert!(_success);
|
||||
}
|
||||
}
|
||||
_ => panic!("global definition not found"),
|
||||
}
|
||||
|
||||
Table {
|
||||
store: store.clone(),
|
||||
r#type,
|
||||
wasmtime_handle,
|
||||
wasmtime_export,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &TableType {
|
||||
&self.r#type
|
||||
}
|
||||
|
||||
fn wasmtime_table_index(&self) -> cranelift_wasm::DefinedTableIndex {
|
||||
match self.wasmtime_export {
|
||||
wasmtime_runtime::Export::Table { definition, .. } => {
|
||||
self.wasmtime_handle.table_index(unsafe { &*definition })
|
||||
}
|
||||
_ => panic!("global definition not found"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, index: u32) -> Val {
|
||||
let table_index = self.wasmtime_table_index();
|
||||
get_table_item(&self.wasmtime_handle, &self.store, table_index, index)
|
||||
}
|
||||
|
||||
pub fn set(&self, index: u32, val: Val) -> bool {
|
||||
let table_index = self.wasmtime_table_index();
|
||||
let mut wasmtime_handle = self.wasmtime_handle.clone();
|
||||
set_table_item(&mut wasmtime_handle, &self.store, table_index, index, val)
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u32 {
|
||||
match self.wasmtime_export {
|
||||
wasmtime_runtime::Export::Table { definition, .. } => unsafe {
|
||||
(*definition).current_elements
|
||||
},
|
||||
_ => panic!("global definition not found"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grow(&mut self, delta: u32, init: Val) -> bool {
|
||||
let index = self.wasmtime_table_index();
|
||||
if let Some(len) = self.wasmtime_handle.table_grow(index, delta) {
|
||||
let mut wasmtime_handle = self.wasmtime_handle.clone();
|
||||
for i in 0..delta {
|
||||
let i = len - (delta - i);
|
||||
let _success =
|
||||
set_table_item(&mut wasmtime_handle, &self.store, index, i, init.clone());
|
||||
assert!(_success);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
|
||||
&self.wasmtime_export
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_table(
|
||||
export: wasmtime_runtime::Export,
|
||||
store: &HostRef<Store>,
|
||||
instance_handle: wasmtime_runtime::InstanceHandle,
|
||||
) -> Table {
|
||||
let table = if let wasmtime_runtime::Export::Table { ref table, .. } = export {
|
||||
table
|
||||
} else {
|
||||
panic!("wasmtime export is not table")
|
||||
};
|
||||
let ty = TableType::from_cranelift_table(table.table.clone());
|
||||
Table {
|
||||
store: store.clone(),
|
||||
r#type: ty,
|
||||
wasmtime_handle: instance_handle,
|
||||
wasmtime_export: export,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Memory {
|
||||
_store: HostRef<Store>,
|
||||
r#type: MemoryType,
|
||||
wasmtime_handle: InstanceHandle,
|
||||
wasmtime_export: wasmtime_runtime::Export,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub fn new(store: &HostRef<Store>, r#type: MemoryType) -> Memory {
|
||||
let (wasmtime_handle, wasmtime_export) =
|
||||
generate_memory_export(&r#type).expect("generated memory");
|
||||
Memory {
|
||||
_store: store.clone(),
|
||||
r#type,
|
||||
wasmtime_handle,
|
||||
wasmtime_export,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &MemoryType {
|
||||
&self.r#type
|
||||
}
|
||||
|
||||
fn wasmtime_memory_definition(&self) -> *mut wasmtime_runtime::VMMemoryDefinition {
|
||||
match self.wasmtime_export {
|
||||
wasmtime_runtime::Export::Memory { definition, .. } => definition,
|
||||
_ => panic!("memory definition not found"),
|
||||
}
|
||||
}
|
||||
|
||||
// Marked unsafe due to posibility that wasmtime can resize internal memory
|
||||
// from other threads.
|
||||
pub unsafe fn data(&self) -> &mut [u8] {
|
||||
let definition = &*self.wasmtime_memory_definition();
|
||||
slice::from_raw_parts_mut(definition.base, definition.current_length)
|
||||
}
|
||||
|
||||
pub fn data_ptr(&self) -> *mut u8 {
|
||||
unsafe { (*self.wasmtime_memory_definition()).base }
|
||||
}
|
||||
|
||||
pub fn data_size(&self) -> usize {
|
||||
unsafe { (*self.wasmtime_memory_definition()).current_length }
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u32 {
|
||||
(self.data_size() / wasmtime_environ::WASM_PAGE_SIZE as usize) as u32
|
||||
}
|
||||
|
||||
pub fn grow(&mut self, delta: u32) -> bool {
|
||||
match self.wasmtime_export {
|
||||
wasmtime_runtime::Export::Memory { definition, .. } => {
|
||||
let definition = unsafe { &(*definition) };
|
||||
let index = self.wasmtime_handle.memory_index(definition);
|
||||
self.wasmtime_handle.memory_grow(index, delta).is_some()
|
||||
}
|
||||
_ => panic!("memory definition not found"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
|
||||
&self.wasmtime_export
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_memory(
|
||||
export: wasmtime_runtime::Export,
|
||||
store: &HostRef<Store>,
|
||||
instance_handle: wasmtime_runtime::InstanceHandle,
|
||||
) -> Memory {
|
||||
let memory = if let wasmtime_runtime::Export::Memory { ref memory, .. } = export {
|
||||
memory
|
||||
} else {
|
||||
panic!("wasmtime export is not memory")
|
||||
};
|
||||
let ty = MemoryType::from_cranelift_memory(memory.memory.clone());
|
||||
Memory {
|
||||
_store: store.clone(),
|
||||
r#type: ty,
|
||||
wasmtime_handle: instance_handle,
|
||||
wasmtime_export: export,
|
||||
}
|
||||
}
|
||||
}
|
||||
148
crates/api/src/instance.rs
Normal file
148
crates/api/src/instance.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
use crate::context::Context;
|
||||
use crate::externals::Extern;
|
||||
use crate::module::Module;
|
||||
use crate::r#ref::HostRef;
|
||||
use crate::runtime::Store;
|
||||
use crate::{HashMap, HashSet};
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
use anyhow::Result;
|
||||
use core::cell::RefCell;
|
||||
|
||||
use wasmtime_jit::{instantiate, Resolver};
|
||||
use wasmtime_runtime::{Export, InstanceHandle};
|
||||
|
||||
struct SimpleResolver {
|
||||
imports: Vec<(String, String, Extern)>,
|
||||
}
|
||||
|
||||
impl Resolver for SimpleResolver {
|
||||
fn resolve(&mut self, name: &str, field: &str) -> Option<Export> {
|
||||
// TODO speedup lookup
|
||||
self.imports
|
||||
.iter_mut()
|
||||
.find(|(n, f, _)| name == n && field == f)
|
||||
.map(|(_, _, e)| e.get_wasmtime_export())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instantiate_in_context(
|
||||
data: &[u8],
|
||||
imports: Vec<(String, String, Extern)>,
|
||||
mut context: Context,
|
||||
exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
|
||||
) -> Result<(InstanceHandle, HashSet<Context>)> {
|
||||
let mut contexts = HashSet::new();
|
||||
let debug_info = context.debug_info();
|
||||
let mut resolver = SimpleResolver { imports };
|
||||
let instance = instantiate(
|
||||
&mut context.compiler(),
|
||||
data,
|
||||
&mut resolver,
|
||||
exports,
|
||||
debug_info,
|
||||
)?;
|
||||
contexts.insert(context);
|
||||
Ok((instance, contexts))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Instance {
|
||||
instance_handle: InstanceHandle,
|
||||
|
||||
// We need to keep CodeMemory alive.
|
||||
contexts: HashSet<Context>,
|
||||
|
||||
exports: Box<[Extern]>,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
pub fn new(
|
||||
store: &HostRef<Store>,
|
||||
module: &HostRef<Module>,
|
||||
externs: &[Extern],
|
||||
) -> Result<Instance> {
|
||||
let context = store.borrow_mut().context().clone();
|
||||
let exports = store.borrow_mut().global_exports().clone();
|
||||
let imports = module
|
||||
.borrow()
|
||||
.imports()
|
||||
.iter()
|
||||
.zip(externs.iter())
|
||||
.map(|(i, e)| (i.module().to_string(), i.name().to_string(), e.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
let (mut instance_handle, contexts) =
|
||||
instantiate_in_context(module.borrow().binary(), imports, context, exports)?;
|
||||
|
||||
let exports = {
|
||||
let module = module.borrow();
|
||||
let mut exports = Vec::with_capacity(module.exports().len());
|
||||
for export in module.exports() {
|
||||
let name = export.name().to_string();
|
||||
let export = instance_handle.lookup(&name).expect("export");
|
||||
exports.push(Extern::from_wasmtime_export(
|
||||
store,
|
||||
instance_handle.clone(),
|
||||
export,
|
||||
));
|
||||
}
|
||||
exports.into_boxed_slice()
|
||||
};
|
||||
Ok(Instance {
|
||||
instance_handle,
|
||||
contexts,
|
||||
exports,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn exports(&self) -> &[Extern] {
|
||||
&self.exports
|
||||
}
|
||||
|
||||
pub fn from_handle(
|
||||
store: &HostRef<Store>,
|
||||
instance_handle: InstanceHandle,
|
||||
) -> Result<(Instance, HashMap<String, usize>)> {
|
||||
let contexts = HashSet::new();
|
||||
|
||||
let mut exports = Vec::new();
|
||||
let mut export_names_map = HashMap::new();
|
||||
let mut mutable = instance_handle.clone();
|
||||
for (name, _) in instance_handle.clone().exports() {
|
||||
let export = mutable.lookup(name).expect("export");
|
||||
if let wasmtime_runtime::Export::Function { signature, .. } = &export {
|
||||
// HACK ensure all handles, instantiated outside Store, present in
|
||||
// the store's SignatureRegistry, e.g. WASI instances that are
|
||||
// imported into this store using the from_handle() method.
|
||||
let _ = store.borrow_mut().register_cranelift_signature(signature);
|
||||
}
|
||||
export_names_map.insert(name.to_owned(), exports.len());
|
||||
exports.push(Extern::from_wasmtime_export(
|
||||
store,
|
||||
instance_handle.clone(),
|
||||
export.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok((
|
||||
Instance {
|
||||
instance_handle,
|
||||
contexts,
|
||||
exports: exports.into_boxed_slice(),
|
||||
},
|
||||
export_names_map,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn handle(&self) -> &InstanceHandle {
|
||||
&self.instance_handle
|
||||
}
|
||||
|
||||
pub fn get_wasmtime_memory(&self) -> Option<wasmtime_runtime::Export> {
|
||||
let mut instance_handle = self.instance_handle.clone();
|
||||
instance_handle.lookup("memory")
|
||||
}
|
||||
}
|
||||
35
crates/api/src/lib.rs
Normal file
35
crates/api/src/lib.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
//! Wasmtime embed API. Based on wasm-c-api.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
mod callable;
|
||||
mod context;
|
||||
mod externals;
|
||||
mod instance;
|
||||
mod module;
|
||||
mod r#ref;
|
||||
mod runtime;
|
||||
mod trampoline;
|
||||
mod trap;
|
||||
mod types;
|
||||
mod values;
|
||||
|
||||
pub mod wasm;
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
pub use crate::callable::Callable;
|
||||
pub use crate::externals::*;
|
||||
pub use crate::instance::Instance;
|
||||
pub use crate::module::Module;
|
||||
pub use crate::r#ref::{AnyRef, HostInfo, HostRef};
|
||||
pub use crate::runtime::{Config, Engine, Store};
|
||||
pub use crate::trap::Trap;
|
||||
pub use crate::types::*;
|
||||
pub use crate::values::*;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashbrown::{hash_map, HashMap, HashSet};
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::{hash_map, HashMap, HashSet};
|
||||
206
crates/api/src/module.rs
Normal file
206
crates/api/src/module.rs
Normal file
@@ -0,0 +1,206 @@
|
||||
use crate::r#ref::HostRef;
|
||||
use crate::runtime::Store;
|
||||
use crate::types::{
|
||||
ExportType, ExternType, FuncType, GlobalType, ImportType, Limits, MemoryType, Mutability,
|
||||
TableType, ValType,
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use anyhow::Result;
|
||||
|
||||
use wasmparser::{validate, ExternalKind, ImportSectionEntryType, ModuleReader, SectionCode};
|
||||
|
||||
fn into_memory_type(mt: wasmparser::MemoryType) -> MemoryType {
|
||||
assert!(!mt.shared);
|
||||
MemoryType::new(Limits::new(
|
||||
mt.limits.initial,
|
||||
mt.limits.maximum.unwrap_or(::core::u32::MAX),
|
||||
))
|
||||
}
|
||||
|
||||
fn into_global_type(gt: &wasmparser::GlobalType) -> GlobalType {
|
||||
let mutability = if gt.mutable {
|
||||
Mutability::Var
|
||||
} else {
|
||||
Mutability::Const
|
||||
};
|
||||
GlobalType::new(into_valtype(>.content_type), mutability)
|
||||
}
|
||||
|
||||
fn into_valtype(ty: &wasmparser::Type) -> ValType {
|
||||
use wasmparser::Type::*;
|
||||
match ty {
|
||||
I32 => ValType::I32,
|
||||
I64 => ValType::I64,
|
||||
F32 => ValType::F32,
|
||||
F64 => ValType::F64,
|
||||
V128 => ValType::V128,
|
||||
AnyFunc => ValType::FuncRef,
|
||||
AnyRef => ValType::AnyRef,
|
||||
_ => unimplemented!("types in into_valtype"),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_func_type(mt: wasmparser::FuncType) -> FuncType {
|
||||
assert!(mt.form == wasmparser::Type::Func);
|
||||
let params = mt.params.iter().map(into_valtype).collect::<Vec<_>>();
|
||||
let returns = mt.returns.iter().map(into_valtype).collect::<Vec<_>>();
|
||||
FuncType::new(params.into_boxed_slice(), returns.into_boxed_slice())
|
||||
}
|
||||
|
||||
fn into_table_type(tt: wasmparser::TableType) -> TableType {
|
||||
assert!(
|
||||
tt.element_type == wasmparser::Type::AnyFunc || tt.element_type == wasmparser::Type::AnyRef
|
||||
);
|
||||
let ty = into_valtype(&tt.element_type);
|
||||
let limits = Limits::new(
|
||||
tt.limits.initial,
|
||||
tt.limits.maximum.unwrap_or(::core::u32::MAX),
|
||||
);
|
||||
TableType::new(ty, limits)
|
||||
}
|
||||
|
||||
fn read_imports_and_exports(binary: &[u8]) -> Result<(Box<[ImportType]>, Box<[ExportType]>)> {
|
||||
let mut reader = ModuleReader::new(binary)?;
|
||||
let mut imports = Vec::new();
|
||||
let mut exports = Vec::new();
|
||||
let mut memories = Vec::new();
|
||||
let mut tables = Vec::new();
|
||||
let mut func_sig = Vec::new();
|
||||
let mut sigs = Vec::new();
|
||||
let mut globals = Vec::new();
|
||||
while !reader.eof() {
|
||||
let section = reader.read()?;
|
||||
match section.code {
|
||||
SectionCode::Memory => {
|
||||
let section = section.get_memory_section_reader()?;
|
||||
memories.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
memories.push(into_memory_type(entry?));
|
||||
}
|
||||
}
|
||||
SectionCode::Type => {
|
||||
let section = section.get_type_section_reader()?;
|
||||
sigs.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
sigs.push(into_func_type(entry?));
|
||||
}
|
||||
}
|
||||
SectionCode::Function => {
|
||||
let section = section.get_function_section_reader()?;
|
||||
func_sig.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
func_sig.push(entry?);
|
||||
}
|
||||
}
|
||||
SectionCode::Global => {
|
||||
let section = section.get_global_section_reader()?;
|
||||
globals.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
globals.push(into_global_type(&entry?.ty));
|
||||
}
|
||||
}
|
||||
SectionCode::Table => {
|
||||
let section = section.get_table_section_reader()?;
|
||||
tables.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
tables.push(into_table_type(entry?))
|
||||
}
|
||||
}
|
||||
SectionCode::Import => {
|
||||
let section = section.get_import_section_reader()?;
|
||||
imports.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
let entry = entry?;
|
||||
let module = String::from(entry.module).into();
|
||||
let name = String::from(entry.field).into();
|
||||
let r#type = match entry.ty {
|
||||
ImportSectionEntryType::Function(index) => {
|
||||
func_sig.push(index);
|
||||
let sig = &sigs[index as usize];
|
||||
ExternType::ExternFunc(sig.clone())
|
||||
}
|
||||
ImportSectionEntryType::Table(tt) => {
|
||||
let table = into_table_type(tt);
|
||||
tables.push(table.clone());
|
||||
ExternType::ExternTable(table)
|
||||
}
|
||||
ImportSectionEntryType::Memory(mt) => {
|
||||
let memory = into_memory_type(mt);
|
||||
memories.push(memory.clone());
|
||||
ExternType::ExternMemory(memory)
|
||||
}
|
||||
ImportSectionEntryType::Global(gt) => {
|
||||
let global = into_global_type(>);
|
||||
globals.push(global.clone());
|
||||
ExternType::ExternGlobal(global)
|
||||
}
|
||||
};
|
||||
imports.push(ImportType::new(module, name, r#type));
|
||||
}
|
||||
}
|
||||
SectionCode::Export => {
|
||||
let section = section.get_export_section_reader()?;
|
||||
exports.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
let entry = entry?;
|
||||
let name = String::from(entry.field).into();
|
||||
let r#type = match entry.kind {
|
||||
ExternalKind::Function => {
|
||||
let sig_index = func_sig[entry.index as usize] as usize;
|
||||
let sig = &sigs[sig_index];
|
||||
ExternType::ExternFunc(sig.clone())
|
||||
}
|
||||
ExternalKind::Table => {
|
||||
ExternType::ExternTable(tables[entry.index as usize].clone())
|
||||
}
|
||||
ExternalKind::Memory => {
|
||||
ExternType::ExternMemory(memories[entry.index as usize].clone())
|
||||
}
|
||||
ExternalKind::Global => {
|
||||
ExternType::ExternGlobal(globals[entry.index as usize].clone())
|
||||
}
|
||||
};
|
||||
exports.push(ExportType::new(name, r#type));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// skip other sections
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((imports.into_boxed_slice(), exports.into_boxed_slice()))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Module {
|
||||
store: HostRef<Store>,
|
||||
binary: Box<[u8]>,
|
||||
imports: Box<[ImportType]>,
|
||||
exports: Box<[ExportType]>,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn new(store: &HostRef<Store>, binary: &[u8]) -> Result<Module> {
|
||||
let (imports, exports) = read_imports_and_exports(binary)?;
|
||||
Ok(Module {
|
||||
store: store.clone(),
|
||||
binary: binary.into(),
|
||||
imports,
|
||||
exports,
|
||||
})
|
||||
}
|
||||
pub(crate) fn binary(&self) -> &[u8] {
|
||||
&self.binary
|
||||
}
|
||||
pub fn validate(_store: &Store, binary: &[u8]) -> bool {
|
||||
validate(binary, None).is_ok()
|
||||
}
|
||||
pub fn imports(&self) -> &[ImportType] {
|
||||
&self.imports
|
||||
}
|
||||
pub fn exports(&self) -> &[ExportType] {
|
||||
&self.exports
|
||||
}
|
||||
}
|
||||
211
crates/api/src/ref.rs
Normal file
211
crates/api/src/ref.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::{Rc, Weak};
|
||||
use core::any::Any;
|
||||
use core::cell::{self, RefCell};
|
||||
use core::fmt;
|
||||
|
||||
pub trait HostInfo {
|
||||
fn finalize(&mut self) {}
|
||||
}
|
||||
|
||||
trait InternalRefBase: Any {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn host_info(&self) -> Option<cell::RefMut<Box<dyn HostInfo>>>;
|
||||
fn set_host_info(&self, info: Option<Box<dyn HostInfo>>);
|
||||
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InternalRef(Rc<dyn InternalRefBase>);
|
||||
|
||||
impl InternalRef {
|
||||
pub fn is_ref<T: 'static>(&self) -> bool {
|
||||
let r = self.0.as_any();
|
||||
Any::is::<HostRef<T>>(r)
|
||||
}
|
||||
pub fn get_ref<T: 'static>(&self) -> HostRef<T> {
|
||||
let r = self.0.as_any();
|
||||
r.downcast_ref::<HostRef<T>>()
|
||||
.expect("reference is not T type")
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
struct AnyAndHostInfo {
|
||||
any: Box<dyn Any>,
|
||||
host_info: Option<Box<dyn HostInfo>>,
|
||||
}
|
||||
|
||||
impl Drop for AnyAndHostInfo {
|
||||
fn drop(&mut self) {
|
||||
if let Some(info) = &mut self.host_info {
|
||||
info.finalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AnyRef {
|
||||
Null,
|
||||
Ref(InternalRef),
|
||||
Other(OtherRef),
|
||||
}
|
||||
|
||||
impl AnyRef {
|
||||
pub fn new(data: Box<dyn Any>) -> Self {
|
||||
let info = AnyAndHostInfo {
|
||||
any: data,
|
||||
host_info: None,
|
||||
};
|
||||
AnyRef::Other(OtherRef(Rc::new(RefCell::new(info))))
|
||||
}
|
||||
|
||||
pub fn null() -> Self {
|
||||
AnyRef::Null
|
||||
}
|
||||
|
||||
pub fn data(&self) -> cell::Ref<Box<dyn Any>> {
|
||||
match self {
|
||||
AnyRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),
|
||||
_ => panic!("expected AnyRef::Other"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ptr_eq(&self, other: &AnyRef) -> bool {
|
||||
match (self, other) {
|
||||
(AnyRef::Null, AnyRef::Null) => true,
|
||||
(AnyRef::Ref(InternalRef(ref a)), AnyRef::Ref(InternalRef(ref b))) => {
|
||||
a.ptr_eq(b.as_ref())
|
||||
}
|
||||
(AnyRef::Other(OtherRef(ref a)), AnyRef::Other(OtherRef(ref b))) => Rc::ptr_eq(a, b),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn host_info(&self) -> Option<cell::RefMut<Box<dyn HostInfo>>> {
|
||||
match self {
|
||||
AnyRef::Null => panic!("null"),
|
||||
AnyRef::Ref(r) => r.0.host_info(),
|
||||
AnyRef::Other(r) => {
|
||||
let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info);
|
||||
if info.is_none() {
|
||||
return None;
|
||||
}
|
||||
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_host_info(&self, info: Option<Box<dyn HostInfo>>) {
|
||||
match self {
|
||||
AnyRef::Null => panic!("null"),
|
||||
AnyRef::Ref(r) => r.0.set_host_info(info),
|
||||
AnyRef::Other(r) => {
|
||||
r.0.borrow_mut().host_info = info;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AnyRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
AnyRef::Null => write!(f, "null"),
|
||||
AnyRef::Ref(_) => write!(f, "anyref"),
|
||||
AnyRef::Other(_) => write!(f, "other ref"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentBox<T> {
|
||||
content: T,
|
||||
host_info: Option<Box<dyn HostInfo>>,
|
||||
anyref_data: Weak<dyn InternalRefBase>,
|
||||
}
|
||||
|
||||
impl<T> Drop for ContentBox<T> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(info) = &mut self.host_info {
|
||||
info.finalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HostRef<T>(Rc<RefCell<ContentBox<T>>>);
|
||||
|
||||
impl<T: 'static> HostRef<T> {
|
||||
pub fn new(item: T) -> HostRef<T> {
|
||||
let anyref_data: Weak<HostRef<T>> = Weak::new();
|
||||
let content = ContentBox {
|
||||
content: item,
|
||||
host_info: None,
|
||||
anyref_data,
|
||||
};
|
||||
HostRef(Rc::new(RefCell::new(content)))
|
||||
}
|
||||
|
||||
pub fn borrow(&self) -> cell::Ref<T> {
|
||||
cell::Ref::map(self.0.borrow(), |b| &b.content)
|
||||
}
|
||||
|
||||
pub fn borrow_mut(&self) -> cell::RefMut<T> {
|
||||
cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.content)
|
||||
}
|
||||
|
||||
pub fn ptr_eq(&self, other: &HostRef<T>) -> bool {
|
||||
Rc::ptr_eq(&self.0, &other.0)
|
||||
}
|
||||
|
||||
pub fn anyref(&self) -> AnyRef {
|
||||
let r = self.0.borrow_mut().anyref_data.upgrade();
|
||||
if let Some(r) = r {
|
||||
return AnyRef::Ref(InternalRef(r));
|
||||
}
|
||||
let anyref_data: Rc<dyn InternalRefBase> = Rc::new(self.clone());
|
||||
self.0.borrow_mut().anyref_data = Rc::downgrade(&anyref_data);
|
||||
AnyRef::Ref(InternalRef(anyref_data))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> InternalRefBase for HostRef<T> {
|
||||
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref() {
|
||||
self.ptr_eq(other)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn host_info(&self) -> Option<cell::RefMut<Box<dyn HostInfo>>> {
|
||||
let info = cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.host_info);
|
||||
if info.is_none() {
|
||||
return None;
|
||||
}
|
||||
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
|
||||
}
|
||||
|
||||
fn set_host_info(&self, info: Option<Box<dyn HostInfo>>) {
|
||||
self.0.borrow_mut().host_info = info;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for HostRef<T> {
|
||||
fn clone(&self) -> HostRef<T> {
|
||||
HostRef(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for HostRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Ref(")?;
|
||||
self.0.borrow().content.fmt(f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
154
crates/api/src/runtime.rs
Normal file
154
crates/api/src/runtime.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
use crate::HashMap;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::string::String;
|
||||
use core::cell::RefCell;
|
||||
|
||||
use crate::context::{create_compiler, Context};
|
||||
use crate::r#ref::HostRef;
|
||||
|
||||
use cranelift_codegen::{ir, settings};
|
||||
use wasmtime_jit::{CompilationStrategy, Features};
|
||||
|
||||
// Runtime Environment
|
||||
|
||||
// Configuration
|
||||
|
||||
fn default_flags() -> settings::Flags {
|
||||
let flag_builder = settings::builder();
|
||||
settings::Flags::new(flag_builder)
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
flags: settings::Flags,
|
||||
features: Features,
|
||||
debug_info: bool,
|
||||
strategy: CompilationStrategy,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn default() -> Config {
|
||||
Config {
|
||||
debug_info: false,
|
||||
features: Default::default(),
|
||||
flags: default_flags(),
|
||||
strategy: CompilationStrategy::Auto,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
flags: settings::Flags,
|
||||
features: Features,
|
||||
debug_info: bool,
|
||||
strategy: CompilationStrategy,
|
||||
) -> Config {
|
||||
Config {
|
||||
flags,
|
||||
features,
|
||||
debug_info,
|
||||
strategy,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn debug_info(&self) -> bool {
|
||||
self.debug_info
|
||||
}
|
||||
|
||||
pub(crate) fn flags(&self) -> &settings::Flags {
|
||||
&self.flags
|
||||
}
|
||||
|
||||
pub(crate) fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
pub(crate) fn strategy(&self) -> CompilationStrategy {
|
||||
self.strategy
|
||||
}
|
||||
}
|
||||
|
||||
// Engine
|
||||
|
||||
pub struct Engine {
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
pub fn new(config: Config) -> Engine {
|
||||
Engine { config }
|
||||
}
|
||||
|
||||
pub fn default() -> Engine {
|
||||
Engine::new(Config::default())
|
||||
}
|
||||
|
||||
pub(crate) fn config(&self) -> &Config {
|
||||
&self.config
|
||||
}
|
||||
|
||||
pub fn create_wasmtime_context(&self) -> wasmtime_jit::Context {
|
||||
let flags = self.config.flags().clone();
|
||||
wasmtime_jit::Context::new(Box::new(create_compiler(flags, self.config.strategy())))
|
||||
}
|
||||
}
|
||||
|
||||
// Store
|
||||
|
||||
pub struct Store {
|
||||
engine: HostRef<Engine>,
|
||||
context: Context,
|
||||
global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
|
||||
signature_cache: HashMap<wasmtime_runtime::VMSharedSignatureIndex, ir::Signature>,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
pub fn new(engine: &HostRef<Engine>) -> Store {
|
||||
let flags = engine.borrow().config().flags().clone();
|
||||
let features = engine.borrow().config().features().clone();
|
||||
let debug_info = engine.borrow().config().debug_info();
|
||||
let strategy = engine.borrow().config().strategy();
|
||||
Store {
|
||||
engine: engine.clone(),
|
||||
context: Context::create(flags, features, debug_info, strategy),
|
||||
global_exports: Rc::new(RefCell::new(HashMap::new())),
|
||||
signature_cache: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn engine(&self) -> &HostRef<Engine> {
|
||||
&self.engine
|
||||
}
|
||||
|
||||
pub(crate) fn context(&mut self) -> &mut Context {
|
||||
&mut self.context
|
||||
}
|
||||
|
||||
// Specific to wasmtime: hack to pass memory around to wasi
|
||||
pub fn global_exports(
|
||||
&self,
|
||||
) -> &Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>> {
|
||||
&self.global_exports
|
||||
}
|
||||
|
||||
pub(crate) fn register_cranelift_signature(
|
||||
&mut self,
|
||||
signature: &ir::Signature,
|
||||
) -> wasmtime_runtime::VMSharedSignatureIndex {
|
||||
use crate::hash_map::Entry;
|
||||
let index = self.context().compiler().signatures().register(signature);
|
||||
match self.signature_cache.entry(index) {
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(signature.clone());
|
||||
}
|
||||
Entry::Occupied(_) => (),
|
||||
}
|
||||
index
|
||||
}
|
||||
|
||||
pub(crate) fn lookup_cranelift_signature(
|
||||
&self,
|
||||
type_index: wasmtime_runtime::VMSharedSignatureIndex,
|
||||
) -> Option<&ir::Signature> {
|
||||
self.signature_cache.get(&type_index)
|
||||
}
|
||||
}
|
||||
62
crates/api/src/trampoline/create_handle.rs
Normal file
62
crates/api/src/trampoline/create_handle.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
//! Support for a calling of an imported function.
|
||||
|
||||
use anyhow::Result;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::DefinedFuncIndex;
|
||||
//use target_lexicon::HOST;
|
||||
use wasmtime_environ::Module;
|
||||
use wasmtime_runtime::{Imports, InstanceHandle, VMFunctionBody};
|
||||
|
||||
use crate::{HashMap, HashSet};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::any::Any;
|
||||
use core::cell::{RefCell, RefMut};
|
||||
|
||||
use crate::runtime::Store;
|
||||
|
||||
pub(crate) fn create_handle(
|
||||
module: Module,
|
||||
signature_registry: Option<RefMut<Store>>,
|
||||
finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody>,
|
||||
state: Box<dyn Any>,
|
||||
) -> Result<InstanceHandle> {
|
||||
let global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>> =
|
||||
Rc::new(RefCell::new(HashMap::new()));
|
||||
|
||||
let imports = Imports::new(
|
||||
HashSet::new(),
|
||||
PrimaryMap::new(),
|
||||
PrimaryMap::new(),
|
||||
PrimaryMap::new(),
|
||||
PrimaryMap::new(),
|
||||
);
|
||||
let data_initializers = Vec::new();
|
||||
|
||||
// Compute indices into the shared signature table.
|
||||
let signatures = signature_registry
|
||||
.and_then(|mut signature_registry| {
|
||||
Some(
|
||||
module
|
||||
.signatures
|
||||
.values()
|
||||
.map(|sig| signature_registry.register_cranelift_signature(sig))
|
||||
.collect::<PrimaryMap<_, _>>(),
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| PrimaryMap::new());
|
||||
|
||||
Ok(InstanceHandle::new(
|
||||
Rc::new(module),
|
||||
global_exports,
|
||||
finished_functions.into_boxed_slice(),
|
||||
imports,
|
||||
&data_initializers,
|
||||
signatures.into_boxed_slice(),
|
||||
None,
|
||||
state,
|
||||
)
|
||||
.expect("instance"))
|
||||
}
|
||||
291
crates/api/src/trampoline/func.rs
Normal file
291
crates/api/src/trampoline/func.rs
Normal file
@@ -0,0 +1,291 @@
|
||||
//! Support for a calling of an imported function.
|
||||
|
||||
use crate::r#ref::HostRef;
|
||||
use anyhow::Result;
|
||||
use cranelift_codegen::ir::types;
|
||||
use cranelift_codegen::ir::{InstBuilder, StackSlotData, StackSlotKind, TrapCode};
|
||||
use cranelift_codegen::Context;
|
||||
use cranelift_codegen::{binemit, ir, isa};
|
||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex};
|
||||
use wasmtime_environ::{CompiledFunction, Export, Module};
|
||||
use wasmtime_jit::CodeMemory;
|
||||
use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp;
|
||||
|
||||
use crate::{Callable, FuncType, Store, Trap, Val};
|
||||
|
||||
use super::create_handle::create_handle;
|
||||
|
||||
struct TrampolineState {
|
||||
func: Rc<dyn Callable + 'static>,
|
||||
trap: Option<HostRef<Trap>>,
|
||||
#[allow(dead_code)]
|
||||
code_memory: CodeMemory,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, call_id: u32, values_vec: *mut i64) -> u32 {
|
||||
let mut instance = InstanceHandle::from_vmctx(vmctx);
|
||||
|
||||
let (args, returns_len) = {
|
||||
let module = instance.module_ref();
|
||||
let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]];
|
||||
|
||||
let mut args = Vec::new();
|
||||
for i in 1..signature.params.len() {
|
||||
args.push(Val::read_value_from(
|
||||
values_vec.offset(i as isize - 1),
|
||||
signature.params[i].value_type,
|
||||
))
|
||||
}
|
||||
(args, signature.returns.len())
|
||||
};
|
||||
|
||||
let mut returns = vec![Val::default(); returns_len];
|
||||
let func = &instance
|
||||
.host_state()
|
||||
.downcast_mut::<TrampolineState>()
|
||||
.expect("state")
|
||||
.func;
|
||||
|
||||
match func.call(&args, &mut returns) {
|
||||
Ok(()) => {
|
||||
for i in 0..returns_len {
|
||||
// TODO check signature.returns[i].value_type ?
|
||||
returns[i].write_value_to(values_vec.offset(i as isize));
|
||||
}
|
||||
0
|
||||
}
|
||||
Err(trap) => {
|
||||
// TODO read custom exception
|
||||
InstanceHandle::from_vmctx(vmctx)
|
||||
.host_state()
|
||||
.downcast_mut::<TrampolineState>()
|
||||
.expect("state")
|
||||
.trap = Some(trap);
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a trampoline for invoking a Callable.
|
||||
fn make_trampoline(
|
||||
isa: &dyn isa::TargetIsa,
|
||||
code_memory: &mut CodeMemory,
|
||||
fn_builder_ctx: &mut FunctionBuilderContext,
|
||||
call_id: u32,
|
||||
signature: &ir::Signature,
|
||||
) -> *const VMFunctionBody {
|
||||
// Mostly reverse copy of the similar method from wasmtime's
|
||||
// wasmtime-jit/src/compiler.rs.
|
||||
let pointer_type = isa.pointer_type();
|
||||
let mut stub_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
|
||||
|
||||
// Add the `vmctx` parameter.
|
||||
stub_sig.params.push(ir::AbiParam::special(
|
||||
pointer_type,
|
||||
ir::ArgumentPurpose::VMContext,
|
||||
));
|
||||
|
||||
// Add the `call_id` parameter.
|
||||
stub_sig.params.push(ir::AbiParam::new(types::I32));
|
||||
|
||||
// Add the `values_vec` parameter.
|
||||
stub_sig.params.push(ir::AbiParam::new(pointer_type));
|
||||
|
||||
// Add error/trap return.
|
||||
stub_sig.returns.push(ir::AbiParam::new(types::I32));
|
||||
|
||||
let values_vec_len = 8 * cmp::max(signature.params.len() - 1, signature.returns.len()) as u32;
|
||||
|
||||
let mut context = Context::new();
|
||||
context.func =
|
||||
ir::Function::with_name_signature(ir::ExternalName::user(0, 0), signature.clone());
|
||||
|
||||
let ss = context.func.create_stack_slot(StackSlotData::new(
|
||||
StackSlotKind::ExplicitSlot,
|
||||
values_vec_len,
|
||||
));
|
||||
let value_size = 8;
|
||||
|
||||
{
|
||||
let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
|
||||
let block0 = builder.create_ebb();
|
||||
|
||||
builder.append_ebb_params_for_function_params(block0);
|
||||
builder.switch_to_block(block0);
|
||||
builder.seal_block(block0);
|
||||
|
||||
let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0);
|
||||
let mflags = ir::MemFlags::trusted();
|
||||
for i in 1..signature.params.len() {
|
||||
if i == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let val = builder.func.dfg.ebb_params(block0)[i];
|
||||
builder.ins().store(
|
||||
mflags,
|
||||
val,
|
||||
values_vec_ptr_val,
|
||||
((i - 1) * value_size) as i32,
|
||||
);
|
||||
}
|
||||
|
||||
let vmctx_ptr_val = builder.func.dfg.ebb_params(block0)[0];
|
||||
let call_id_val = builder.ins().iconst(types::I32, call_id as i64);
|
||||
|
||||
let callee_args = vec![vmctx_ptr_val, call_id_val, values_vec_ptr_val];
|
||||
|
||||
let new_sig = builder.import_signature(stub_sig.clone());
|
||||
|
||||
let callee_value = builder
|
||||
.ins()
|
||||
.iconst(pointer_type, stub_fn as *const VMFunctionBody as i64);
|
||||
let call = builder
|
||||
.ins()
|
||||
.call_indirect(new_sig, callee_value, &callee_args);
|
||||
|
||||
let call_result = builder.func.dfg.inst_results(call)[0];
|
||||
builder.ins().trapnz(call_result, TrapCode::User(0));
|
||||
|
||||
let mflags = ir::MemFlags::trusted();
|
||||
let mut results = Vec::new();
|
||||
for (i, r) in signature.returns.iter().enumerate() {
|
||||
let load = builder.ins().load(
|
||||
r.value_type,
|
||||
mflags,
|
||||
values_vec_ptr_val,
|
||||
(i * value_size) as i32,
|
||||
);
|
||||
results.push(load);
|
||||
}
|
||||
builder.ins().return_(&results);
|
||||
builder.finalize()
|
||||
}
|
||||
|
||||
let mut code_buf: Vec<u8> = Vec::new();
|
||||
let mut reloc_sink = RelocSink {};
|
||||
let mut trap_sink = binemit::NullTrapSink {};
|
||||
let mut stackmap_sink = binemit::NullStackmapSink {};
|
||||
context
|
||||
.compile_and_emit(
|
||||
isa,
|
||||
&mut code_buf,
|
||||
&mut reloc_sink,
|
||||
&mut trap_sink,
|
||||
&mut stackmap_sink,
|
||||
)
|
||||
.expect("compile_and_emit");
|
||||
|
||||
let mut unwind_info = Vec::new();
|
||||
context.emit_unwind_info(isa, &mut unwind_info);
|
||||
|
||||
code_memory
|
||||
.allocate_for_function(&CompiledFunction {
|
||||
body: code_buf,
|
||||
jt_offsets: context.func.jt_offsets,
|
||||
unwind_info,
|
||||
})
|
||||
.expect("allocate_for_function")
|
||||
.as_ptr()
|
||||
}
|
||||
|
||||
pub fn create_handle_with_function(
|
||||
ft: &FuncType,
|
||||
func: &Rc<dyn Callable + 'static>,
|
||||
store: &HostRef<Store>,
|
||||
) -> Result<InstanceHandle> {
|
||||
let sig = ft.get_cranelift_signature().clone();
|
||||
|
||||
let isa = {
|
||||
let isa_builder =
|
||||
cranelift_native::builder().expect("host machine is not a supported target");
|
||||
let flag_builder = cranelift_codegen::settings::builder();
|
||||
isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder))
|
||||
};
|
||||
|
||||
let mut fn_builder_ctx = FunctionBuilderContext::new();
|
||||
let mut module = Module::new();
|
||||
let mut finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody> =
|
||||
PrimaryMap::new();
|
||||
let mut code_memory = CodeMemory::new();
|
||||
|
||||
//let pointer_type = types::Type::triple_pointer_type(&HOST);
|
||||
//let call_conv = isa::CallConv::triple_default(&HOST);
|
||||
|
||||
let sig_id = module.signatures.push(sig.clone());
|
||||
let func_id = module.functions.push(sig_id);
|
||||
module
|
||||
.exports
|
||||
.insert("trampoline".to_string(), Export::Function(func_id));
|
||||
let trampoline = make_trampoline(
|
||||
isa.as_ref(),
|
||||
&mut code_memory,
|
||||
&mut fn_builder_ctx,
|
||||
func_id.index() as u32,
|
||||
&sig,
|
||||
);
|
||||
code_memory.publish();
|
||||
|
||||
finished_functions.push(trampoline);
|
||||
|
||||
let trampoline_state = TrampolineState {
|
||||
func: func.clone(),
|
||||
trap: None,
|
||||
code_memory,
|
||||
};
|
||||
|
||||
create_handle(
|
||||
module,
|
||||
Some(store.borrow_mut()),
|
||||
finished_functions,
|
||||
Box::new(trampoline_state),
|
||||
)
|
||||
}
|
||||
|
||||
/// We don't expect trampoline compilation to produce any relocations, so
|
||||
/// this `RelocSink` just asserts that it doesn't recieve any.
|
||||
struct RelocSink {}
|
||||
|
||||
impl binemit::RelocSink for RelocSink {
|
||||
fn reloc_ebb(
|
||||
&mut self,
|
||||
_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_ebb_offset: binemit::CodeOffset,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce ebb relocs");
|
||||
}
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_name: &ir::ExternalName,
|
||||
_addend: binemit::Addend,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce external symbol relocs");
|
||||
}
|
||||
fn reloc_constant(
|
||||
&mut self,
|
||||
_code_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_constant_offset: ir::ConstantOffset,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce constant relocs");
|
||||
}
|
||||
fn reloc_jt(
|
||||
&mut self,
|
||||
_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_jt: ir::JumpTable,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce jump table relocs");
|
||||
}
|
||||
}
|
||||
46
crates/api/src/trampoline/global.rs
Normal file
46
crates/api/src/trampoline/global.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use alloc::boxed::Box;
|
||||
use anyhow::Result;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use wasmtime_environ::Module;
|
||||
use wasmtime_runtime::{InstanceHandle, VMGlobalDefinition};
|
||||
|
||||
use super::create_handle::create_handle;
|
||||
use crate::{GlobalType, Mutability, Val};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct GlobalState {
|
||||
definition: Box<VMGlobalDefinition>,
|
||||
handle: InstanceHandle,
|
||||
}
|
||||
|
||||
pub fn create_global(gt: &GlobalType, val: Val) -> Result<(wasmtime_runtime::Export, GlobalState)> {
|
||||
let mut definition = Box::new(VMGlobalDefinition::new());
|
||||
unsafe {
|
||||
match val {
|
||||
Val::I32(i) => *definition.as_i32_mut() = i,
|
||||
Val::I64(i) => *definition.as_i64_mut() = i,
|
||||
Val::F32(f) => *definition.as_u32_mut() = f,
|
||||
Val::F64(f) => *definition.as_u64_mut() = f,
|
||||
_ => unimplemented!("create_global for {:?}", gt),
|
||||
}
|
||||
}
|
||||
|
||||
let global = cranelift_wasm::Global {
|
||||
ty: gt.content().get_cranelift_type(),
|
||||
mutability: match gt.mutability() {
|
||||
Mutability::Const => false,
|
||||
Mutability::Var => true,
|
||||
},
|
||||
initializer: cranelift_wasm::GlobalInit::Import, // TODO is it right?
|
||||
};
|
||||
let mut handle =
|
||||
create_handle(Module::new(), None, PrimaryMap::new(), Box::new(())).expect("handle");
|
||||
Ok((
|
||||
wasmtime_runtime::Export::Global {
|
||||
definition: definition.as_mut(),
|
||||
vmctx: handle.vmctx_mut_ptr(),
|
||||
global,
|
||||
},
|
||||
GlobalState { definition, handle },
|
||||
))
|
||||
}
|
||||
35
crates/api/src/trampoline/memory.rs
Normal file
35
crates/api/src/trampoline/memory.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::ToString;
|
||||
use anyhow::Result;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use wasmtime_environ::Module;
|
||||
use wasmtime_runtime::InstanceHandle;
|
||||
|
||||
use super::create_handle::create_handle;
|
||||
use crate::MemoryType;
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
||||
pub fn create_handle_with_memory(memory: &MemoryType) -> Result<InstanceHandle> {
|
||||
let mut module = Module::new();
|
||||
|
||||
let memory = cranelift_wasm::Memory {
|
||||
minimum: memory.limits().min(),
|
||||
maximum: if memory.limits().max() == core::u32::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(memory.limits().max())
|
||||
},
|
||||
shared: false, // TODO
|
||||
};
|
||||
let tunable = Default::default();
|
||||
|
||||
let memory_plan = wasmtime_environ::MemoryPlan::for_memory(memory, &tunable);
|
||||
let memory_id = module.memory_plans.push(memory_plan);
|
||||
module.exports.insert(
|
||||
"memory".to_string(),
|
||||
wasmtime_environ::Export::Memory(memory_id),
|
||||
);
|
||||
|
||||
create_handle(module, None, PrimaryMap::new(), Box::new(()))
|
||||
}
|
||||
52
crates/api/src/trampoline/mod.rs
Normal file
52
crates/api/src/trampoline/mod.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
//! Utility module to create trampolines in/out WebAssembly module.
|
||||
|
||||
mod create_handle;
|
||||
mod func;
|
||||
mod global;
|
||||
mod memory;
|
||||
mod table;
|
||||
|
||||
use crate::r#ref::HostRef;
|
||||
use alloc::rc::Rc;
|
||||
use anyhow::Result;
|
||||
|
||||
use self::func::create_handle_with_function;
|
||||
use self::global::create_global;
|
||||
use self::memory::create_handle_with_memory;
|
||||
use self::table::create_handle_with_table;
|
||||
use super::{Callable, FuncType, GlobalType, MemoryType, Store, TableType, Val};
|
||||
|
||||
pub use self::global::GlobalState;
|
||||
|
||||
pub fn generate_func_export(
|
||||
ft: &FuncType,
|
||||
func: &Rc<dyn Callable + 'static>,
|
||||
store: &HostRef<Store>,
|
||||
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
|
||||
let mut instance = create_handle_with_function(ft, func, store)?;
|
||||
let export = instance.lookup("trampoline").expect("trampoline export");
|
||||
Ok((instance, export))
|
||||
}
|
||||
|
||||
pub fn generate_global_export(
|
||||
gt: &GlobalType,
|
||||
val: Val,
|
||||
) -> Result<(wasmtime_runtime::Export, GlobalState)> {
|
||||
create_global(gt, val)
|
||||
}
|
||||
|
||||
pub fn generate_memory_export(
|
||||
m: &MemoryType,
|
||||
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
|
||||
let mut instance = create_handle_with_memory(m)?;
|
||||
let export = instance.lookup("memory").expect("memory export");
|
||||
Ok((instance, export))
|
||||
}
|
||||
|
||||
pub fn generate_table_export(
|
||||
t: &TableType,
|
||||
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
|
||||
let mut instance = create_handle_with_table(t)?;
|
||||
let export = instance.lookup("table").expect("table export");
|
||||
Ok((instance, export))
|
||||
}
|
||||
37
crates/api/src/trampoline/table.rs
Normal file
37
crates/api/src/trampoline/table.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::ToString;
|
||||
use anyhow::Result;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::TableElementType;
|
||||
use wasmtime_environ::Module;
|
||||
use wasmtime_runtime::InstanceHandle;
|
||||
|
||||
use super::create_handle::create_handle;
|
||||
use crate::{TableType, ValType};
|
||||
|
||||
pub fn create_handle_with_table(table: &TableType) -> Result<InstanceHandle> {
|
||||
let mut module = Module::new();
|
||||
|
||||
let table = cranelift_wasm::Table {
|
||||
minimum: table.limits().min(),
|
||||
maximum: if table.limits().max() == core::u32::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(table.limits().max())
|
||||
},
|
||||
ty: match table.element() {
|
||||
ValType::FuncRef => TableElementType::Func,
|
||||
_ => TableElementType::Val(table.element().get_cranelift_type()),
|
||||
},
|
||||
};
|
||||
let tunable = Default::default();
|
||||
|
||||
let table_plan = wasmtime_environ::TablePlan::for_table(table, &tunable);
|
||||
let table_id = module.table_plans.push(table_plan);
|
||||
module.exports.insert(
|
||||
"table".to_string(),
|
||||
wasmtime_environ::Export::Table(table_id),
|
||||
);
|
||||
|
||||
create_handle(module, None, PrimaryMap::new(), Box::new(()))
|
||||
}
|
||||
22
crates/api/src/trap.rs
Normal file
22
crates/api/src/trap.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use alloc::string::{String, ToString};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("Wasm trap: {message}")]
|
||||
pub struct Trap {
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl Trap {
|
||||
pub fn new(message: String) -> Trap {
|
||||
Trap { message }
|
||||
}
|
||||
|
||||
pub fn fake() -> Trap {
|
||||
Trap::new("TODO trap".to_string())
|
||||
}
|
||||
|
||||
pub fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
}
|
||||
353
crates/api/src/types.rs
Normal file
353
crates/api/src/types.rs
Normal file
@@ -0,0 +1,353 @@
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use cranelift_codegen::ir;
|
||||
|
||||
// Type Representations
|
||||
|
||||
// Type attributes
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Mutability {
|
||||
Const,
|
||||
Var,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Limits {
|
||||
min: u32,
|
||||
max: u32,
|
||||
}
|
||||
|
||||
impl Limits {
|
||||
pub fn new(min: u32, max: u32) -> Limits {
|
||||
Limits { min, max }
|
||||
}
|
||||
|
||||
pub fn at_least(min: u32) -> Limits {
|
||||
Limits {
|
||||
min,
|
||||
max: ::core::u32::MAX,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min(&self) -> u32 {
|
||||
self.min
|
||||
}
|
||||
|
||||
pub fn max(&self) -> u32 {
|
||||
self.max
|
||||
}
|
||||
}
|
||||
|
||||
// Value Types
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum ValType {
|
||||
I32,
|
||||
I64,
|
||||
F32,
|
||||
F64,
|
||||
V128,
|
||||
AnyRef, /* = 128 */
|
||||
FuncRef,
|
||||
}
|
||||
|
||||
impl ValType {
|
||||
pub fn is_num(&self) -> bool {
|
||||
match self {
|
||||
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ref(&self) -> bool {
|
||||
match self {
|
||||
ValType::AnyRef | ValType::FuncRef => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_cranelift_type(&self) -> ir::Type {
|
||||
match self {
|
||||
ValType::I32 => ir::types::I32,
|
||||
ValType::I64 => ir::types::I64,
|
||||
ValType::F32 => ir::types::F32,
|
||||
ValType::F64 => ir::types::F64,
|
||||
ValType::V128 => ir::types::I8X16,
|
||||
_ => unimplemented!("get_cranelift_type other"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_cranelift_type(ty: ir::Type) -> ValType {
|
||||
match ty {
|
||||
ir::types::I32 => ValType::I32,
|
||||
ir::types::I64 => ValType::I64,
|
||||
ir::types::F32 => ValType::F32,
|
||||
ir::types::F64 => ValType::F64,
|
||||
ir::types::I8X16 => ValType::V128,
|
||||
_ => unimplemented!("from_cranelift_type other"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// External Types
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ExternType {
|
||||
ExternFunc(FuncType),
|
||||
ExternGlobal(GlobalType),
|
||||
ExternTable(TableType),
|
||||
ExternMemory(MemoryType),
|
||||
}
|
||||
|
||||
impl ExternType {
|
||||
pub fn func(&self) -> &FuncType {
|
||||
match self {
|
||||
ExternType::ExternFunc(func) => func,
|
||||
_ => panic!("ExternType::ExternFunc expected"),
|
||||
}
|
||||
}
|
||||
pub fn global(&self) -> &GlobalType {
|
||||
match self {
|
||||
ExternType::ExternGlobal(func) => func,
|
||||
_ => panic!("ExternType::ExternGlobal expected"),
|
||||
}
|
||||
}
|
||||
pub fn table(&self) -> &TableType {
|
||||
match self {
|
||||
ExternType::ExternTable(table) => table,
|
||||
_ => panic!("ExternType::ExternTable expected"),
|
||||
}
|
||||
}
|
||||
pub fn memory(&self) -> &MemoryType {
|
||||
match self {
|
||||
ExternType::ExternMemory(memory) => memory,
|
||||
_ => panic!("ExternType::ExternMemory expected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function Types
|
||||
fn from_cranelift_abiparam(param: &ir::AbiParam) -> ValType {
|
||||
assert!(param.purpose == ir::ArgumentPurpose::Normal);
|
||||
ValType::from_cranelift_type(param.value_type)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FuncType {
|
||||
params: Box<[ValType]>,
|
||||
results: Box<[ValType]>,
|
||||
signature: ir::Signature,
|
||||
}
|
||||
|
||||
impl FuncType {
|
||||
pub fn new(params: Box<[ValType]>, results: Box<[ValType]>) -> FuncType {
|
||||
use cranelift_codegen::ir::*;
|
||||
use cranelift_codegen::isa::CallConv;
|
||||
use target_lexicon::HOST;
|
||||
let call_conv = CallConv::triple_default(&HOST);
|
||||
let signature: Signature = {
|
||||
let mut params = params
|
||||
.iter()
|
||||
.map(|p| AbiParam::new(p.get_cranelift_type()))
|
||||
.collect::<Vec<_>>();
|
||||
let returns = results
|
||||
.iter()
|
||||
.map(|p| AbiParam::new(p.get_cranelift_type()))
|
||||
.collect::<Vec<_>>();
|
||||
params.insert(0, AbiParam::special(types::I64, ArgumentPurpose::VMContext));
|
||||
|
||||
Signature {
|
||||
params,
|
||||
returns,
|
||||
call_conv,
|
||||
}
|
||||
};
|
||||
FuncType {
|
||||
params,
|
||||
results,
|
||||
signature,
|
||||
}
|
||||
}
|
||||
pub fn params(&self) -> &[ValType] {
|
||||
&self.params
|
||||
}
|
||||
pub fn results(&self) -> &[ValType] {
|
||||
&self.results
|
||||
}
|
||||
|
||||
pub(crate) fn get_cranelift_signature(&self) -> &ir::Signature {
|
||||
&self.signature
|
||||
}
|
||||
|
||||
pub(crate) fn from_cranelift_signature(signature: ir::Signature) -> FuncType {
|
||||
let params = signature
|
||||
.params
|
||||
.iter()
|
||||
.filter(|p| p.purpose == ir::ArgumentPurpose::Normal)
|
||||
.map(|p| from_cranelift_abiparam(p))
|
||||
.collect::<Vec<_>>();
|
||||
let results = signature
|
||||
.returns
|
||||
.iter()
|
||||
.map(|p| from_cranelift_abiparam(p))
|
||||
.collect::<Vec<_>>();
|
||||
FuncType {
|
||||
params: params.into_boxed_slice(),
|
||||
results: results.into_boxed_slice(),
|
||||
signature,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Global Types
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GlobalType {
|
||||
content: ValType,
|
||||
mutability: Mutability,
|
||||
}
|
||||
|
||||
impl GlobalType {
|
||||
pub fn new(content: ValType, mutability: Mutability) -> GlobalType {
|
||||
GlobalType {
|
||||
content,
|
||||
mutability,
|
||||
}
|
||||
}
|
||||
pub fn content(&self) -> &ValType {
|
||||
&self.content
|
||||
}
|
||||
pub fn mutability(&self) -> Mutability {
|
||||
self.mutability
|
||||
}
|
||||
|
||||
pub(crate) fn from_cranelift_global(global: cranelift_wasm::Global) -> GlobalType {
|
||||
let ty = ValType::from_cranelift_type(global.ty);
|
||||
let mutability = if global.mutability {
|
||||
Mutability::Var
|
||||
} else {
|
||||
Mutability::Const
|
||||
};
|
||||
GlobalType::new(ty, mutability)
|
||||
}
|
||||
}
|
||||
|
||||
// Table Types
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TableType {
|
||||
element: ValType,
|
||||
limits: Limits,
|
||||
}
|
||||
|
||||
impl TableType {
|
||||
pub fn new(element: ValType, limits: Limits) -> TableType {
|
||||
TableType { element, limits }
|
||||
}
|
||||
pub fn element(&self) -> &ValType {
|
||||
&self.element
|
||||
}
|
||||
pub fn limits(&self) -> &Limits {
|
||||
&self.limits
|
||||
}
|
||||
|
||||
pub(crate) fn from_cranelift_table(table: cranelift_wasm::Table) -> TableType {
|
||||
assert!(if let cranelift_wasm::TableElementType::Func = table.ty {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
});
|
||||
let ty = ValType::FuncRef;
|
||||
let limits = Limits::new(table.minimum, table.maximum.unwrap_or(::core::u32::MAX));
|
||||
TableType::new(ty, limits)
|
||||
}
|
||||
}
|
||||
|
||||
// Memory Types
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MemoryType {
|
||||
limits: Limits,
|
||||
}
|
||||
|
||||
impl MemoryType {
|
||||
pub fn new(limits: Limits) -> MemoryType {
|
||||
MemoryType { limits }
|
||||
}
|
||||
pub fn limits(&self) -> &Limits {
|
||||
&self.limits
|
||||
}
|
||||
|
||||
pub(crate) fn from_cranelift_memory(memory: cranelift_wasm::Memory) -> MemoryType {
|
||||
MemoryType::new(Limits::new(
|
||||
memory.minimum,
|
||||
memory.maximum.unwrap_or(::core::u32::MAX),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Import Types
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Name(String);
|
||||
|
||||
impl From<String> for Name {
|
||||
fn from(s: String) -> Name {
|
||||
Name(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::alloc::string::ToString for Name {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImportType {
|
||||
module: Name,
|
||||
name: Name,
|
||||
r#type: ExternType,
|
||||
}
|
||||
|
||||
impl ImportType {
|
||||
pub fn new(module: Name, name: Name, r#type: ExternType) -> ImportType {
|
||||
ImportType {
|
||||
module,
|
||||
name,
|
||||
r#type,
|
||||
}
|
||||
}
|
||||
pub fn module(&self) -> &Name {
|
||||
&self.module
|
||||
}
|
||||
pub fn name(&self) -> &Name {
|
||||
&self.name
|
||||
}
|
||||
pub fn r#type(&self) -> &ExternType {
|
||||
&self.r#type
|
||||
}
|
||||
}
|
||||
|
||||
// Export Types
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExportType {
|
||||
name: Name,
|
||||
r#type: ExternType,
|
||||
}
|
||||
|
||||
impl ExportType {
|
||||
pub fn new(name: Name, r#type: ExternType) -> ExportType {
|
||||
ExportType { name, r#type }
|
||||
}
|
||||
pub fn name(&self) -> &Name {
|
||||
&self.name
|
||||
}
|
||||
pub fn r#type(&self) -> &ExternType {
|
||||
&self.r#type
|
||||
}
|
||||
}
|
||||
235
crates/api/src/values.rs
Normal file
235
crates/api/src/values.rs
Normal file
@@ -0,0 +1,235 @@
|
||||
use crate::externals::Func;
|
||||
use crate::r#ref::{AnyRef, HostRef};
|
||||
use crate::runtime::Store;
|
||||
use crate::types::ValType;
|
||||
use core::ptr;
|
||||
|
||||
use cranelift_codegen::ir;
|
||||
use wasmtime_jit::RuntimeValue;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Val {
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
F32(u32),
|
||||
F64(u64),
|
||||
AnyRef(AnyRef),
|
||||
FuncRef(HostRef<Func>),
|
||||
}
|
||||
|
||||
impl Val {
|
||||
pub fn default() -> Val {
|
||||
Val::AnyRef(AnyRef::null())
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> ValType {
|
||||
match self {
|
||||
Val::I32(_) => ValType::I32,
|
||||
Val::I64(_) => ValType::I64,
|
||||
Val::F32(_) => ValType::F32,
|
||||
Val::F64(_) => ValType::F64,
|
||||
Val::AnyRef(_) => ValType::AnyRef,
|
||||
Val::FuncRef(_) => ValType::FuncRef,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn write_value_to(&self, p: *mut i64) {
|
||||
match self {
|
||||
Val::I32(i) => ptr::write(p as *mut i32, *i),
|
||||
Val::I64(i) => ptr::write(p as *mut i64, *i),
|
||||
Val::F32(u) => ptr::write(p as *mut u32, *u),
|
||||
Val::F64(u) => ptr::write(p as *mut u64, *u),
|
||||
_ => unimplemented!("Val::write_value_to"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn read_value_from(p: *const i64, ty: ir::Type) -> Val {
|
||||
match ty {
|
||||
ir::types::I32 => Val::I32(ptr::read(p as *const i32)),
|
||||
ir::types::I64 => Val::I64(ptr::read(p as *const i64)),
|
||||
ir::types::F32 => Val::F32(ptr::read(p as *const u32)),
|
||||
ir::types::F64 => Val::F64(ptr::read(p as *const u64)),
|
||||
_ => unimplemented!("Val::read_value_from"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_f32_bits(v: u32) -> Val {
|
||||
Val::F32(v)
|
||||
}
|
||||
|
||||
pub fn from_f64_bits(v: u64) -> Val {
|
||||
Val::F64(v)
|
||||
}
|
||||
|
||||
pub fn i32(&self) -> i32 {
|
||||
if let Val::I32(i) = self {
|
||||
*i
|
||||
} else {
|
||||
panic!("Invalid conversion of {:?} to i32.", self);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn i64(&self) -> i64 {
|
||||
if let Val::I64(i) = self {
|
||||
*i
|
||||
} else {
|
||||
panic!("Invalid conversion of {:?} to i64.", self);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn f32(&self) -> f32 {
|
||||
RuntimeValue::F32(self.f32_bits()).unwrap_f32()
|
||||
}
|
||||
|
||||
pub fn f64(&self) -> f64 {
|
||||
RuntimeValue::F64(self.f64_bits()).unwrap_f64()
|
||||
}
|
||||
|
||||
pub fn f32_bits(&self) -> u32 {
|
||||
if let Val::F32(i) = self {
|
||||
*i
|
||||
} else {
|
||||
panic!("Invalid conversion of {:?} to f32.", self);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn f64_bits(&self) -> u64 {
|
||||
if let Val::F64(i) = self {
|
||||
*i
|
||||
} else {
|
||||
panic!("Invalid conversion of {:?} to f64.", self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Val {
|
||||
fn from(val: i32) -> Val {
|
||||
Val::I32(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Val {
|
||||
fn from(val: i64) -> Val {
|
||||
Val::I64(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for Val {
|
||||
fn from(val: f32) -> Val {
|
||||
Val::F32(val.to_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Val {
|
||||
fn from(val: f64) -> Val {
|
||||
Val::F64(val.to_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<i32> for Val {
|
||||
fn into(self) -> i32 {
|
||||
self.i32()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<i64> for Val {
|
||||
fn into(self) -> i64 {
|
||||
self.i64()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<f32> for Val {
|
||||
fn into(self) -> f32 {
|
||||
self.f32()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<f64> for Val {
|
||||
fn into(self) -> f64 {
|
||||
self.f64()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnyRef> for Val {
|
||||
fn from(val: AnyRef) -> Val {
|
||||
match &val {
|
||||
AnyRef::Ref(r) => {
|
||||
if r.is_ref::<Func>() {
|
||||
Val::FuncRef(r.get_ref())
|
||||
} else {
|
||||
Val::AnyRef(val)
|
||||
}
|
||||
}
|
||||
_ => unimplemented!("AnyRef::Other"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Func>> for Val {
|
||||
fn from(val: HostRef<Func>) -> Val {
|
||||
Val::FuncRef(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<AnyRef> for Val {
|
||||
fn into(self) -> AnyRef {
|
||||
match self {
|
||||
Val::AnyRef(r) => r,
|
||||
Val::FuncRef(f) => f.anyref(),
|
||||
_ => panic!("Invalid conversion of {:?} to anyref.", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_checked_anyfunc(
|
||||
val: Val,
|
||||
store: &HostRef<Store>,
|
||||
) -> wasmtime_runtime::VMCallerCheckedAnyfunc {
|
||||
match val {
|
||||
Val::AnyRef(AnyRef::Null) => wasmtime_runtime::VMCallerCheckedAnyfunc {
|
||||
func_ptr: ptr::null(),
|
||||
type_index: wasmtime_runtime::VMSharedSignatureIndex::default(),
|
||||
vmctx: ptr::null_mut(),
|
||||
},
|
||||
Val::FuncRef(f) => {
|
||||
let f = f.borrow();
|
||||
let (vmctx, func_ptr, signature) = match f.wasmtime_export() {
|
||||
wasmtime_runtime::Export::Function {
|
||||
vmctx,
|
||||
address,
|
||||
signature,
|
||||
} => (*vmctx, *address, signature),
|
||||
_ => panic!("expected function export"),
|
||||
};
|
||||
let type_index = store.borrow_mut().register_cranelift_signature(signature);
|
||||
wasmtime_runtime::VMCallerCheckedAnyfunc {
|
||||
func_ptr,
|
||||
type_index,
|
||||
vmctx,
|
||||
}
|
||||
}
|
||||
_ => panic!("val is not funcref"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_checked_anyfunc(
|
||||
item: &wasmtime_runtime::VMCallerCheckedAnyfunc,
|
||||
store: &HostRef<Store>,
|
||||
) -> Val {
|
||||
if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() {
|
||||
return Val::AnyRef(AnyRef::Null);
|
||||
}
|
||||
let signature = store
|
||||
.borrow()
|
||||
.lookup_cranelift_signature(item.type_index)
|
||||
.expect("signature")
|
||||
.clone();
|
||||
let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) };
|
||||
let export = wasmtime_runtime::Export::Function {
|
||||
address: item.func_ptr,
|
||||
signature,
|
||||
vmctx: item.vmctx,
|
||||
};
|
||||
let f = Func::from_wasmtime_function(export, store, instance_handle);
|
||||
Val::FuncRef(HostRef::new(f))
|
||||
}
|
||||
1655
crates/api/src/wasm.rs
Normal file
1655
crates/api/src/wasm.rs
Normal file
File diff suppressed because it is too large
Load Diff
39
crates/api/tests/examples.rs
Normal file
39
crates/api/tests/examples.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use std::env;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
fn run_example(name: &'static str) {
|
||||
let cargo = env::var("CARGO").unwrap_or("cargo".to_string());
|
||||
let pkg_dir = env!("CARGO_MANIFEST_DIR");
|
||||
assert!(
|
||||
Command::new(cargo)
|
||||
.current_dir(pkg_dir)
|
||||
.stdout(Stdio::null())
|
||||
.args(&["run", "-q", "--example", name])
|
||||
.status()
|
||||
.expect("success")
|
||||
.success(),
|
||||
"failed to execute the example '{}'",
|
||||
name,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_hello_example() {
|
||||
run_example("hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_gcd_example() {
|
||||
run_example("gcd");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_memory_example() {
|
||||
run_example("memory");
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[test]
|
||||
fn test_run_multi_example() {
|
||||
run_example("multi");
|
||||
}
|
||||
70
crates/api/tests/import_calling_export.rs
Normal file
70
crates/api/tests/import_calling_export.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::{Ref, RefCell};
|
||||
use std::fs::read;
|
||||
use wasmtime_api::*;
|
||||
|
||||
#[test]
|
||||
fn test_import_calling_export() {
|
||||
struct Callback {
|
||||
pub other: RefCell<Option<HostRef<Func>>>,
|
||||
}
|
||||
|
||||
impl Callable for Callback {
|
||||
fn call(&self, _params: &[Val], _results: &mut [Val]) -> Result<(), HostRef<Trap>> {
|
||||
self.other
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.expect("expected a function ref")
|
||||
.borrow()
|
||||
.call(&[])
|
||||
.expect("expected function not to trap");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let engine = HostRef::new(Engine::new(Config::default()));
|
||||
let store = HostRef::new(Store::new(&engine));
|
||||
let module = HostRef::new(
|
||||
Module::new(
|
||||
&store,
|
||||
&read("tests/import_calling_export.wasm").expect("failed to read wasm file"),
|
||||
)
|
||||
.expect("failed to create module"),
|
||||
);
|
||||
|
||||
let callback = Rc::new(Callback {
|
||||
other: RefCell::new(None),
|
||||
});
|
||||
|
||||
let callback_func = HostRef::new(Func::new(
|
||||
&store,
|
||||
FuncType::new(Box::new([]), Box::new([])),
|
||||
callback.clone(),
|
||||
));
|
||||
|
||||
let imports = vec![callback_func.into()];
|
||||
let instance = HostRef::new(
|
||||
Instance::new(&store, &module, imports.as_slice()).expect("failed to instantiate module"),
|
||||
);
|
||||
|
||||
let exports = Ref::map(instance.borrow(), |instance| instance.exports());
|
||||
assert!(!exports.is_empty());
|
||||
|
||||
let run_func = exports[0]
|
||||
.func()
|
||||
.expect("expected a run func in the module");
|
||||
|
||||
*callback.other.borrow_mut() = Some(
|
||||
exports[1]
|
||||
.func()
|
||||
.expect("expected an other func in the module")
|
||||
.clone(),
|
||||
);
|
||||
|
||||
run_func
|
||||
.borrow()
|
||||
.call(&[])
|
||||
.expect("expected function not to trap");
|
||||
}
|
||||
BIN
crates/api/tests/import_calling_export.wasm
Normal file
BIN
crates/api/tests/import_calling_export.wasm
Normal file
Binary file not shown.
8
crates/api/tests/import_calling_export.wat
Normal file
8
crates/api/tests/import_calling_export.wat
Normal file
@@ -0,0 +1,8 @@
|
||||
(module
|
||||
(type $t0 (func))
|
||||
(import "" "imp" (func $.imp (type $t0)))
|
||||
(func $run call $.imp)
|
||||
(func $other)
|
||||
(export "run" (func $run))
|
||||
(export "other" (func $other))
|
||||
)
|
||||
Reference in New Issue
Block a user