Repository: bytecodealliance/wasmtime
Pull request #8170 (2024-03-18 18:21:15)

Remove wasm-c-api submodule
This submodule hasn't been updated in ~3 years at this point and we additionally don't need most of the submodule. Instead add a script to copy the files we need and verify in CI that the files are up-to-date.

This also makes using the C API a bit nicer where you don't have to have two `include` directories with a Wasmtime source tree, just one suffices.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index eb48f0ad9..e606cf6bb 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -65,7 +65,11 @@ jobs:
     - uses: actions/checkout@v4
       with:
         submodules: true
-    - run: git ls-files '*.h' '*.c' '*.cpp' | grep -v wasmtime-platform.h | xargs clang-format-15 --dry-run --Werror --verbose
+    - run: |
+        git ls-files '*.h' '*.c' '*.cpp' | \
+          grep -v wasmtime-platform.h | \
+          grep -v wasm.h | \
+          xargs clang-format-15 --dry-run --Werror --verbose
 
     # common logic to cancel the entire run if this job fails
     - run: gh run cancel ${{ github.run_id }}
@@ -360,6 +364,11 @@ jobs:
       run: ./ci/vendor-wit.sh
     - run: git diff --exit-code
 
+    # Re-vendor the C API and make sure it's up-to-date.
+    - name: Re-vendor WIT
+      run: ./ci/vendor-c-api-headers.sh
+    - run: git diff --exit-code
+
     # common logic to cancel the entire run if this job fails
     - run: gh run cancel ${{ github.run_id }}
       if: failure() && github.event_name != 'pull_request'
diff --git a/.gitmodules b/.gitmodules
index bef1a49be..dc9dfa50f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,9 +1,6 @@
 [submodule "tests/spec_testsuite"]
 	path = tests/spec_testsuite
 	url = https://github.com/WebAssembly/testsuite
-[submodule "crates/c-api/wasm-c-api"]
-	path = crates/c-api/wasm-c-api
-	url = https://github.com/WebAssembly/wasm-c-api
 [submodule "crates/wasi-nn/spec"]
 	path = crates/wasi-nn/spec
 	url = https://github.com/WebAssembly/wasi-nn
diff --git a/ci/build-tarballs.sh b/ci/build-tarballs.sh
index d8a249f11..114174bfe 100755
--- a/ci/build-tarballs.sh
+++ b/ci/build-tarballs.sh
@@ -41,7 +41,6 @@ mkdir tmp/$bin_pkgname
 cp LICENSE README.md tmp/$api_pkgname
 cp LICENSE README.md tmp/$bin_pkgname
 cp -r crates/c-api/include tmp/$api_pkgname
-cp crates/c-api/wasm-c-api/include/wasm.h tmp/$api_pkgname/include
 
 # For *-min builds rename artifacts with a `-min` suffix to avoid eventual
 # clashes with the normal builds when the tarballs are unioned together.
diff --git a/ci/vendor-c-api-headers.sh b/ci/vendor-c-api-headers.sh
new file mode 100755
index 000000000..fa0b41792
--- /dev/null
+++ b/ci/vendor-c-api-headers.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+# Script to re-vendor the headers from the `wasm-c-api` proposal.
+
+rev="2ce1367c9d1271c83fb63bef26d896a2f290cd23"
+files="wasm.h wasm.hh"
+
+set -ex
+
+for file in $files; do
+  dst=crates/c-api/include/$file
+  pretty_url=https://github.com/WebAssembly/wasm-c-api/blob/$rev/include/$file
+  url=https://raw.githubusercontent.com/WebAssembly/wasm-c-api/$rev/include/$file
+  cat >$dst <<-EOF
+// Wasmtime-vendored copy of this header file as present in upstream:
+// <$pretty_url>
+//
+// Wasmtime maintainers can update this vendored copy in-tree with the
+// ./ci/vendor-c-api-headers.sh shell script.
+//
+EOF
+  curl $url >> $dst
+done
diff --git a/crates/c-api/CMakeLists.txt b/crates/c-api/CMakeLists.txt
index ff92a091e..2075dd2bd 100644
--- a/crates/c-api/CMakeLists.txt
+++ b/crates/c-api/CMakeLists.txt
@@ -107,16 +107,14 @@ else()
 	endif()
 endif()
 
-target_include_directories(wasmtime INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include
-	${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/include)
+target_include_directories(wasmtime INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
 
 include(GNUInstallDirs)
 install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/wasmtime.h
 	${CMAKE_CURRENT_SOURCE_DIR}/include/wasi.h
 	${CMAKE_CURRENT_SOURCE_DIR}/include/doc-wasm.h
-	DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
-install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/include/wasm.h
-	${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/include/wasm.hh
+	${CMAKE_CURRENT_SOURCE_DIR}/include/wasm.h
+	${CMAKE_CURRENT_SOURCE_DIR}/include/wasm.hh
 	DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
 install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/wasmtime
 	DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml
index 2e6942f45..c54b64d89 100644
--- a/crates/c-api/Cargo.toml
+++ b/crates/c-api/Cargo.toml
@@ -8,7 +8,7 @@ repository = "https://github.com/bytecodealliance/wasmtime"
 readme = "README.md"
 edition.workspace = true
 links = "wasmtime-c-api"
-include = ["include", "src", "wasm-c-api/include", "build.rs"]
+include = ["include", "src", "build.rs"]
 
 [lints]
 workspace = true
diff --git a/crates/c-api/build.rs b/crates/c-api/build.rs
index 7f3f03bd9..7c9151fbe 100644
--- a/crates/c-api/build.rs
+++ b/crates/c-api/build.rs
@@ -1,5 +1,4 @@
 fn main() {
     let dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
     println!("cargo:include={dir}/include");
-    println!("cargo:wasm_include={dir}/wasm-c-api/include");
 }
diff --git a/crates/c-api/doxygen.conf b/crates/c-api/doxygen.conf
index 4e43ca407..6c8147e11 100644
--- a/crates/c-api/doxygen.conf
+++ b/crates/c-api/doxygen.conf
@@ -864,7 +864,7 @@ WARN_LOGFILE           =
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = include wasm-c-api/include
+INPUT                  = include
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -2162,8 +2162,7 @@ SEARCH_INCLUDES        = YES
 # preprocessor.
 # This tag requires that the tag SEARCH_INCLUDES is set to YES.
 
-INCLUDE_PATH           = wasm-c-api/include \
-                         include
+INCLUDE_PATH           = include
 
 # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
 # patterns (like *.h and *.hpp) to filter out the header-files in the
diff --git a/crates/c-api/include/wasm.h b/crates/c-api/include/wasm.h
new file mode 100644
index 000000000..c8b3cb7d3
--- /dev/null
+++ b/crates/c-api/include/wasm.h
@@ -0,0 +1,729 @@
+// Wasmtime-vendored copy of this header file as present in upstream:
+// <https://github.com/WebAssembly/wasm-c-api/blob/2ce1367c9d1271c83fb63bef26d896a2f290cd23/include/wasm.h>
+//
+// Wasmtime maintainers can update this vendored copy in-tree with the
+// ./ci/vendor-c-api-headers.sh shell script.
+//
+// WebAssembly C API
+
+#ifndef WASM_H
+#define WASM_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+
+#ifndef WASM_API_EXTERN
+#if defined(_WIN32) && !defined(__MINGW32__) && !defined(LIBWASM_STATIC)
+#define WASM_API_EXTERN __declspec(dllimport)
+#else
+#define WASM_API_EXTERN
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Auxiliaries
+
+// Machine types
+
+inline void assertions(void) {
+  static_assert(sizeof(float) == sizeof(uint32_t), "incompatible float type");
+  static_assert(sizeof(double) == sizeof(uint64_t), "incompatible double type");
+  static_assert(sizeof(intptr_t) == sizeof(uint32_t) ||
+                sizeof(intptr_t) == sizeof(uint64_t),
+                "incompatible pointer type");
+}
+
+typedef char byte_t;
+typedef float float32_t;
+typedef double float64_t;
+
+
+// Ownership
+
+#define own
+
+// The qualifier `own` is used to indicate ownership of data in this API.
+// It is intended to be interpreted similar to a `const` qualifier:
+//
+// - `own wasm_xxx_t*` owns the pointed-to data
+// - `own wasm_xxx_t` distributes to all fields of a struct or union `xxx`
+// - `own wasm_xxx_vec_t` owns the vector as well as its elements(!)
+// - an `own` function parameter passes ownership from caller to callee
+// - an `own` function result passes ownership from callee to caller
+// - an exception are `own` pointer parameters named `out`, which are copy-back
+//   output parameters passing back ownership from callee to caller
+//
+// Own data is created by `wasm_xxx_new` functions and some others.
+// It must be released with the corresponding `wasm_xxx_delete` function.
+//
+// Deleting a reference does not necessarily delete the underlying object,
+// it merely indicates that this owner no longer uses it.
+//
+// For vectors, `const wasm_xxx_vec_t` is used informally to indicate that
+// neither the vector nor its elements should be modified.
+// TODO: introduce proper `wasm_xxx_const_vec_t`?
+
+
+#define WASM_DECLARE_OWN(name) \
+  typedef struct wasm_##name##_t wasm_##name##_t; \
+  \
+  WASM_API_EXTERN void wasm_##name##_delete(own wasm_##name##_t*);
+
+
+// Vectors
+
+#define WASM_DECLARE_VEC(name, ptr_or_none) \
+  typedef struct wasm_##name##_vec_t { \
+    size_t size; \
+    wasm_##name##_t ptr_or_none* data; \
+  } wasm_##name##_vec_t; \
+  \
+  WASM_API_EXTERN void wasm_##name##_vec_new_empty(own wasm_##name##_vec_t* out); \
+  WASM_API_EXTERN void wasm_##name##_vec_new_uninitialized( \
+    own wasm_##name##_vec_t* out, size_t); \
+  WASM_API_EXTERN void wasm_##name##_vec_new( \
+    own wasm_##name##_vec_t* out, \
+    size_t, own wasm_##name##_t ptr_or_none const[]); \
+  WASM_API_EXTERN void wasm_##name##_vec_copy( \
+    own wasm_##name##_vec_t* out, const wasm_##name##_vec_t*); \
+  WASM_API_EXTERN void wasm_##name##_vec_delete(own wasm_##name##_vec_t*);
+
+
+// Byte vectors
+
+typedef byte_t wasm_byte_t;
+WASM_DECLARE_VEC(byte, )
+
+typedef wasm_byte_vec_t wasm_name_t;
+
+#define wasm_name wasm_byte_vec
+#define wasm_name_new wasm_byte_vec_new
+#define wasm_name_new_empty wasm_byte_vec_new_empty
+#define wasm_name_new_new_uninitialized wasm_byte_vec_new_uninitialized
+#define wasm_name_copy wasm_byte_vec_copy
+#define wasm_name_delete wasm_byte_vec_delete
+
+static inline void wasm_name_new_from_string(
+  own wasm_name_t* out, const char* s
+) {
+  wasm_name_new(out, strlen(s), s);
+}
+
+static inline void wasm_name_new_from_string_nt(
+  own wasm_name_t* out, const char* s
+) {
+  wasm_name_new(out, strlen(s) + 1, s);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Runtime Environment
+
+// Configuration
+
+WASM_DECLARE_OWN(config)
+
+WASM_API_EXTERN own wasm_config_t* wasm_config_new(void);
+
+// Embedders may provide custom functions for manipulating configs.
+
+
+// Engine
+
+WASM_DECLARE_OWN(engine)
+
+WASM_API_EXTERN own wasm_engine_t* wasm_engine_new(void);
+WASM_API_EXTERN own wasm_engine_t* wasm_engine_new_with_config(own wasm_config_t*);
+
+
+// Store
+
+WASM_DECLARE_OWN(store)
+
+WASM_API_EXTERN own wasm_store_t* wasm_store_new(wasm_engine_t*);
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Type Representations
+
+// Type attributes
+
+typedef uint8_t wasm_mutability_t;
+enum wasm_mutability_enum {
+  WASM_CONST,
+  WASM_VAR,
+};
+
+typedef struct wasm_limits_t {
+  uint32_t min;
+  uint32_t max;
+} wasm_limits_t;
+
+static const uint32_t wasm_limits_max_default = 0xffffffff;
+
+
+// Generic
+
+#define WASM_DECLARE_TYPE(name) \
+  WASM_DECLARE_OWN(name) \
+  WASM_DECLARE_VEC(name, *) \
+  \
+  WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*);
+
+
+// Value Types
+
+WASM_DECLARE_TYPE(valtype)
+
+typedef uint8_t wasm_valkind_t;
+enum wasm_valkind_enum {
+  WASM_I32,
+  WASM_I64,
+  WASM_F32,
+  WASM_F64,
+  WASM_EXTERNREF = 128,
+  WASM_FUNCREF,
+};
+
+WASM_API_EXTERN own wasm_valtype_t* wasm_valtype_new(wasm_valkind_t);
+
+WASM_API_EXTERN wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t*);
+
+static inline bool wasm_valkind_is_num(wasm_valkind_t k) {
+  return k < WASM_EXTERNREF;
+}
+static inline bool wasm_valkind_is_ref(wasm_valkind_t k) {
+  return k >= WASM_EXTERNREF;
+}
+
+static inline bool wasm_valtype_is_num(const wasm_valtype_t* t) {
+  return wasm_valkind_is_num(wasm_valtype_kind(t));
+}
+static inline bool wasm_valtype_is_ref(const wasm_valtype_t* t) {
+  return wasm_valkind_is_ref(wasm_valtype_kind(t));
+}
+
+
+// Function Types
+
+WASM_DECLARE_TYPE(functype)
+
+WASM_API_EXTERN own wasm_functype_t* wasm_functype_new(
+  own wasm_valtype_vec_t* params, own wasm_valtype_vec_t* results);
+
+WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t*);
+WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t*);
+
+
+// Global Types
+
+WASM_DECLARE_TYPE(globaltype)
+
+WASM_API_EXTERN own wasm_globaltype_t* wasm_globaltype_new(
+  own wasm_valtype_t*, wasm_mutability_t);
+
+WASM_API_EXTERN const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t*);
+WASM_API_EXTERN wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t*);
+
+
+// Table Types
+
+WASM_DECLARE_TYPE(tabletype)
+
+WASM_API_EXTERN own wasm_tabletype_t* wasm_tabletype_new(
+  own wasm_valtype_t*, const wasm_limits_t*);
+
+WASM_API_EXTERN const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t*);
+WASM_API_EXTERN const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t*);
+
+
+// Memory Types
+
+WASM_DECLARE_TYPE(memorytype)
+
+WASM_API_EXTERN own wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t*);
+
+WASM_API_EXTERN const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t*);
+
+
+// Extern Types
+
+WASM_DECLARE_TYPE(externtype)
+
+typedef uint8_t wasm_externkind_t;
+enum wasm_externkind_enum {
+  WASM_EXTERN_FUNC,
+  WASM_EXTERN_GLOBAL,
+  WASM_EXTERN_TABLE,
+  WASM_EXTERN_MEMORY,
+};
+
+WASM_API_EXTERN wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t*);
+
+WASM_API_EXTERN wasm_externtype_t* wasm_functype_as_externtype(wasm_functype_t*);
+WASM_API_EXTERN wasm_externtype_t* wasm_globaltype_as_externtype(wasm_globaltype_t*);
+WASM_API_EXTERN wasm_externtype_t* wasm_tabletype_as_externtype(wasm_tabletype_t*);
+WASM_API_EXTERN wasm_externtype_t* wasm_memorytype_as_externtype(wasm_memorytype_t*);
+
+WASM_API_EXTERN wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t*);
+WASM_API_EXTERN wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t*);
+WASM_API_EXTERN wasm_tabletype_t* wasm_externtype_as_tabletype(wasm_externtype_t*);
+WASM_API_EXTERN wasm_memorytype_t* wasm_externtype_as_memorytype(wasm_externtype_t*);
+
+WASM_API_EXTERN const wasm_externtype_t* wasm_functype_as_externtype_const(const wasm_functype_t*);
+WASM_API_EXTERN const wasm_externtype_t* wasm_globaltype_as_externtype_const(const wasm_globaltype_t*);
+WASM_API_EXTERN const wasm_externtype_t* wasm_tabletype_as_externtype_const(const wasm_tabletype_t*);
+WASM_API_EXTERN const wasm_externtype_t* wasm_memorytype_as_externtype_const(const wasm_memorytype_t*);
+
+WASM_API_EXTERN const wasm_functype_t* wasm_externtype_as_functype_const(const wasm_externtype_t*);
+WASM_API_EXTERN const wasm_globaltype_t* wasm_externtype_as_globaltype_const(const wasm_externtype_t*);
+WASM_API_EXTERN const wasm_tabletype_t* wasm_externtype_as_tabletype_const(const wasm_externtype_t*);
+WASM_API_EXTERN const wasm_memorytype_t* wasm_externtype_as_memorytype_const(const wasm_externtype_t*);
+
+
+// Import Types
+
+WASM_DECLARE_TYPE(importtype)
+
+WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new(
+  own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*);
+
+WASM_API_EXTERN const wasm_name_t* wasm_importtype_module(const wasm_importtype_t*);
+WASM_API_EXTERN const wasm_name_t* wasm_importtype_name(const wasm_importtype_t*);
+WASM_API_EXTERN const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t*);
+
+
+// Export Types
+
+WASM_DECLARE_TYPE(exporttype)
+
+WASM_API_EXTERN own wasm_exporttype_t* wasm_exporttype_new(
+  own wasm_name_t*, own wasm_externtype_t*);
+
+WASM_API_EXTERN const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t*);
+WASM_API_EXTERN const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t*);
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Runtime Objects
+
+// Values
+
+struct wasm_ref_t;
+
+typedef struct wasm_val_t {
+  wasm_valkind_t kind;
+  union {
+    int32_t i32;
+    int64_t i64;
+    float32_t f32;
+    float64_t f64;
+    struct wasm_ref_t* ref;
+  } of;
+} wasm_val_t;
+
+WASM_API_EXTERN void wasm_val_delete(own wasm_val_t* v);
+WASM_API_EXTERN void wasm_val_copy(own wasm_val_t* out, const wasm_val_t*);
+
+WASM_DECLARE_VEC(val, )
+
+
+// References
+
+#define WASM_DECLARE_REF_BASE(name) \
+  WASM_DECLARE_OWN(name) \
+  \
+  WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*); \
+  WASM_API_EXTERN bool wasm_##name##_same(const wasm_##name##_t*, const wasm_##name##_t*); \
+  \
+  WASM_API_EXTERN void* wasm_##name##_get_host_info(const wasm_##name##_t*); \
+  WASM_API_EXTERN void wasm_##name##_set_host_info(wasm_##name##_t*, void*); \
+  WASM_API_EXTERN void wasm_##name##_set_host_info_with_finalizer( \
+    wasm_##name##_t*, void*, void (*)(void*));
+
+#define WASM_DECLARE_REF(name) \
+  WASM_DECLARE_REF_BASE(name) \
+  \
+  WASM_API_EXTERN wasm_ref_t* wasm_##name##_as_ref(wasm_##name##_t*); \
+  WASM_API_EXTERN wasm_##name##_t* wasm_ref_as_##name(wasm_ref_t*); \
+  WASM_API_EXTERN const wasm_ref_t* wasm_##name##_as_ref_const(const wasm_##name##_t*); \
+  WASM_API_EXTERN const wasm_##name##_t* wasm_ref_as_##name##_const(const wasm_ref_t*);
+
+#define WASM_DECLARE_SHARABLE_REF(name) \
+  WASM_DECLARE_REF(name) \
+  WASM_DECLARE_OWN(shared_##name) \
+  \
+  WASM_API_EXTERN own wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t*); \
+  WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, const wasm_shared_##name##_t*);
+
+
+WASM_DECLARE_REF_BASE(ref)
+
+
+// Frames
+
+WASM_DECLARE_OWN(frame)
+WASM_DECLARE_VEC(frame, *)
+WASM_API_EXTERN own wasm_frame_t* wasm_frame_copy(const wasm_frame_t*);
+
+WASM_API_EXTERN struct wasm_instance_t* wasm_frame_instance(const wasm_frame_t*);
+WASM_API_EXTERN uint32_t wasm_frame_func_index(const wasm_frame_t*);
+WASM_API_EXTERN size_t wasm_frame_func_offset(const wasm_frame_t*);
+WASM_API_EXTERN size_t wasm_frame_module_offset(const wasm_frame_t*);
+
+
+// Traps
+
+typedef wasm_name_t wasm_message_t;  // null terminated
+
+WASM_DECLARE_REF(trap)
+
+WASM_API_EXTERN own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t*);
+
+WASM_API_EXTERN void wasm_trap_message(const wasm_trap_t*, own wasm_message_t* out);
+WASM_API_EXTERN own wasm_frame_t* wasm_trap_origin(const wasm_trap_t*);
+WASM_API_EXTERN void wasm_trap_trace(const wasm_trap_t*, own wasm_frame_vec_t* out);
+
+
+// Foreign Objects
+
+WASM_DECLARE_REF(foreign)
+
+WASM_API_EXTERN own wasm_foreign_t* wasm_foreign_new(wasm_store_t*);
+
+
+// Modules
+
+WASM_DECLARE_SHARABLE_REF(module)
+
+WASM_API_EXTERN own wasm_module_t* wasm_module_new(
+  wasm_store_t*, const wasm_byte_vec_t* binary);
+
+WASM_API_EXTERN bool wasm_module_validate(wasm_store_t*, const wasm_byte_vec_t* binary);
+
+WASM_API_EXTERN void wasm_module_imports(const wasm_module_t*, own wasm_importtype_vec_t* out);
+WASM_API_EXTERN void wasm_module_exports(const wasm_module_t*, own wasm_exporttype_vec_t* out);
+
+WASM_API_EXTERN void wasm_module_serialize(const wasm_module_t*, own wasm_byte_vec_t* out);
+WASM_API_EXTERN own wasm_module_t* wasm_module_deserialize(wasm_store_t*, const wasm_byte_vec_t*);
+
+
+// Function Instances
+
+WASM_DECLARE_REF(func)
+
+typedef own wasm_trap_t* (*wasm_func_callback_t)(
+  const wasm_val_vec_t* args, own wasm_val_vec_t* results);
+typedef own wasm_trap_t* (*wasm_func_callback_with_env_t)(
+  void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results);
+
+WASM_API_EXTERN own wasm_func_t* wasm_func_new(
+  wasm_store_t*, const wasm_functype_t*, wasm_func_callback_t);
+WASM_API_EXTERN own wasm_func_t* wasm_func_new_with_env(
+  wasm_store_t*, const wasm_functype_t* type, wasm_func_callback_with_env_t,
+  void* env, void (*finalizer)(void*));
+
+WASM_API_EXTERN own wasm_functype_t* wasm_func_type(const wasm_func_t*);
+WASM_API_EXTERN size_t wasm_func_param_arity(const wasm_func_t*);
+WASM_API_EXTERN size_t wasm_func_result_arity(const wasm_func_t*);
+
+WASM_API_EXTERN own wasm_trap_t* wasm_func_call(
+  const wasm_func_t*, const wasm_val_vec_t* args, wasm_val_vec_t* results);
+
+
+// Global Instances
+
+WASM_DECLARE_REF(global)
+
+WASM_API_EXTERN own wasm_global_t* wasm_global_new(
+  wasm_store_t*, const wasm_globaltype_t*, const wasm_val_t*);
+
+WASM_API_EXTERN own wasm_globaltype_t* wasm_global_type(const wasm_global_t*);
+
+WASM_API_EXTERN void wasm_global_get(const wasm_global_t*, own wasm_val_t* out);
+WASM_API_EXTERN void wasm_global_set(wasm_global_t*, const wasm_val_t*);
+
+
+// Table Instances
+
+WASM_DECLARE_REF(table)
+
+typedef uint32_t wasm_table_size_t;
+
+WASM_API_EXTERN own wasm_table_t* wasm_table_new(
+  wasm_store_t*, const wasm_tabletype_t*, wasm_ref_t* init);
+
+WASM_API_EXTERN own wasm_tabletype_t* wasm_table_type(const wasm_table_t*);
+
+WASM_API_EXTERN own wasm_ref_t* wasm_table_get(const wasm_table_t*, wasm_table_size_t index);
+WASM_API_EXTERN bool wasm_table_set(wasm_table_t*, wasm_table_size_t index, wasm_ref_t*);
+
+WASM_API_EXTERN wasm_table_size_t wasm_table_size(const wasm_table_t*);
+WASM_API_EXTERN bool wasm_table_grow(wasm_table_t*, wasm_table_size_t delta, wasm_ref_t* init);
+
+
+// Memory Instances
+
+WASM_DECLARE_REF(memory)
+
+typedef uint32_t wasm_memory_pages_t;
+
+static const size_t MEMORY_PAGE_SIZE = 0x10000;
+
+WASM_API_EXTERN own wasm_memory_t* wasm_memory_new(wasm_store_t*, const wasm_memorytype_t*);
+
+WASM_API_EXTERN own wasm_memorytype_t* wasm_memory_type(const wasm_memory_t*);
+
+WASM_API_EXTERN byte_t* wasm_memory_data(wasm_memory_t*);
+WASM_API_EXTERN size_t wasm_memory_data_size(const wasm_memory_t*);
+
+WASM_API_EXTERN wasm_memory_pages_t wasm_memory_size(const wasm_memory_t*);
+WASM_API_EXTERN bool wasm_memory_grow(wasm_memory_t*, wasm_memory_pages_t delta);
+
+
+// Externals
+
+WASM_DECLARE_REF(extern)
+WASM_DECLARE_VEC(extern, *)
+
+WASM_API_EXTERN wasm_externkind_t wasm_extern_kind(const wasm_extern_t*);
+WASM_API_EXTERN own wasm_externtype_t* wasm_extern_type(const wasm_extern_t*);
+
+WASM_API_EXTERN wasm_extern_t* wasm_func_as_extern(wasm_func_t*);
+WASM_API_EXTERN wasm_extern_t* wasm_global_as_extern(wasm_global_t*);
+WASM_API_EXTERN wasm_extern_t* wasm_table_as_extern(wasm_table_t*);
+WASM_API_EXTERN wasm_extern_t* wasm_memory_as_extern(wasm_memory_t*);
+
+WASM_API_EXTERN wasm_func_t* wasm_extern_as_func(wasm_extern_t*);
+WASM_API_EXTERN wasm_global_t* wasm_extern_as_global(wasm_extern_t*);
+WASM_API_EXTERN wasm_table_t* wasm_extern_as_table(wasm_extern_t*);
+WASM_API_EXTERN wasm_memory_t* wasm_extern_as_memory(wasm_extern_t*);
+
+WASM_API_EXTERN const wasm_extern_t* wasm_func_as_extern_const(const wasm_func_t*);
+WASM_API_EXTERN const wasm_extern_t* wasm_global_as_extern_const(const wasm_global_t*);
+WASM_API_EXTERN const wasm_extern_t* wasm_table_as_extern_const(const wasm_table_t*);
+WASM_API_EXTERN const wasm_extern_t* wasm_memory_as_extern_const(const wasm_memory_t*);
+
+WASM_API_EXTERN const wasm_func_t* wasm_extern_as_func_const(const wasm_extern_t*);
+WASM_API_EXTERN const wasm_global_t* wasm_extern_as_global_const(const wasm_extern_t*);
+WASM_API_EXTERN const wasm_table_t* wasm_extern_as_table_const(const wasm_extern_t*);
+WASM_API_EXTERN const wasm_memory_t* wasm_extern_as_memory_const(const wasm_extern_t*);
+
+
+// Module Instances
+
+WASM_DECLARE_REF(instance)
+
+WASM_API_EXTERN own wasm_instance_t* wasm_instance_new(
+  wasm_store_t*, const wasm_module_t*, const wasm_extern_vec_t* imports,
+  own wasm_trap_t**
+);
+
+WASM_API_EXTERN void wasm_instance_exports(const wasm_instance_t*, own wasm_extern_vec_t* out);
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Convenience
+
+// Vectors
+
+#define WASM_EMPTY_VEC {0, NULL}
+#define WASM_ARRAY_VEC(array) {sizeof(array)/sizeof(*(array)), array}
+
+
+// Value Type construction short-hands
+
+static inline own wasm_valtype_t* wasm_valtype_new_i32(void) {
+  return wasm_valtype_new(WASM_I32);
+}
+static inline own wasm_valtype_t* wasm_valtype_new_i64(void) {
+  return wasm_valtype_new(WASM_I64);
+}
+static inline own wasm_valtype_t* wasm_valtype_new_f32(void) {
+  return wasm_valtype_new(WASM_F32);
+}
+static inline own wasm_valtype_t* wasm_valtype_new_f64(void) {
+  return wasm_valtype_new(WASM_F64);
+}
+
+static inline own wasm_valtype_t* wasm_valtype_new_externref(void) {
+  return wasm_valtype_new(WASM_EXTERNREF);
+}
+static inline own wasm_valtype_t* wasm_valtype_new_funcref(void) {
+  return wasm_valtype_new(WASM_FUNCREF);
+}
+
+
+// Function Types construction short-hands
+
+static inline own wasm_functype_t* wasm_functype_new_0_0(void) {
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new_empty(&params);
+  wasm_valtype_vec_new_empty(&results);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_1_0(
+  own wasm_valtype_t* p
+) {
+  wasm_valtype_t* ps[1] = {p};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 1, ps);
+  wasm_valtype_vec_new_empty(&results);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_2_0(
+  own wasm_valtype_t* p1, own wasm_valtype_t* p2
+) {
+  wasm_valtype_t* ps[2] = {p1, p2};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 2, ps);
+  wasm_valtype_vec_new_empty(&results);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_3_0(
+  own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3
+) {
+  wasm_valtype_t* ps[3] = {p1, p2, p3};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 3, ps);
+  wasm_valtype_vec_new_empty(&results);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_0_1(
+  own wasm_valtype_t* r
+) {
+  wasm_valtype_t* rs[1] = {r};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new_empty(&params);
+  wasm_valtype_vec_new(&results, 1, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_1_1(
+  own wasm_valtype_t* p, own wasm_valtype_t* r
+) {
+  wasm_valtype_t* ps[1] = {p};
+  wasm_valtype_t* rs[1] = {r};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 1, ps);
+  wasm_valtype_vec_new(&results, 1, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_2_1(
+  own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* r
+) {
+  wasm_valtype_t* ps[2] = {p1, p2};
+  wasm_valtype_t* rs[1] = {r};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 2, ps);
+  wasm_valtype_vec_new(&results, 1, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_3_1(
+  own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3,
+  own wasm_valtype_t* r
+) {
+  wasm_valtype_t* ps[3] = {p1, p2, p3};
+  wasm_valtype_t* rs[1] = {r};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 3, ps);
+  wasm_valtype_vec_new(&results, 1, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_0_2(
+  own wasm_valtype_t* r1, own wasm_valtype_t* r2
+) {
+  wasm_valtype_t* rs[2] = {r1, r2};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new_empty(&params);
+  wasm_valtype_vec_new(&results, 2, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_1_2(
+  own wasm_valtype_t* p, own wasm_valtype_t* r1, own wasm_valtype_t* r2
+) {
+  wasm_valtype_t* ps[1] = {p};
+  wasm_valtype_t* rs[2] = {r1, r2};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 1, ps);
+  wasm_valtype_vec_new(&results, 2, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_2_2(
+  own wasm_valtype_t* p1, own wasm_valtype_t* p2,
+  own wasm_valtype_t* r1, own wasm_valtype_t* r2
+) {
+  wasm_valtype_t* ps[2] = {p1, p2};
+  wasm_valtype_t* rs[2] = {r1, r2};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 2, ps);
+  wasm_valtype_vec_new(&results, 2, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_3_2(
+  own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3,
+  own wasm_valtype_t* r1, own wasm_valtype_t* r2
+) {
+  wasm_valtype_t* ps[3] = {p1, p2, p3};
+  wasm_valtype_t* rs[2] = {r1, r2};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 3, ps);
+  wasm_valtype_vec_new(&results, 2, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+
+// Value construction short-hands
+
+static inline void wasm_val_init_ptr(own wasm_val_t* out, void* p) {
+#if UINTPTR_MAX == UINT32_MAX
+  out->kind = WASM_I32;
+  out->of.i32 = (intptr_t)p;
+#elif UINTPTR_MAX == UINT64_MAX
+  out->kind = WASM_I64;
+  out->of.i64 = (intptr_t)p;
+#endif
+}
+
+static inline void* wasm_val_ptr(const wasm_val_t* val) {
+#if UINTPTR_MAX == UINT32_MAX
+  return (void*)(intptr_t)val->of.i32;
+#elif UINTPTR_MAX == UINT64_MAX
+  return (void*)(intptr_t)val->of.i64;
+#endif
+}
+
+#define WASM_I32_VAL(i) {.kind = WASM_I32, .of = {.i32 = i}}
+#define WASM_I64_VAL(i) {.kind = WASM_I64, .of = {.i64 = i}}
+#define WASM_F32_VAL(z) {.kind = WASM_F32, .of = {.f32 = z}}
+#define WASM_F64_VAL(z) {.kind = WASM_F64, .of = {.f64 = z}}
+#define WASM_REF_VAL(r) {.kind = WASM_EXTERNREF, .of = {.ref = r}}
+#define WASM_INIT_VAL {.kind = WASM_EXTERNREF, .of = {.ref = NULL}}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#undef own
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // #ifdef WASM_H
diff --git a/crates/c-api/include/wasm.hh b/crates/c-api/include/wasm.hh
new file mode 100644
index 000000000..e03408241
--- /dev/null
+++ b/crates/c-api/include/wasm.hh
@@ -0,0 +1,849 @@
+// Wasmtime-vendored copy of this header file as present in upstream:
+// <https://github.com/WebAssembly/wasm-c-api/blob/2ce1367c9d1271c83fb63bef26d896a2f290cd23/include/wasm.hh>
+//
+// Wasmtime maintainers can update this vendored copy in-tree with the
+// ./ci/vendor-c-api-headers.sh shell script.
+//
+// WebAssembly C++ API
+
+#ifndef WASM_HH
+#define WASM_HH
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <limits>
+#include <string>
+
+#ifndef WASM_API_EXTERN
+#if defined(_WIN32) && !defined(__MINGW32__) && !defined(LIBWASM_STATIC)
+#define WASM_API_EXTERN __declspec(dllimport)
+#else
+#define WASM_API_EXTERN
+#endif
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Auxiliaries
+
+// Machine types
+
+static_assert(sizeof(float) == sizeof(int32_t), "incompatible float type");
+static_assert(sizeof(double) == sizeof(int64_t), "incompatible double type");
+static_assert(sizeof(intptr_t) == sizeof(int32_t) ||
+              sizeof(intptr_t) == sizeof(int64_t), "incompatible pointer type");
+
+using byte_t = char;
+using float32_t = float;
+using float64_t = double;
+
+
+namespace wasm {
+
+// Vectors
+
+template<class T>
+class vec {
+  static const size_t invalid_size = SIZE_MAX;
+
+  size_t size_;
+  std::unique_ptr<T[]> data_;
+
+#ifdef WASM_API_DEBUG
+  WASM_API_EXTERN void make_data();
+  WASM_API_EXTERN void free_data();
+#else
+  void make_data() {}
+  void free_data() {}
+#endif
+
+  vec(size_t size) : vec(size, size ? new(std::nothrow) T[size] : nullptr) {
+    make_data();
+  }
+
+  vec(size_t size, T* data) : size_(size), data_(data) {
+    assert(!!size_ == !!data_ || size_ == invalid_size);
+  }
+
+public:
+  using elem_type = T;
+
+  vec(vec<T>&& that) : vec(that.size_, that.data_.release()) {
+    that.size_ = invalid_size;
+  }
+
+  ~vec() {
+    free_data();
+  }
+
+  operator bool() const {
+    return bool(size_ != invalid_size);
+  }
+
+  auto size() const -> size_t {
+    return size_;
+  }
+
+  auto get() const -> const T* {
+    return data_.get();
+  }
+
+  auto get() -> T* {
+    return data_.get();
+  }
+
+  auto release() -> T* {
+    size_ = invalid_size;
+    return data_.release();
+  }
+
+  void reset() {
+    free_data();
+    size_ = invalid_size;
+    data_.reset();
+  }
+
+  void reset(vec& that) {
+    free_data();
+    size_ = that.size_;
+    data_.reset(that.data_.release());
+    that.size_ = invalid_size;
+  }
+
+  auto operator=(vec&& that) -> vec& {
+    reset(that);
+    return *this;
+  }
+
+  auto operator[](size_t i) -> T& {
+    assert(i < size_);
+    return data_[i];
+  }
+
+  auto operator[](size_t i) const -> const T& {
+    assert(i < size_);
+    return data_[i];
+  }
+
+  auto copy() const -> vec {
+    auto v = vec(size_);
+    if (v) for (size_t i = 0; i < size_; ++i) v.data_[i] = data_[i];
+    return v;
+  }
+
+  // TODO: This can't be used for e.g. vec<Val>
+  auto deep_copy() const -> vec {
+    auto v = vec(size_);
+    if (v) for (size_t i = 0; i < size_; ++i) v.data_[i] = data_[i]->copy();
+    return v;
+  }
+
+  static auto make_uninitialized(size_t size = 0) -> vec {
+    return vec(size);
+  }
+
+  static auto make(size_t size, T init[]) -> vec {
+    auto v = vec(size);
+    if (v) for (size_t i = 0; i < size; ++i) v.data_[i] = std::move(init[i]);
+    return v;
+  }
+
+  static auto make(std::string s) -> vec<char> {
+    auto v = vec(s.length());
+    if (v) std::strncpy(v.get(), s.data(), s.length());
+    return v;
+  }
+
+  static auto make_nt(std::string s) -> vec<char> {
+    auto v = vec(s.length() + 1);
+    if (v) std::strcpy(v.get(), s.data());
+    return v;
+  }
+
+  // TODO(mvsc): MVSC requires this special case:
+  static auto make() -> vec {
+    return vec(0);
+  }
+
+  template<class... Ts>
+  static auto make(Ts&&... args) -> vec {
+    T data[] = { std::forward<Ts>(args)... };
+    return make(sizeof...(Ts), data);
+  }
+
+  static auto adopt(size_t size, T data[]) -> vec {
+    return vec(size, data);
+  }
+
+  static auto invalid() -> vec {
+    return vec(invalid_size, nullptr);
+  }
+};
+
+
+// Ownership
+
+class destroyer {
+public:
+  template <typename T>
+  void operator()(T* ptr) {
+    ptr->destroy();
+  }
+};
+
+template<class T> using own = std::unique_ptr<T, destroyer>;
+template<class T> using ownvec = vec<own<T>>;
+
+template<class T> own<T> make_own(T* ptr) {
+  return own<T>(ptr);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Runtime Environment
+
+// Configuration
+
+class WASM_API_EXTERN Config {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Config() = default;
+  ~Config() = default;
+
+public:
+  static auto make() -> own<Config>;
+
+  // Implementations may provide custom methods for manipulating Configs.
+};
+
+
+// Engine
+
+class WASM_API_EXTERN Engine {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Engine() = default;
+  ~Engine() = default;
+
+public:
+  static auto make(own<Config>&& = Config::make()) -> own<Engine>;
+};
+
+
+// Store
+
+class WASM_API_EXTERN Store {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Store() = default;
+  ~Store() = default;
+
+public:
+  static auto make(Engine*) -> own<Store>;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Type Representations
+
+// Type attributes
+
+enum class Mutability : uint8_t { CONST, VAR };
+
+struct Limits {
+  uint32_t min;
+  uint32_t max;
+
+  Limits(uint32_t min, uint32_t max = std::numeric_limits<uint32_t>::max()) :
+    min(min), max(max) {}
+};
+
+
+// Value Types
+
+enum class ValKind : uint8_t {
+  I32, I64, F32, F64,
+  EXTERNREF = 128, FUNCREF,
+};
+
+inline bool is_num(ValKind k) { return k < ValKind::EXTERNREF; }
+inline bool is_ref(ValKind k) { return k >= ValKind::EXTERNREF; }
+
+
+class WASM_API_EXTERN ValType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  ValType() = default;
+  ~ValType() = default;
+
+public:
+  static auto make(ValKind) -> own<ValType>;
+  auto copy() const -> own<ValType>;
+
+  auto kind() const -> ValKind;
+  auto is_num() const -> bool { return wasm::is_num(kind()); }
+  auto is_ref() const -> bool { return wasm::is_ref(kind()); }
+};
+
+
+// External Types
+
+enum class ExternKind : uint8_t {
+  FUNC, GLOBAL, TABLE, MEMORY
+};
+
+class FuncType;
+class GlobalType;
+class TableType;
+class MemoryType;
+
+class WASM_API_EXTERN ExternType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  ExternType() = default;
+  ~ExternType() = default;
+
+public:
+  auto copy() const-> own<ExternType>;
+
+  auto kind() const -> ExternKind;
+
+  auto func() -> FuncType*;
+  auto global() -> GlobalType*;
+  auto table() -> TableType*;
+  auto memory() -> MemoryType*;
+
+  auto func() const -> const FuncType*;
+  auto global() const -> const GlobalType*;
+  auto table() const -> const TableType*;
+  auto memory() const -> const MemoryType*;
+};
+
+
+// Function Types
+
+class WASM_API_EXTERN FuncType : public ExternType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  FuncType() = default;
+  ~FuncType() = default;
+
+public:
+  static auto make(
+    ownvec<ValType>&& params = ownvec<ValType>::make(),
+    ownvec<ValType>&& results = ownvec<ValType>::make()
+  ) -> own<FuncType>;
+
+  auto copy() const -> own<FuncType>;
+
+  auto params() const -> const ownvec<ValType>&;
+  auto results() const -> const ownvec<ValType>&;
+};
+
+
+// Global Types
+
+class WASM_API_EXTERN GlobalType : public ExternType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  GlobalType() = default;
+  ~GlobalType() = default;
+
+public:
+  static auto make(own<ValType>&&, Mutability) -> own<GlobalType>;
+  auto copy() const -> own<GlobalType>;
+
+  auto content() const -> const ValType*;
+  auto mutability() const -> Mutability;
+};
+
+
+// Table Types
+
+class WASM_API_EXTERN TableType : public ExternType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  TableType() = default;
+  ~TableType() = default;
+
+public:
+  static auto make(own<ValType>&&, Limits) -> own<TableType>;
+  auto copy() const -> own<TableType>;
+
+  auto element() const -> const ValType*;
+  auto limits() const -> const Limits&;
+};
+
+
+// Memory Types
+
+class WASM_API_EXTERN MemoryType : public ExternType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  MemoryType() = default;
+  ~MemoryType() = default;
+
+public:
+  static auto make(Limits) -> own<MemoryType>;
+  auto copy() const -> own<MemoryType>;
+
+  auto limits() const -> const Limits&;
+};
+
+
+// Import Types
+
+using Name = vec<byte_t>;
+
+class WASM_API_EXTERN ImportType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  ImportType() = default;
+  ~ImportType() = default;
+
+public:
+  static auto make(Name&& module, Name&& name, own<ExternType>&&) ->
+    own<ImportType>;
+  auto copy() const -> own<ImportType>;
+
+  auto module() const -> const Name&;
+  auto name() const -> const Name&;
+  auto type() const -> const ExternType*;
+};
+
+
+// Export Types
+
+class WASM_API_EXTERN ExportType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  ExportType() = default;
+  ~ExportType() = default;
+
+public:
+  static auto make(Name&&, own<ExternType>&&) -> own<ExportType>;
+  auto copy() const -> own<ExportType>;
+
+  auto name() const -> const Name&;
+  auto type() const -> const ExternType*;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Runtime Objects
+
+// References
+
+class WASM_API_EXTERN Ref {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Ref() = default;
+  ~Ref() = default;
+
+public:
+  auto copy() const -> own<Ref>;
+  auto same(const Ref*) const -> bool;
+
+  auto get_host_info() const -> void*;
+  void set_host_info(void* info, void (*finalizer)(void*) = nullptr);
+};
+
+
+// Values
+
+class Val {
+  ValKind kind_;
+  union impl {
+    int32_t i32;
+    int64_t i64;
+    float32_t f32;
+    float64_t f64;
+    Ref* ref;
+  } impl_;
+
+  Val(ValKind kind, impl impl) : kind_(kind), impl_(impl) {}
+
+public:
+  Val() : kind_(ValKind::EXTERNREF) { impl_.ref = nullptr; }
+  explicit Val(int32_t i) : kind_(ValKind::I32) { impl_.i32 = i; }
+  explicit Val(int64_t i) : kind_(ValKind::I64) { impl_.i64 = i; }
+  explicit Val(float32_t z) : kind_(ValKind::F32) { impl_.f32 = z; }
+  explicit Val(float64_t z) : kind_(ValKind::F64) { impl_.f64 = z; }
+  explicit Val(own<Ref>&& r) : kind_(ValKind::EXTERNREF) { impl_.ref = r.release(); }
+
+  Val(Val&& that) : kind_(that.kind_), impl_(that.impl_) {
+    if (is_ref()) that.impl_.ref = nullptr;
+  }
+
+  ~Val() {
+    reset();
+  }
+
+  auto is_num() const -> bool { return wasm::is_num(kind_); }
+  auto is_ref() const -> bool { return wasm::is_ref(kind_); }
+
+  static auto i32(int32_t x) -> Val { return Val(x); }
+  static auto i64(int64_t x) -> Val { return Val(x); }
+  static auto f32(float32_t x) -> Val { return Val(x); }
+  static auto f64(float64_t x) -> Val { return Val(x); }
+  static auto ref(own<Ref>&& x) -> Val { return Val(std::move(x)); }
+  template<class T> inline static auto make(T x) -> Val;
+  template<class T> inline static auto make(own<T>&& x) -> Val;
+
+  void reset() {
+    if (is_ref() && impl_.ref) {
+      destroyer()(impl_.ref);
+      impl_.ref = nullptr;
+    }
+  }
+
+  void reset(Val& that) {
+    reset();
+    kind_ = that.kind_;
+    impl_ = that.impl_;
+    if (is_ref()) that.impl_.ref = nullptr;
+  }
+
+  auto operator=(Val&& that) -> Val& {
+    reset(that);
+    return *this;
+  }
+
+  auto kind() const -> ValKind { return kind_; }
+  auto i32() const -> int32_t { assert(kind_ == ValKind::I32); return impl_.i32; }
+  auto i64() const -> int64_t { assert(kind_ == ValKind::I64); return impl_.i64; }
+  auto f32() const -> float32_t { assert(kind_ == ValKind::F32); return impl_.f32; }
+  auto f64() const -> float64_t { assert(kind_ == ValKind::F64); return impl_.f64; }
+  auto ref() const -> Ref* { assert(is_ref()); return impl_.ref; }
+  template<class T> inline auto get() const -> T;
+
+  auto release_ref() -> own<Ref> {
+    assert(is_ref());
+    auto ref = impl_.ref;
+    impl_.ref = nullptr;
+    return own<Ref>(ref);
+  }
+
+  auto copy() const -> Val {
+    if (is_ref() && impl_.ref != nullptr) {
+      // TODO(mvsc): MVSC cannot handle this:
+      // impl impl = {.ref = impl_.ref->copy().release()};
+      impl impl;
+      impl.ref = impl_.ref->copy().release();
+      return Val(kind_, impl);
+    } else {
+      return Val(kind_, impl_);
+    }
+  }
+};
+
+
+template<> inline auto Val::make<int32_t>(int32_t x) -> Val { return Val(x); }
+template<> inline auto Val::make<int64_t>(int64_t x) -> Val { return Val(x); }
+template<> inline auto Val::make<float32_t>(float32_t x) -> Val { return Val(x); }
+template<> inline auto Val::make<float64_t>(float64_t x) -> Val { return Val(x); }
+template<> inline auto Val::make<Ref>(own<Ref>&& x) -> Val {
+  return Val(std::move(x));
+}
+
+template<> inline auto Val::make<uint32_t>(uint32_t x) -> Val {
+  return Val(static_cast<int32_t>(x));
+}
+template<> inline auto Val::make<uint64_t>(uint64_t x) -> Val {
+  return Val(static_cast<int64_t>(x));
+}
+
+template<> inline auto Val::get<int32_t>() const -> int32_t { return i32(); }
+template<> inline auto Val::get<int64_t>() const -> int64_t { return i64(); }
+template<> inline auto Val::get<float32_t>() const -> float32_t { return f32(); }
+template<> inline auto Val::get<float64_t>() const -> float64_t { return f64(); }
+template<> inline auto Val::get<Ref*>() const -> Ref* { return ref(); }
+
+template<> inline auto Val::get<uint32_t>() const -> uint32_t {
+  return static_cast<uint32_t>(i32());
+}
+template<> inline auto Val::get<uint64_t>() const -> uint64_t {
+  return static_cast<uint64_t>(i64());
+}
+
+
+// Traps
+
+using Message = vec<byte_t>;  // null terminated
+
+class Instance;
+
+class WASM_API_EXTERN Frame {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Frame() = default;
+  ~Frame() = default;
+
+public:
+  auto copy() const -> own<Frame>;
+
+  auto instance() const -> Instance*;
+  auto func_index() const -> uint32_t;
+  auto func_offset() const -> size_t;
+  auto module_offset() const -> size_t;
+};
+
+class WASM_API_EXTERN Trap : public Ref {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Trap() = default;
+  ~Trap() = default;
+
+public:
+  static auto make(Store*, const Message& msg) -> own<Trap>;
+  auto copy() const -> own<Trap>;
+
+  auto message() const -> Message;
+  auto origin() const -> own<Frame>;  // may be null
+  auto trace() const -> ownvec<Frame>;  // may be empty, origin first
+};
+
+
+// Modules
+
+template<class T> class WASM_API_EXTERN Shared;
+
+class WASM_API_EXTERN Module : public Ref {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Module() = default;
+  ~Module() = default;
+
+public:
+  static auto validate(Store*, const vec<byte_t>& binary) -> bool;
+  static auto make(Store*, const vec<byte_t>& binary) -> own<Module>;
+  auto copy() const -> own<Module>;
+
+  auto imports() const -> ownvec<ImportType>;
+  auto exports() const -> ownvec<ExportType>;
+
+  auto share() const -> own<Shared<Module>>;
+  static auto obtain(Store*, const Shared<Module>*) -> own<Module>;
+
+  auto serialize() const -> vec<byte_t>;
+  static auto deserialize(Store*, const vec<byte_t>&) -> own<Module>;
+};
+
+
+// Shared objects
+
+template<>
+class WASM_API_EXTERN Shared<Module> {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Shared() = default;
+  ~Shared() = default;
+};
+
+
+// Foreign Objects
+
+class WASM_API_EXTERN Foreign : public Ref {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Foreign() = default;
+  ~Foreign() = default;
+
+public:
+  static auto make(Store*) -> own<Foreign>;
+  auto copy() const -> own<Foreign>;
+};
+
+
+// Externals
+
+class Func;
+class Global;
+class Table;
+class Memory;
+
+class WASM_API_EXTERN Extern : public Ref {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Extern() = default;
+  ~Extern() = default;
+
+public:
+  auto copy() const -> own<Extern>;
+
+  auto kind() const -> ExternKind;
+  auto type() const -> own<ExternType>;
+
+  auto func() -> Func*;
+  auto global() -> Global*;
+  auto table() -> Table*;
+  auto memory() -> Memory*;
+
+  auto func() const -> const Func*;
+  auto global() const -> const Global*;
+  auto table() const -> const Table*;
+  auto memory() const -> const Memory*;
+};
+
+
+// Function Instances
+
+class WASM_API_EXTERN Func : public Extern {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Func() = default;
+  ~Func() = default;
+
+public:
+  using callback = auto (*)(const vec<Val>&, vec<Val>&) -> own<Trap>;
+  using callback_with_env = auto (*)(void*, const vec<Val>&, vec<Val>&) -> own<Trap>;
+
+  static auto make(Store*, const FuncType*, callback) -> own<Func>;
+  static auto make(Store*, const FuncType*, callback_with_env,
+    void*, void (*finalizer)(void*) = nullptr) -> own<Func>;
+  auto copy() const -> own<Func>;
+
+  auto type() const -> own<FuncType>;
+  auto param_arity() const -> size_t;
+  auto result_arity() const -> size_t;
+
+  auto call(const vec<Val>&, vec<Val>&) const -> own<Trap>;
+};
+
+
+// Global Instances
+
+class WASM_API_EXTERN Global : public Extern {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Global() = default;
+  ~Global() = default;
+
+ public:
+  static auto make(Store*, const GlobalType*, const Val&) -> own<Global>;
+  auto copy() const -> own<Global>;
+
+  auto type() const -> own<GlobalType>;
+  auto get() const -> Val;
+  void set(const Val&);
+};
+
+
+// Table Instances
+
+class WASM_API_EXTERN Table : public Extern {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Table() = default;
+  ~Table() = default;
+
+public:
+  using size_t = uint32_t;
+
+  static auto make(
+    Store*, const TableType*, const Ref* init = nullptr) -> own<Table>;
+  auto copy() const -> own<Table>;
+
+  auto type() const -> own<TableType>;
+  auto get(size_t index) const -> own<Ref>;
+  auto set(size_t index, const Ref*) -> bool;
+  auto size() const -> size_t;
+  auto grow(size_t delta, const Ref* init = nullptr) -> bool;
+};
+
+
+// Memory Instances
+
+class WASM_API_EXTERN Memory : public Extern {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Memory() = default;
+  ~Memory() = default;
+
+public:
+  static auto make(Store*, const MemoryType*) -> own<Memory>;
+  auto copy() const -> own<Memory>;
+
+  using pages_t = uint32_t;
+
+  static const size_t page_size = 0x10000;
+
+  auto type() const -> own<MemoryType>;
+  auto data() const -> byte_t*;
+  auto data_size() const -> size_t;
+  auto size() const -> pages_t;
+  auto grow(pages_t delta) -> bool;
+};
+
+
+// Module Instances
+
+class WASM_API_EXTERN Instance : public Ref {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Instance() = default;
+  ~Instance() = default;
+
+public:
+  static auto make(
+    Store*, const Module*, const vec<Extern*>&, own<Trap>* = nullptr
+  ) -> own<Instance>;
+  auto copy() const -> own<Instance>;
+
+  auto exports() const -> ownvec<Extern>;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace wasm
+
+#endif  // #ifdef WASM_HH
diff --git a/crates/c-api/wasm-c-api b/crates/c-api/wasm-c-api
deleted file mode 160000
index c9d312846..000000000
--- a/crates/c-api/wasm-c-api
+++ /dev/null
@@ -1,1 +0,0 @@
-Subproject commit c9d31284651b975f05ac27cee0bab1377560b87e
diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs
index 821636a53..5875e0b8d 100644
--- a/crates/cli-flags/src/lib.rs
+++ b/crates/cli-flags/src/lib.rs
@@ -293,6 +293,10 @@ wasmtime_option_group! {
         pub udp: Option<bool>,
         /// Allows imports from the `wasi_unstable` core wasm module.
         pub preview0: Option<bool>,
+        /// Inherit all environment variables from the parent process.
+        ///
+        /// This option can be further overwritten with `--env` flags.
+        pub inherit_env: Option<bool>,
     }
 
     enum Wasi {
diff --git a/examples/async.cpp b/examples/async.cpp
index a5feae115..fac006a2a 100644
--- a/examples/async.cpp
+++ b/examples/async.cpp
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    c++ examples/async.cpp \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -std=c++11 \
        -lpthread -ldl -lm \
diff --git a/examples/fuel.c b/examples/fuel.c
index 945dc5bf5..521d05628 100644
--- a/examples/fuel.c
+++ b/examples/fuel.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/fuel.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o fuel
diff --git a/examples/gcd.c b/examples/gcd.c
index 9f7f45c0a..7447f8f4a 100644
--- a/examples/gcd.c
+++ b/examples/gcd.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/gcd.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o gcd
diff --git a/examples/hello.c b/examples/hello.c
index dc7343432..45ff62072 100644
--- a/examples/hello.c
+++ b/examples/hello.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/hello.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o hello
diff --git a/examples/interrupt.c b/examples/interrupt.c
index 13292f2ca..3866cec1a 100644
--- a/examples/interrupt.c
+++ b/examples/interrupt.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/interrupt.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o interrupt
diff --git a/examples/linking.c b/examples/linking.c
index d048451c2..d9741ab97 100644
--- a/examples/linking.c
+++ b/examples/linking.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/linking.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o linking
diff --git a/examples/memory.c b/examples/memory.c
index 3f4bf311e..2edca2198 100644
--- a/examples/memory.c
+++ b/examples/memory.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/memory.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o memory
diff --git a/examples/multi.c b/examples/multi.c
index 6edcdd18b..af1e61157 100644
--- a/examples/multi.c
+++ b/examples/multi.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/multi.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o multi
diff --git a/examples/multimemory.c b/examples/multimemory.c
index 88f1f5752..8fa4973e7 100644
--- a/examples/multimemory.c
+++ b/examples/multimemory.c
@@ -6,7 +6,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/multimemory.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o multimemory
diff --git a/examples/serialize.c b/examples/serialize.c
index 1d3acc7b3..65fb7263a 100644
--- a/examples/serialize.c
+++ b/examples/serialize.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/hello.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o hello
diff --git a/examples/threads.c b/examples/threads.c
index 7e13b9f3a..d89480db6 100644
--- a/examples/threads.c
+++ b/examples/threads.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/threads.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o threads
diff --git a/examples/wasi/main.c b/examples/wasi/main.c
index 753d66497..520259d53 100644
--- a/examples/wasi/main.c
+++ b/examples/wasi/main.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/wasi/main.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o wasi
diff --git a/src/commands/run.rs b/src/commands/run.rs
index 8f25ba91e..10c97649a 100644
--- a/src/commands/run.rs
+++ b/src/commands/run.rs
@@ -740,6 +740,11 @@ impl RunCommand {
         let mut builder = WasiCtxBuilder::new();
         builder.inherit_stdio().args(&self.compute_argv()?)?;
 
+        if self.run.common.wasi.inherit_env == Some(true) {
+            for (k, v) in std::env::vars() {
+                builder.env(&k, &v)?;
+            }
+        }
         for (key, value) in self.vars.iter() {
             let value = match value {
                 Some(value) => value.clone(),
@@ -779,6 +784,11 @@ impl RunCommand {
         let mut builder = wasmtime_wasi::WasiCtxBuilder::new();
         builder.inherit_stdio().args(&self.compute_argv()?);
 
+        if self.run.common.wasi.inherit_env == Some(true) {
+            for (k, v) in std::env::vars() {
+                builder.env(&k, &v);
+            }
+        }
         for (key, value) in self.vars.iter() {
             let value = match value {
                 Some(value) => value.clone(),


Test patch:
diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs
index 8fab74ac6..8fa270135 100644
--- a/tests/all/cli_tests.rs
+++ b/tests/all/cli_tests.rs
@@ -521,6 +521,15 @@ fn specify_env() -> Result<()> {
         .output()?;
     assert!(output.status.success());
 
+    // Inherit all env vars
+    let output = get_wasmtime_command()?
+        .args(&["run", "-Sinherit-env", "tests/all/cli_tests/print_env.wat"])
+        .env("FOO", "bar")
+        .output()?;
+    assert!(output.status.success());
+    let stdout = String::from_utf8_lossy(&output.stdout);
+    assert!(stdout.contains("FOO=bar"), "bad output: {stdout}");
+
     Ok(())
 }
 
diff --git a/tests/spec_testsuite b/tests/spec_testsuite
index 5a1a59060..16a839d56 160000
--- a/tests/spec_testsuite
+++ b/tests/spec_testsuite
@@ -1,1 +1,1 @@
-Subproject commit 5a1a590603d81f40ef471abba70a90a9ae5f4627
+Subproject commit 16a839d5601c283541a84572b47637f035b51437