Skip to content

Commit 7c80d05

Browse files
authored
Initial WASM C API implementation. (#1250)
All tests except `threads` pass.
1 parent d663c80 commit 7c80d05

File tree

11 files changed

+1816
-45
lines changed

11 files changed

+1816
-45
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,7 @@ jobs:
3131
run: cmake --build out
3232
- name: unittests
3333
run: cmake --build out --target run-unittests
34+
- name: c-api-tests
35+
run: cmake --build out --target run-c-api-tests
3436
- name: tests
3537
run: cmake --build out --target run-tests

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@
77
[submodule "third_party/ply"]
88
path = third_party/ply
99
url = https://github.com/dabeaz/ply
10+
[submodule "third_party/wasm-c-api"]
11+
path = third_party/wasm-c-api
12+
url = https://github.com/WebAssembly/wasm-c-api

CMakeLists.txt

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${WABT_SOURCE_DIR}/cmake)
224224

225225
add_custom_target(everything)
226226

227-
add_library(wabt STATIC
227+
set(WABT_LIBRARY_SRC
228228
src/apply-names.h
229229
src/apply-names.cc
230230
src/binary.h
@@ -308,12 +308,27 @@ add_library(wabt STATIC
308308
# TODO(binji): Move this into its own library?
309309
src/interp/binary-reader-interp.h
310310
src/interp/binary-reader-interp.cc
311+
src/interp/binary-reader-metadata.h
312+
src/interp/binary-reader-metadata.cc
311313
src/interp/interp.h
312314
src/interp/interp.cc
313315
src/interp/interp-disassemble.cc
314316
src/interp/interp-trace.cc
315317
)
316318

319+
add_library(wabt STATIC ${WABT_LIBRARY_SRC})
320+
321+
# libwasm, which implenents the wasm C API
322+
add_library(wasm SHARED ${WABT_LIBRARY_SRC} src/interp/interp-wasm-c-api.cc)
323+
target_link_libraries(wasm wabt)
324+
target_include_directories(wasm PUBLIC third_party/wasm-c-api/include)
325+
if (COMPILER_IS_MSVC)
326+
target_compile_definitions(wasm PRIVATE "WASM_API_EXTERN=__declspec(dllexport)")
327+
else ()
328+
target_compile_options(wasm PRIVATE -Wno-old-style-cast)
329+
target_compile_definitions(wasm PRIVATE "WASM_API_EXTERN=__attribute__((visibility(\"default\")))")
330+
endif ()
331+
set_target_properties(wasm PROPERTIES CXX_VISIBILITY_PRESET hidden)
317332

318333
if (NOT EMSCRIPTEN)
319334
if (CODE_COVERAGE)
@@ -507,6 +522,7 @@ if (NOT EMSCRIPTEN)
507522
# See: https://github.com/actions/setup-python/issues/40
508523
find_package(PythonInterp REQUIRED)
509524
set(RUN_TESTS_PY ${WABT_SOURCE_DIR}/test/run-tests.py)
525+
510526
add_custom_target(run-tests
511527
COMMAND ${PYTHON_EXECUTABLE} ${RUN_TESTS_PY} --bindir $<TARGET_FILE_DIR:wat2wasm>
512528
DEPENDS ${WABT_EXECUTABLES}
@@ -521,7 +537,49 @@ if (NOT EMSCRIPTEN)
521537
${USES_TERMINAL}
522538
)
523539

524-
add_custom_target(check DEPENDS run-tests wabt-unittests)
540+
add_custom_target(run-c-api-tests
541+
COMMAND ${PYTHON_EXECUTABLE} ${WABT_SOURCE_DIR}/test/run-c-api-examples.py --bindir $<TARGET_FILE_DIR:wat2wasm>
542+
WORKING_DIRECTORY ${WABT_SOURCE_DIR}
543+
${USES_TERMINAL}
544+
)
545+
546+
add_custom_target(check DEPENDS run-unittests run-tests run-c-api-tests)
547+
548+
function(c_api_example NAME)
549+
set(EXENAME wasm-c-api-${NAME})
550+
add_executable(${EXENAME} third_party/wasm-c-api/example/${NAME}.c)
551+
if (NOT COMPILER_IS_MSVC)
552+
set_target_properties(${EXENAME} PROPERTIES COMPILE_FLAGS "-std=gnu11 -Wno-pointer-to-int-cast")
553+
endif ()
554+
target_link_libraries(${EXENAME} wasm Threads::Threads)
555+
add_custom_target(${EXENAME}-copy-to-bin ALL
556+
COMMAND ${CMAKE_COMMAND} -E make_directory ${WABT_SOURCE_DIR}/bin
557+
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${EXENAME}> ${WABT_SOURCE_DIR}/bin/
558+
COMMAND ${CMAKE_COMMAND} -E copy ${WABT_SOURCE_DIR}/third_party/wasm-c-api/example/${NAME}.wasm $<TARGET_FILE_DIR:${EXENAME}>/
559+
COMMAND ${CMAKE_COMMAND} -E copy ${WABT_SOURCE_DIR}/third_party/wasm-c-api/example/${NAME}.wasm ${WABT_SOURCE_DIR}/bin/
560+
DEPENDS ${EXENAME}
561+
)
562+
add_dependencies(run-c-api-tests ${EXENAME})
563+
endfunction()
564+
565+
c_api_example(callback)
566+
c_api_example(finalize)
567+
c_api_example(global)
568+
c_api_example(hello)
569+
c_api_example(hostref)
570+
c_api_example(multi)
571+
c_api_example(memory)
572+
c_api_example(reflect)
573+
c_api_example(serialize)
574+
c_api_example(start)
575+
c_api_example(table)
576+
c_api_example(trap)
577+
if (NOT WIN32)
578+
# depends on pthreads
579+
set(THREADS_PREFER_PTHREAD_FLAG ON)
580+
find_package(Threads REQUIRED)
581+
c_api_example(threads)
582+
endif ()
525583
endif ()
526584

527585
# install

src/interp/binary-reader-interp.cc

Lines changed: 59 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class BinaryReaderInterp : public BinaryReaderNop {
6060
BinaryReaderInterp(Environment* env,
6161
DefinedModule* module,
6262
std::unique_ptr<OutputBuffer> istream,
63+
const std::vector<Export*>& imports_,
6364
Errors* errors,
6465
const Features& features);
6566

@@ -323,11 +324,15 @@ class BinaryReaderInterp : public BinaryReaderNop {
323324
string_view name);
324325
wabt::Result FindRegisteredModule(string_view module_name,
325326
Module** out_module);
326-
wabt::Result GetModuleExport(Module* module,
327-
string_view field_name,
328-
Export** out_export);
327+
wabt::Result ResolveImport(Index import_index,
328+
ExternalKind kind,
329+
string_view module_name,
330+
string_view field_name,
331+
Index sig_index,
332+
Export** out_export);
329333

330334
Features features_;
335+
const std::vector<Export*>& imports_;
331336
Errors* errors_ = nullptr;
332337
Environment* env_ = nullptr;
333338
DefinedModule* module_ = nullptr;
@@ -363,9 +368,11 @@ class BinaryReaderInterp : public BinaryReaderNop {
363368
BinaryReaderInterp::BinaryReaderInterp(Environment* env,
364369
DefinedModule* module,
365370
std::unique_ptr<OutputBuffer> istream,
371+
const std::vector<Export*>& imports,
366372
Errors* errors,
367373
const Features& features)
368374
: features_(features),
375+
imports_(imports),
369376
errors_(errors),
370377
env_(env),
371378
module_(module),
@@ -765,16 +772,35 @@ wabt::Result BinaryReaderInterp::FindRegisteredModule(string_view module_name,
765772
return wabt::Result::Ok;
766773
}
767774

768-
wabt::Result BinaryReaderInterp::GetModuleExport(Module* module,
769-
string_view field_name,
770-
Export** out_export) {
771-
Export* export_ = module->GetExport(field_name);
772-
if (!export_) {
773-
PrintError("unknown module field \"" PRIstringview "\"",
774-
WABT_PRINTF_STRING_VIEW_ARG(field_name));
775-
return wabt::Result::Error;
775+
wabt::Result BinaryReaderInterp::ResolveImport(Index import_index,
776+
ExternalKind kind,
777+
string_view module_name,
778+
string_view field_name,
779+
Index sig_index,
780+
Export** out_export) {
781+
Export* export_ = nullptr;
782+
if (!imports_.empty()) {
783+
export_ = imports_[import_index];
784+
} else {
785+
Module* module;
786+
CHECK_RESULT(FindRegisteredModule(module_name, &module));
787+
788+
// Func imports get special handled due to the face that they can be
789+
// overloaded on signature.
790+
if (kind == ExternalKind::Func) {
791+
export_ = module->GetFuncExport(env_, field_name, sig_index);
792+
}
793+
if (!export_) {
794+
export_ = module->GetExport(field_name);
795+
if (!export_) {
796+
PrintError("unknown module field \"" PRIstringview "\"",
797+
WABT_PRINTF_STRING_VIEW_ARG(field_name));
798+
return wabt::Result::Error;
799+
}
800+
}
776801
}
777802

803+
CHECK_RESULT(CheckImportKind(module_name, field_name, kind, export_->kind));
778804
*out_export = export_;
779805
return wabt::Result::Ok;
780806
}
@@ -786,20 +812,9 @@ wabt::Result BinaryReaderInterp::OnImportFunc(Index import_index,
786812
Index sig_index) {
787813
Index env_sig_index = TranslateSigIndexToEnv(sig_index);
788814

789-
Module* import_module;
790-
CHECK_RESULT(FindRegisteredModule(module_name, &import_module));
791-
792-
Export* export_ =
793-
import_module->GetFuncExport(env_, field_name, env_sig_index);
794-
if (!export_) {
795-
// If GetFuncExport fails then GetModuleExport will fail too. But it's
796-
// useful to call here to share the same error handling code as other
797-
// imports.
798-
CHECK_RESULT(GetModuleExport(import_module, field_name, &export_));
799-
}
800-
801-
CHECK_RESULT(CheckImportKind(module_name, field_name, ExternalKind::Func,
802-
export_->kind));
815+
Export* export_;
816+
CHECK_RESULT(ResolveImport(import_index, ExternalKind::Func, module_name,
817+
field_name, env_sig_index, &export_));
803818

804819
Func* func = env_->GetFunc(export_->index);
805820
if (!env_->FuncSignaturesAreEqual(env_sig_index, func->sig_index)) {
@@ -819,12 +834,9 @@ wabt::Result BinaryReaderInterp::OnImportTable(Index import_index,
819834
Type elem_type,
820835
const Limits* elem_limits) {
821836
has_table = true;
822-
Module* import_module;
823-
CHECK_RESULT(FindRegisteredModule(module_name, &import_module));
824-
825837
Export* export_;
826-
CHECK_RESULT(GetModuleExport(import_module, field_name, &export_));
827-
CHECK_RESULT(CheckImportKind(module_name, field_name, ExternalKind::Table, export_->kind));
838+
CHECK_RESULT(ResolveImport(import_index, ExternalKind::Table, module_name,
839+
field_name, 0, &export_));
828840

829841
Table* table = env_->GetTable(export_->index);
830842
if (elem_type != table->elem_type) {
@@ -849,12 +861,9 @@ wabt::Result BinaryReaderInterp::OnImportMemory(Index import_index,
849861
return wabt::Result::Error;
850862
}
851863

852-
Module* import_module;
853-
CHECK_RESULT(FindRegisteredModule(module_name, &import_module));
854-
855864
Export* export_;
856-
CHECK_RESULT(GetModuleExport(import_module, field_name, &export_));
857-
CHECK_RESULT(CheckImportKind(module_name, field_name, ExternalKind::Memory, export_->kind));
865+
CHECK_RESULT(ResolveImport(import_index, ExternalKind::Memory, module_name,
866+
field_name, 0, &export_));
858867

859868
Memory* memory = env_->GetMemory(export_->index);
860869
CHECK_RESULT(CheckImportLimits(page_limits, &memory->page_limits));
@@ -890,18 +899,13 @@ wabt::Result BinaryReaderInterp::OnImportGlobal(Index import_index,
890899
Index global_index,
891900
Type type,
892901
bool mutable_) {
893-
Module* import_module;
894-
CHECK_RESULT(FindRegisteredModule(module_name, &import_module));
895-
896902
Export* export_;
897-
CHECK_RESULT(GetModuleExport(import_module, field_name, &export_));
898-
CHECK_RESULT(CheckImportKind(module_name, field_name, ExternalKind::Global,
899-
export_->kind));
903+
CHECK_RESULT(ResolveImport(import_index, ExternalKind::Global, module_name,
904+
field_name, 0, &export_));
900905

901906
Global* exported_global = env_->GetGlobal(export_->index);
902907
GlobalType expected = {type, mutable_};
903908
GlobalType actual = {exported_global->type, exported_global->mutable_};
904-
905909
if (Failed(CheckGlobalType(actual, expected))) {
906910
return wabt::Result::Error;
907911
}
@@ -1932,6 +1936,7 @@ wabt::Result ReadBinaryInterp(Environment* env,
19321936
const void* data,
19331937
size_t size,
19341938
const ReadBinaryOptions& options,
1939+
const std::vector<Export*>& imports,
19351940
Errors* errors,
19361941
DefinedModule** out_module) {
19371942
// Need to mark before taking ownership of env->istream.
@@ -1941,7 +1946,7 @@ wabt::Result ReadBinaryInterp(Environment* env,
19411946
IstreamOffset istream_offset = istream->size();
19421947
DefinedModule* module = new DefinedModule(env);
19431948

1944-
BinaryReaderInterp reader(env, module, std::move(istream), errors,
1949+
BinaryReaderInterp reader(env, module, std::move(istream), imports, errors,
19451950
options.features);
19461951
env->EmplaceBackModule(module);
19471952

@@ -1959,4 +1964,15 @@ wabt::Result ReadBinaryInterp(Environment* env,
19591964
return result;
19601965
}
19611966

1967+
wabt::Result ReadBinaryInterp(Environment* env,
1968+
const void* data,
1969+
size_t size,
1970+
const ReadBinaryOptions& options,
1971+
Errors* errors,
1972+
DefinedModule** out_module) {
1973+
std::vector<Export*> empty_imports;
1974+
return ReadBinaryInterp(env, data, size, options, empty_imports, errors,
1975+
out_module);
1976+
}
1977+
19621978
} // namespace wabt

src/interp/binary-reader-interp.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,32 @@ namespace wabt {
2424

2525
namespace interp {
2626

27+
struct Export;
2728
struct DefinedModule;
2829
class Environment;
2930

3031
} // namespace interp
3132

3233
struct ReadBinaryOptions;
3334

35+
// Read and instantiate a module in the given environment.
3436
Result ReadBinaryInterp(interp::Environment* env,
3537
const void* data,
3638
size_t size,
3739
const ReadBinaryOptions& options,
3840
Errors*,
3941
interp::DefinedModule** out_module);
4042

43+
// Read and instantiate a module in the given environment. Similar to above but
44+
// using using a fixed set of exports to resolve the module exports.
45+
Result ReadBinaryInterp(interp::Environment* env,
46+
const void* data,
47+
size_t size,
48+
const ReadBinaryOptions& options,
49+
const std::vector<interp::Export*>& imports,
50+
Errors*,
51+
interp::DefinedModule** out_module);
52+
4153
} // namespace wabt
4254

4355
#endif /* WABT_BINARY_READER_INTERP_H_ */

0 commit comments

Comments
 (0)