Skip to content

Commit

Permalink
Import function target IDs (#70)
Browse files Browse the repository at this point in the history
* Prefer library wasm.h file

* Rename godot-wasm.h to wasm.h to reflect class

* Prefer Godot library

* Use import function target object ID

Closes #69

* Tests for freed target

* Fix Godot test comparable check

* Use defines for object ID functions
  • Loading branch information
ashtonmeuser authored Jun 13, 2024
1 parent e9f7b0a commit 1ec8f3b
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 23 deletions.
6 changes: 6 additions & 0 deletions examples/wasm-test/.godot/global_script_class_cache.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ list=Array[Dictionary]([{
"language": &"GDScript",
"path": "res://utils/GodotWasmTestSuite.gd"
}, {
"base": &"Object",
"class": &"ImportTarget",
"icon": "",
"language": &"GDScript",
"path": "res://utils/ImportTarget.gd"
}, {
"base": &"Node",
"class": &"TestRunner",
"icon": "",
Expand Down
37 changes: 37 additions & 0 deletions examples/wasm-test/TestImports.gd
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,43 @@ func test_invalid_imports():
expect_eq(error, ERR_CANT_CREATE)
expect_error("Invalid import method import.import_int")

func test_instantiation_freed_target():
var target = ImportTarget.new()
var imports = { "functions": {
"import.import_int": target.dummy_import(),
"import.import_float": target.dummy_import(),
} }
var wasm = Wasm.new()
var buffer = read_file("import")
var error = wasm.compile(buffer)
expect_eq(error, OK)
target.free() # Free target before instantiation
error = wasm.instantiate(imports)
expect_eq(error, ERR_CANT_CREATE)
expect_error("Invalid import target import.import_(int|float)")

func test_invocation_freed_target():
var target = ImportTarget.new()
var imports = { "functions": {
"import.import_int": target.dummy_import(),
"import.import_float": target.dummy_import(),
} }
var wasm = load_wasm("import", imports)
target.free() # Free target before invocation
wasm.function("callback", [])
expect_error("Failed to retrieve import function target")
expect_error("Failed calling function callback")

func test_import_method_missing():
var imports = { "functions": {
"import.import_int": [self, "invalid_method_0"],
"import.import_float": [self, "invalid_method_1"],
} }
var wasm = load_wasm("import", imports)
wasm.function("callback", [])
expect_error(".*::invalid_method_0': Method not found.*")
expect_error(".*::invalid_method_1': Method not found.*")

func test_callback_function():
var imports = { "functions": {
"import.import_int": dummy_import(),
Expand Down
11 changes: 11 additions & 0 deletions examples/wasm-test/utils/ImportTarget.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
extends Object
class_name ImportTarget

# Dummy import to supply to Wasm modules
static func _dummy_import(a = "", b = "", c = "", d = ""):
var message = "Dummy import %s %s %s %s" % [a, b, c, d]
print(message.strip_edges())
return a

func dummy_import() -> Array:
return [self, "_dummy_import"]
2 changes: 1 addition & 1 deletion examples/wasm-test/utils/Utils.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ extends Object
class_name Utils

static func comparable(o):
return o.hash() if (o is Dictionary or o is Object) else o
return o.hash() if ((o is Dictionary or o is Object) and o != null) else o

static func make_regex(pattern: String) -> RegEx:
var regex = RegEx.new()
Expand Down
2 changes: 1 addition & 1 deletion register_types.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "register_types.h"
#include "src/godot-wasm.h"
#include "src/wasm.h"
#include "src/wasm-memory.h"

using namespace godot;
Expand Down
27 changes: 16 additions & 11 deletions src/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,34 @@ Useful for minimizing changes to implementation files between targets e.g. GDExt
*/

#ifdef GODOT_MODULE // Godot includes when building module
#include "core/os/os.h"
#include "core/os/time.h"
#include "core/crypto/crypto.h"
#include "core/io/stream_peer.h"
#include <core/os/os.h>
#include <core/os/time.h>
#include <core/crypto/crypto.h>
#include <core/io/stream_peer.h>
#include <core/variant/variant_utility.h>
#else // Godot addon includes
#include "godot_cpp/classes/ref_counted.hpp"
#include "godot_cpp/classes/os.hpp"
#include "godot_cpp/classes/time.hpp"
#include "godot_cpp/classes/crypto.hpp"
#include "godot_cpp/classes/stream_peer_extension.hpp"
#include "godot_cpp/variant/utility_functions.hpp"
#include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/classes/os.hpp>
#include <godot_cpp/classes/time.hpp>
#include <godot_cpp/classes/crypto.hpp>
#include <godot_cpp/classes/stream_peer_extension.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#endif

#ifdef GODOT_MODULE
#define godot_error Error
#define PRINT(message) print_line(String(message))
#define PRINT_ERROR(message) print_error("Godot Wasm: " + String(message))
#define godot_error Error
#define INSTANCE_FROM_ID(id) ObjectDB::get_instance(id)
#define INSTANCE_VALIDATE(id) VariantUtilityFunctions::is_instance_valid(id)
#define REGISTRATION_METHOD _bind_methods
#define RANDOM_BYTES(n) Crypto::create()->generate_random_bytes(n)
#else
#define PRINT(message) UtilityFunctions::print(String(message))
#define PRINT_ERROR(message) _err_print_error(__FUNCTION__, __FILE__, __LINE__, "Godot Wasm: " + String(message))
#define godot_error Error
#define INSTANCE_FROM_ID(id) ObjectDB::get_instance(id)
#define INSTANCE_VALIDATE(id) UtilityFunctions::is_instance_valid(id)
#define REGISTRATION_METHOD _bind_methods
#define RANDOM_BYTES(n) [n]()->PackedByteArray{Ref<Crypto> c;c.instantiate();return c->generate_random_bytes(n);}()
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/store.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Singleton Wasm C API store
The same store is used between all compiled Wasm modules
*/

#include "wasm.h"
#include <wasm.h>

#define STORE ::godot_wasm::Store::instance().store

Expand Down
2 changes: 1 addition & 1 deletion src/wasi-shim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <vector>
#include <map>
#include "wasi-shim.h"
#include "godot-wasm.h"
#include "wasm.h"
#include "defer.h"
#include "string-container.h"

Expand Down
2 changes: 1 addition & 1 deletion src/wasi-shim.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#define WASI_SHIM_H

#include <functional>
#include "wasm.h"
#include <wasm.h>
#include "defs.h"

namespace godot {
Expand Down
2 changes: 1 addition & 1 deletion src/wasm-memory.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "wasm.h"
#include <wasm.h>
#include "wasm-memory.h"
#include "store.h"

Expand Down
12 changes: 7 additions & 5 deletions src/godot-wasm.cpp → src/wasm.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include <string>
#include <vector>
#include "godot-wasm.h"
#include "wasm.h"
#include "wasi-shim.h"
#include "defer.h"
#include "store.h"
Expand All @@ -13,7 +13,7 @@ namespace godot {
};

struct ContextFuncImport: public ContextExtern {
Object* target; // The object from which to invoke callback method
ObjectID target; // The ID of the object on which to invoke callback method
String method; // External name; doesn't necessarily match import name
std::vector<wasm_valkind_t> results; // Return types
ContextFuncImport(uint16_t i, const wasm_functype_t* func_type): ContextExtern(i) {
Expand Down Expand Up @@ -204,8 +204,9 @@ namespace godot {
Array params = Array();
// TODO: Check if args and results match expected sizes
for (uint16_t i = 0; i < args->size; i++) params.push_back(decode_variant(args->data[i]));
// TODO: Ensure target is valid and has method
Variant variant = context->target->callv(context->method, params);
Object* target = INSTANCE_FROM_ID(context->target);
FAIL_IF(target == nullptr, "Failed to retrieve import function target", trap("Failed to retrieve import function target\0"));
Variant variant = target->callv(context->method, params);
godot_error error = extract_results(variant, context, results);
if (error) FAIL("Extracting import function results failed", trap("Extracting import function results failed\0"));
return NULL;
Expand Down Expand Up @@ -334,9 +335,10 @@ namespace godot {
const Array& import = dict_safe_get(functions, it.first, Array());
FAIL_IF(import.size() != 2, "Invalid import function " + it.first, ERR_CANT_CREATE);
FAIL_IF(import[0].get_type() != Variant::OBJECT, "Invalid import target " + it.first, ERR_CANT_CREATE);
FAIL_IF(!INSTANCE_VALIDATE(import[0]), "Invalid import target " + it.first, ERR_CANT_CREATE);
FAIL_IF(import[1].get_type() != Variant::STRING, "Invalid import method " + it.first, ERR_CANT_CREATE);
godot_wasm::ContextFuncImport* context = (godot_wasm::ContextFuncImport*)&it.second;
context->target = import[0];
context->target = import[0].operator Object*()->get_instance_id();
context->method = import[1];
extern_map[it.second.index] = wasm_func_as_extern(create_callback(context));
}
Expand Down
2 changes: 1 addition & 1 deletion src/godot-wasm.h → src/wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#define GODOT_WASM_H

#include <map>
#include "wasm.h"
#include <wasm.h>
#include "defs.h"
#include "wasm-memory.h"

Expand Down

0 comments on commit 1ec8f3b

Please sign in to comment.