Skip to content

Commit d20b2a1

Browse files
authored
Extensions refactor (#81)
* Restructure extensions subdir * Rename * Extension base class * WIP: Header-only WASI Preview 1 extension * Finalize header-only WASI Preview 1 extension * Remove permissions, add enabled extensions list * Update tests * Formatting * Docs * Prepare for Godot 3.x backport
1 parent 3cfff34 commit d20b2a1

File tree

11 files changed

+188
-138
lines changed

11 files changed

+188
-138
lines changed

SConstruct

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ env.Append(CPPPATH=[".", "{}/include".format(env["wasm_runtime"])])
5454
env.Append(LIBS=[runtime_lib])
5555

5656
# Godot Wasm sources
57-
source = ["register_types.cpp", env.Glob("src/*.cpp")]
57+
source = ["register_types.cpp", env.Glob("src/*.cpp"), env.Glob("src/extensions/*.cpp")]
5858

5959
# Builders
6060
library = env.SharedLibrary(target="addons/godot-wasm/bin/{}/godot-wasm".format(env["platform"]), source=source)

SCsub

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,9 @@ module_env.Append(CPPDEFINES=["GODOT_MODULE", "LIBWASM_STATIC"])
5151

5252
# Module sources
5353
module_env.add_source_files(
54-
env.modules_sources, ["register_types.cpp", env.Glob("src/*.cpp", exclude="src/godot-library.cpp")]
54+
env.modules_sources, [
55+
"register_types.cpp",
56+
env.Glob("src/*.cpp", exclude="src/godot-library.cpp"),
57+
env.Glob("src/extensions/*.cpp")
58+
]
5559
)

doc_classes/Wasm.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,8 @@
6969
<member name="memory" type="WasmMemory" setter="" getter="get_memory">
7070
A [StreamPeer] interface for interacting with the memory of an instantiated Wasm module.
7171
</member>
72+
<member name="extensions" type="PackedStringArray" setter="set_extensions" getter="get_extensions">
73+
An array of strings listing enabled extensions that satisfy Wasm module imports.
74+
</member>
7275
</members>
7376
</class>

examples/wasm-test/TestWasi.gd

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,28 +43,11 @@ func test_clock_time_get():
4343
var result = wasm.function("clock_time_get", [])
4444
expect_within(result, time, 1000.0) # Within one second
4545

46-
func test_permissions():
47-
var wasm = load_wasm("wasi")
48-
expect_includes(wasm.permissions, "print")
49-
expect_eq(wasm.permissions.get("print"), true)
50-
wasm.permissions = { "print": false }
51-
expect_eq(wasm.permissions.get("print"), false)
52-
var permission = wasm.has_permission("print")
53-
expect_eq(permission, false)
54-
wasm.permissions = { "print": true }
55-
permission = wasm.has_permission("print")
56-
expect_eq(permission, true)
57-
58-
func test_invalid_permissions():
59-
# TODO: No exit permissions causes crash on proc_exit
60-
# TODO: Args & env not affected by permissions
61-
var wasm = load_wasm("wasi")
62-
wasm.permissions = { "print": false }
63-
wasm.function("fd_write", [])
64-
expect_error("Failed calling function fd_write")
65-
wasm.permissions = { "print": true, "random": false }
66-
wasm.function("random_get", [])
67-
expect_error("Failed calling function random_get")
68-
wasm.permissions = { "random": true, "time": false }
69-
wasm.function("clock_time_get", [])
70-
expect_error("Failed calling function clock_time_get")
46+
func test_disable_extension():
47+
var wasm = Wasm.new()
48+
var buffer = read_file("wasi")
49+
expect_includes(wasm.extensions, "wasi_preview1")
50+
wasm.extensions = [] # Remove all extensions
51+
var error = wasm.load(buffer, {})
52+
expect_eq(error, ERR_CANT_CREATE)
53+
expect_error("Missing import function wasi_snapshot_preview1.args_get")

examples/wasm-test/utils/TestSuite.gd

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,22 @@ func expect_includes(o, v: String):
7676
if !o.keys().has(v): _fail("Expect contains: %s%s" % [v, o.keys()])
7777
elif o is Array:
7878
if !o.has(v): _fail("Expect contains: %s%s" % [v, o])
79-
else: _fail("Expect contains: Invalid object")
79+
else:
80+
var a = Array(o) # May be a packed array; attempt cast
81+
if a != null:
82+
if !a.has(v): _fail("Expect contains: %s%s" % [v, a])
83+
else: _fail("Expect contains: Invalid object")
8084

8185
func expect_excludes(o, v: String):
8286
if o is Dictionary:
8387
if o.keys().has(v): _fail("Expect excludes: %s%s" % [v, o.keys()])
8488
elif o is Array:
8589
if o.has(v): _fail("Expect excludes: %s%s" % [v, o])
86-
else: _fail("Expect excludes: Invalid object")
90+
else:
91+
var a = Array(o) # May be a packed array; attempt cast
92+
if a != null:
93+
if !a.has(v): _fail("Expect contains: %s%s" % [v, a])
94+
else: _fail("Expect excludes: Invalid object")
8795

8896
# General utils
8997

src/defs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Useful for minimizing changes to implementation files between targets e.g. GDExt
4242
#define FAIL_IF(cond, message, ret) if (unlikely(cond)) FAIL(message, ret)
4343
#define INSTANTIATE_REF(ref) ref.instantiate()
4444
#define BYTE_ARRAY_POINTER(array) array.ptr()
45+
#define PACKED_ARRAY_HAS(array, value) array.has(value)
4546
#define CMDLINE_ARGS OS::get_singleton()->get_cmdline_user_args()
4647
#define TIME_REALTIME Time::get_singleton()->get_unix_time_from_system() * 1000000000
4748
#define TIME_MONOTONIC Time::get_singleton()->get_ticks_usec() * 1000

src/extensions/extension.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#ifndef GODOT_WASM_EXTENSION_H
2+
#define GODOT_WASM_EXTENSION_H
3+
4+
#include <map>
5+
#include <string>
6+
#include <vector>
7+
#include <wasm.h>
8+
#include "../defs.h"
9+
#include "../defer.h"
10+
#include "../store.h"
11+
12+
namespace godot {
13+
class Wasm; // Forward declare to avoid circular dependency
14+
15+
namespace godot_wasm {
16+
typedef wasm_trap_t* (*extension_callback_t)(Wasm*, const wasm_val_vec_t*, wasm_val_vec_t*);
17+
typedef std::tuple<std::vector<wasm_valkind_enum>, std::vector<wasm_valkind_enum>, extension_callback_t> callback_signature;
18+
19+
class Extension {
20+
private:
21+
Wasm* wasm; // Non-owning pointer to the Wasm instance
22+
std::map<std::string, callback_signature> signatures;
23+
24+
wasm_func_t* create_callback(const callback_signature& signature) {
25+
const auto& param_types = std::get<0>(signature);
26+
const auto& result_types = std::get<1>(signature);
27+
28+
wasm_valtype_vec_t params;
29+
wasm_valtype_vec_new_uninitialized(&params, param_types.size());
30+
for (size_t i = 0; i < param_types.size(); i++) params.data[i] = wasm_valtype_new(param_types[i]);
31+
DEFER(wasm_valtype_vec_delete(&params));
32+
33+
wasm_valtype_vec_t results;
34+
wasm_valtype_vec_new_uninitialized(&results, result_types.size());
35+
for (size_t i = 0; i < result_types.size(); i++) results.data[i] = wasm_valtype_new(result_types[i]);
36+
DEFER(wasm_valtype_vec_delete(&results));
37+
38+
wasm_functype_t* functype = wasm_functype_new(&params, &results);
39+
DEFER(wasm_functype_delete(functype));
40+
41+
extension_callback_t callback = std::get<2>(signature);
42+
43+
return wasm_func_new_with_env(STORE, functype, (wasm_func_callback_with_env_t)callback, wasm, NULL);
44+
}
45+
46+
protected:
47+
void register_callback(
48+
const String& import_name,
49+
const std::vector<wasm_valkind_enum>& param_types,
50+
const std::vector<wasm_valkind_enum>& result_types,
51+
extension_callback_t callback
52+
) {
53+
std::string key = std::string(import_name.utf8().get_data());
54+
signatures[key] = std::make_tuple(param_types, result_types, callback);
55+
}
56+
57+
public:
58+
Extension(Wasm* wasm_instance): wasm(wasm_instance) {}
59+
virtual ~Extension() {}
60+
61+
virtual wasm_func_t* get_callback(const String &name) final {
62+
std::string key = std::string(name.utf8().get_data());
63+
if (signatures.count(key)) {
64+
return create_callback(signatures[key]);
65+
}
66+
return NULL;
67+
}
68+
};
69+
}
70+
}
71+
72+
#endif

0 commit comments

Comments
 (0)