Skip to content

Commit

Permalink
Implement C++ API and reimplement C on top (#7)
Browse files Browse the repository at this point in the history
* Add a C++ version of the API
* Refactor wasm-v8 to implement C++ API, which is safer
* Reimplement C API on top of C++ API
* Various fixes, additions, and clean-ups
* Some minor changes to C API:
  * Enforce `const` correctness in various places
  * Replace `init`, `deinit` global functions with `engine` object to be created
  * Store creation requires `engine` argument
  * Add `wasm_*_vec_new_empty` functions for creating *own* vectors of size 0
  * Changed `wasm_limits_t` from `size_t` to `uint32_t`
  * Changed `wasm_val_t` from `uint32_t`, `uint64_t` to `int32_t`, `int64_t`
  * Removed `wasm_numkind_t`, `wasm_refkind_t` and `wasm_numtype_t` `wasm_reftype_t` type aliases
  * Renamed `wasm_valkind_is_numkind`, `wasm_valkind_is_refkind` to `wasm_valkind_is_num`, `wasm_valkind_is_ref` and `wasm_valtype_is_numtype`, `wasm_valtype_is_reftype` to `wasm_valtype_is_num`, `wasm_valtype_is_ref`
  * Removed `wasm_ref_null` and `wasm_ref_is_null` (use NULL pointer)
  * Extended `wasm_func_new_with_env` function to take finalizer for env (which may be NULL)
  * Turned `wasm_extern_t` into an abstract type `wasm_external_t` and added conversion functions
  * Removed `wasm_instance_export` function
* Add comprehensive TODO list to README
  • Loading branch information
rossberg authored Jun 13, 2018
1 parent ea06297 commit 26eac1b
Show file tree
Hide file tree
Showing 11 changed files with 2,914 additions and 1,250 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
out
v8
54 changes: 30 additions & 24 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
CFLAGS = -ggdb
CXXFLAGS = ${CFLAGS} -fsanitize=address
LDFLAGS = -fsanitize-memory-track-origins -fsanitize-memory-use-after-dtor

OUT_DIR = out
WASM_DIR = .
EXAMPLE_DIR = example
Expand All @@ -15,8 +19,8 @@ WASM_INTERPRETER = ../spec.master/interpreter/wasm # change as needed

WASM_INCLUDE = ${WASM_DIR}/include
WASM_SRC = ${WASM_DIR}/src
WASM_OUT = ${OUT_DIR}/${WASM_SRC}
WASM_LIBS = wasm-v8 wasm-bin wasm-v8-lowlevel
WASM_OUT = ${OUT_DIR}/${WASM_DIR}
WASM_LIBS = wasm-c wasm-v8 wasm-bin
WASM_O = ${WASM_LIBS:%=${WASM_OUT}/%.o}

V8_BUILD = ${V8_ARCH}.${V8_MODE}
Expand All @@ -31,20 +35,30 @@ V8_ICU_LIBS = uc i18n
V8_OTHER_LIBS = src/inspector/libinspector
V8_BIN = natives_blob snapshot_blob snapshot_blob_trusted

CFLAGS =
CXXFLAGS = ${CFLAGS}

# Example

.PHONY: example
example: ${EXAMPLE_OUT}/${EXAMPLE_NAME} ${V8_BIN:%=${EXAMPLE_OUT}/%.bin} ${EXAMPLE_WAT:%=${EXAMPLE_OUT}/%.wasm}
cd ${EXAMPLE_OUT}; ./${EXAMPLE_NAME}
.PHONY: examples c cc
examples: c cc
@echo ==== Done ====

c: ${EXAMPLE_OUT}/${EXAMPLE_NAME}-c ${V8_BIN:%=${EXAMPLE_OUT}/%.bin} ${EXAMPLE_WAT:%=${EXAMPLE_OUT}/%.wasm}
@echo ==== C ====; \
cd ${EXAMPLE_OUT}; ./${EXAMPLE_NAME}-c

cc: ${EXAMPLE_OUT}/${EXAMPLE_NAME}-cc ${V8_BIN:%=${EXAMPLE_OUT}/%.bin} ${EXAMPLE_WAT:%=${EXAMPLE_OUT}/%.wasm}
@echo ==== C++ ====; \
cd ${EXAMPLE_OUT}; ./${EXAMPLE_NAME}-cc

${EXAMPLE_OUT}/${EXAMPLE_NAME}.o: ${EXAMPLE_DIR}/${EXAMPLE_NAME}.c ${WASM_INCLUDE}/wasm.h ${EXAMPLE_OUT}
clang ${CFLAGS} -c -I. -I${V8_INCLUDE} -I${WASM_INCLUDE} $< -o $@
${EXAMPLE_OUT}/${EXAMPLE_NAME}.c.o: ${EXAMPLE_DIR}/${EXAMPLE_NAME}.c ${WASM_INCLUDE}/wasm.h
mkdir -p ${EXAMPLE_OUT}
clang -c ${CFLAGS} -I. -I${V8_INCLUDE} -I${WASM_INCLUDE} $< -o $@

${EXAMPLE_OUT}/${EXAMPLE_NAME}: ${EXAMPLE_OUT}/${EXAMPLE_NAME}.o ${WASM_O}
clang++ ${CXXFLAGS} $< -o $@ \
${EXAMPLE_OUT}/${EXAMPLE_NAME}.cc.o: ${EXAMPLE_DIR}/${EXAMPLE_NAME}.cc ${WASM_INCLUDE}/wasm.hh
mkdir -p ${EXAMPLE_OUT}
clang++ -c -std=c++14 ${CXXFLAGS} -I. -I${V8_INCLUDE} -I${WASM_INCLUDE} $< -o $@

${EXAMPLE_OUT}/${EXAMPLE_NAME}-%: ${EXAMPLE_OUT}/${EXAMPLE_NAME}.%.o ${WASM_O}
clang++ ${CXXFLAGS} ${LDFLAGS} $< -o $@ \
${V8_LIBS:%=${V8_OUT}/obj/libv8_%.a} \
${V8_ICU_LIBS:%=${V8_OUT}/obj/third_party/icu/libicu%.a} \
${V8_OTHER_LIBS:%=${V8_OUT}/obj/%.a} \
Expand All @@ -60,23 +74,15 @@ ${EXAMPLE_OUT}/%.wasm: ${EXAMPLE_DIR}/%.wasm
%.wasm: %.wat
${WASM_INTERPRETER} -d $< -o $@

${EXAMPLE_OUT}: ${OUT_DIR}
mkdir -p $@


# Wasm C API

.PHONY: wasm
wasm: ${WASM_LIBS:%=%.o}

${WASM_O}: ${WASM_OUT}/%.o: ${WASM_SRC}/%.cc ${WASM_OUT}
clang++ ${CXXFLAGS} -c -I. -I${V8_INCLUDE} -I${V8_SRC} -I${V8_V8} -I${V8_OUT}/gen -I${WASM_INCLUDE} -I${WASM_SRC} $< -o $@ -std=c++0x

${WASM_OUT}: ${OUT_DIR}
mkdir -p $@
wasm: ${WASM_LIBS:%=${WASM_OUT}/%.o}

${OUT_DIR}:
mkdir -p $@
${WASM_O}: ${WASM_OUT}/%.o: ${WASM_SRC}/%.cc
mkdir -p ${WASM_OUT}
clang++ -c -std=c++14 ${CXXFLAGS} -I. -I${V8_INCLUDE} -I${V8_SRC} -I${V8_V8} -I${V8_OUT}/gen -I${WASM_INCLUDE} -I${WASM_SRC} $< -o $@


# V8
Expand Down
45 changes: 42 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,50 @@
# WebAssembly C API
# WebAssembly C and C++ API

Work in progress. No docs yet.

* See `example/hello.c` for example usage.
* C API:

* See `include/wasm.h` for interface.
* See `example/hello.c` for example usage.

* See `include/wasm.h` for interface.

* C++ API:

* See `example/hello.cc` for example usage.

* See `include/wasm.hh` for interface.

* A half-complete implementation based on V8 is in `src`.

* C API is build on top of C++ API.

* See `Makefile` for build recipe.

* TODO in V8 implementation:

* Replace use of JS API with V8 internal

* Implement missing functionality through V8 internals

* global::get, global::set
* table::get, table::set, table::size, table::grow
* memory::data, memory::data_size, memory::size, memory::grow
* module::serialize, module::deserialize
* multiple return values

* Simplify reference wrappers to be plain persistent handles

* Move host information to V8 object (func callback & env)
* Compute reflection on demand

* Possible API tweaks:

* Find a way to perform C callbacks through C++ without extra wrapper?

* Possible renamings?

* `externkind`, `externtype` to `externalkind`, `externaltype`?
* `memtype` to `memorytype`?
* CamlCase class names in C++ API?

* Add iterators to `vec` class?
29 changes: 17 additions & 12 deletions example/hello.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ own wasm_val_vec_t print_wasm(wasm_val_vec_t args) {
int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
wasm_init(argc, argv);
wasm_store_t* store = wasm_store_new();
wasm_engine_t* engine = wasm_engine_new(argc, argv);
wasm_store_t* store = wasm_store_new(engine);

// Load binary.
printf("Loading binary...\n");
Expand Down Expand Up @@ -90,43 +90,48 @@ int main(int argc, const char* argv[]) {

// Instantiate.
printf("Instantiating module...\n");
wasm_extern_t imports[] = {
wasm_extern_func(print_func1), wasm_extern_func(print_func2)
const wasm_external_t* imports[] = {
wasm_func_as_external(print_func1), wasm_func_as_external(print_func2)
};
own wasm_instance_t* instance = wasm_instance_new(store, module, wasm_extern_vec(2, imports));
own wasm_instance_t* instance = wasm_instance_new(store, module, wasm_external_vec_const(2, imports));
if (!instance) {
printf("> Error instantiating module!\n");
return 1;
}

// Extract export.
printf("Extracting exports...\n");
own wasm_extern_t exp = wasm_instance_export(instance, 0);
if (exp.kind != WASM_EXTERN_FUNC || exp.func == NULL) {
printf("> Error accessing export!");
own wasm_external_vec_t exports = wasm_instance_exports(instance);
if (exports.size == 0) {
printf("> Error accessing exports!\n");
return 1;
}
const wasm_func_t* run_func = wasm_external_as_func(exports.data[0]);
if (run_func == NULL) {
printf("> Error accessing export!\n");
return 1;
}
own wasm_func_t* run_func = exp.func;

wasm_module_delete(module);
wasm_instance_delete(instance);

// Call.
printf("Calling exports...\n");
wasm_val_t args[] = {wasm_i32_val(3), wasm_i32_val(4)};
wasm_val_t args[] = { wasm_i32_val(3), wasm_i32_val(4) };
own wasm_val_vec_t results = wasm_func_call(run_func, wasm_val_vec(2, args));

wasm_external_vec_delete(exports);

// Print result.
printf("Printing result...\n");
printf("> %u\n", results.data[0].i32);

wasm_func_delete(run_func);
wasm_val_vec_delete(results);

// Shut down.
printf("Shutting down...\n");
wasm_store_delete(store);
wasm_deinit();
wasm_engine_delete(engine);

// All done.
printf("Done.\n");
Expand Down
127 changes: 127 additions & 0 deletions example/hello.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <cinttypes>

#include "wasm.hh"

// Print a Wasm value
void val_print(const wasm::val& val) {
switch (val.kind()) {
case wasm::I32: {
std::cout << val.i32();
} break;
case wasm::I64: {
std::cout << val.i64();
} break;
case wasm::F32: {
std::cout << val.f32();
} break;
case wasm::F64: {
std::cout << val.f64();
} break;
case wasm::ANYREF:
case wasm::FUNCREF: {
if (val.ref() == nullptr) {
std::cout << "null";
} else {
std::cout << "ref(" << val.ref() << ")";
}
} break;
}
}

// A function to be called from Wasm code.
auto print_wasm(const wasm::vec<wasm::val>& args) -> wasm::vec<wasm::val> {
std::cout << "Calling back..." << std::endl << ">";
for (size_t i = 0; i < args.size(); ++i) {
std::cout << " ";
val_print(args[i]);
}
std::cout << std::endl;

int32_t n = args.size();
return wasm::vec<wasm::val>::make(wasm::val(n));
}


void run(int argc, const char* argv[]) {
// Initialize.
std::cout << "Initializing..." << std::endl;
auto engine = wasm::engine::make(argc, argv);
auto store = wasm::store::make(engine);

// Load binary.
std::cout << "Loading binary..." << std::endl;
std::ifstream file("hello.wasm");
file.seekg(0, std::ios_base::end);
auto file_size = file.tellg();
file.seekg(0);
auto binary = wasm::vec<byte_t>::make_uninitialized(file_size);
file.read(binary.get(), file_size);
file.close();
if (file.fail()) {
std::cout << "> Error loading module!" << std::endl;
return;
}

// Compile.
std::cout << "Compiling module..." << std::endl;
auto module = wasm::module::make(store, binary);
if (!module) {
std::cout << "> Error compiling module!" << std::endl;
return;
}

// Create external print functions.
std::cout << "Creating callbacks..." << std::endl;
auto print_type1 = wasm::functype::make(
wasm::vec<wasm::valtype*>::make(wasm::valtype::make(wasm::I32)),
wasm::vec<wasm::valtype*>::make(wasm::valtype::make(wasm::I32))
);
auto print_func1 = wasm::func::make(store, print_type1, print_wasm);

auto print_type2 = wasm::functype::make(
wasm::vec<wasm::valtype*>::make(wasm::valtype::make(wasm::I32), wasm::valtype::make(wasm::I32)),
wasm::vec<wasm::valtype*>::make(wasm::valtype::make(wasm::I32))
);
auto print_func2 = wasm::func::make(store, print_type2, print_wasm);

// Instantiate.
std::cout << "Instantiating module..." << std::endl;
auto imports = wasm::vec<wasm::external*>::make(print_func1, print_func2);
auto instance = wasm::instance::make(store, module, imports);
if (!instance) {
std::cout << "> Error instantiating module!" << std::endl;
return;
}

// Extract export.
std::cout << "Extracting exports..." << std::endl;
auto exports = instance->exports();
if (exports.size() == 0 || exports[0]->kind() != wasm::EXTERN_FUNC || !exports[0]->func()) {
std::cout << "> Error accessing export!" << std::endl;
return;
}
auto run_func = exports[0]->func();

// Call.
std::cout << "Calling exports..." << std::endl;
auto results = run_func->call(wasm::val(3), wasm::val(4));

// Print result.
std::cout << "Printing result..." << std::endl;
std::cout << "> " << results[0].i32() << std::endl;

// Shut down.
std::cout << "Shutting down..." << std::endl;
}


int main(int argc, const char* argv[]) {
run(argc, argv);
std::cout << "Done." << std::endl;
return 0;
}

Loading

0 comments on commit 26eac1b

Please sign in to comment.