mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-25 13:35:54 +00:00
Host function gas cost (#5488)
* Update Wamr to 2.3.1 * Add gas cost per host-function * Fix windows build * Fix wasm test * Add no import test
This commit is contained in:
2
.github/actions/dependencies/action.yml
vendored
2
.github/actions/dependencies/action.yml
vendored
@@ -17,7 +17,7 @@ runs:
|
||||
conan export external/rocksdb rocksdb/9.7.3@
|
||||
conan export external/soci soci/4.0.3@
|
||||
conan export external/nudb nudb/2.0.8@
|
||||
conan export -k external/wamr wamr/2.2.0@
|
||||
conan export external/wamr wamr/2.3.1@
|
||||
- name: add Ripple Conan remote
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
2
.github/workflows/nix.yml
vendored
2
.github/workflows/nix.yml
vendored
@@ -408,7 +408,7 @@ jobs:
|
||||
conan profile update 'conf.tools.build:cxxflags+=["-DBOOST_ASIO_DISABLE_CONCEPTS"]' default
|
||||
conan export external/snappy snappy/1.1.10@
|
||||
conan export external/soci soci/4.0.3@
|
||||
conan export -k external/wamr wamr/2.2.0@
|
||||
conan export external/wamr wamr/2.3.1@
|
||||
|
||||
- name: build dependencies
|
||||
run: |
|
||||
|
||||
@@ -31,7 +31,7 @@ class Xrpl(ConanFile):
|
||||
'openssl/1.1.1v',
|
||||
'soci/4.0.3',
|
||||
'zlib/1.3.1',
|
||||
'wamr/2.2.0',
|
||||
'wamr/2.3.1',
|
||||
]
|
||||
|
||||
tool_requires = [
|
||||
|
||||
2
external/wamr/conandata.yml
vendored
2
external/wamr/conandata.yml
vendored
@@ -1,5 +1,5 @@
|
||||
patches:
|
||||
2.2.0:
|
||||
2.3.1:
|
||||
- patch_description: add metering to iwasm interpreter
|
||||
patch_file: patches/ripp_metering.patch
|
||||
patch_type: conan
|
||||
|
||||
5
external/wamr/conanfile.py
vendored
5
external/wamr/conanfile.py
vendored
@@ -13,7 +13,7 @@ required_conan_version = ">=1.55.0"
|
||||
|
||||
class WamrConan(ConanFile):
|
||||
name = "wamr"
|
||||
version = "2.2.0"
|
||||
version = "2.3.1"
|
||||
license = "Apache License v2.0"
|
||||
url = "https://github.com/bytecodealliance/wasm-micro-runtime.git"
|
||||
description = "Webassembly micro runtime"
|
||||
@@ -42,7 +42,7 @@ class WamrConan(ConanFile):
|
||||
git = tools.Git()
|
||||
git.clone(
|
||||
"https://github.com/bytecodealliance/wasm-micro-runtime.git",
|
||||
"c883fafead005e87ad3122b05409886f507c1cb0",
|
||||
"2a303861cc916dc182b7fecaa0aacc1b797e7ac6",
|
||||
shallow=True,
|
||||
)
|
||||
# get(self, **self.conan_data["sources"][self.version], strip_root=True)
|
||||
@@ -89,3 +89,4 @@ class WamrConan(ConanFile):
|
||||
self.cpp_info.libs = ["iwasm"]
|
||||
self.cpp_info.names["cmake_find_package"] = "wamr"
|
||||
self.cpp_info.names["cmake_find_package_multi"] = "wamr"
|
||||
|
||||
|
||||
508
external/wamr/patches/ripp_metering.patch
vendored
508
external/wamr/patches/ripp_metering.patch
vendored
@@ -1,5 +1,5 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 551991f8..5f48a0b8 100644
|
||||
index 88a1642b..aeb29912 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -1,7 +1,7 @@
|
||||
@@ -11,97 +11,127 @@ index 551991f8..5f48a0b8 100644
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build using shared libraries" OFF)
|
||||
|
||||
diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake
|
||||
index 1cb50235..bd103022 100644
|
||||
--- a/build-scripts/config_common.cmake
|
||||
+++ b/build-scripts/config_common.cmake
|
||||
@@ -669,6 +669,10 @@ if (WAMR_BUILD_AOT_VALIDATOR EQUAL 1)
|
||||
message (" AOT validator enabled")
|
||||
add_definitions (-DWASM_ENABLE_AOT_VALIDATOR=1)
|
||||
endif ()
|
||||
+if (WAMR_BUILD_INSTRUCTION_METERING EQUAL 1)
|
||||
+ message (" Instruction metering enabled")
|
||||
+ add_definitions (-DWASM_ENABLE_INSTRUCTION_METERING=1)
|
||||
+endif ()
|
||||
|
||||
########################################
|
||||
# Show Phase4 Wasm proposals status.
|
||||
diff --git a/core/config.h b/core/config.h
|
||||
index cb1189c9..a4e1499e 100644
|
||||
--- a/core/config.h
|
||||
+++ b/core/config.h
|
||||
@@ -716,4 +716,8 @@ unless used elsewhere */
|
||||
#define WASM_ENABLE_AOT_VALIDATOR 0
|
||||
#endif
|
||||
|
||||
+#ifndef WASM_ENABLE_INSTRUCTION_METERING
|
||||
+#define WASM_ENABLE_INSTRUCTION_METERING 0
|
||||
+#endif
|
||||
+
|
||||
#endif /* end of _CONFIG_H_ */
|
||||
diff --git a/core/iwasm/common/wasm_c_api.c b/core/iwasm/common/wasm_c_api.c
|
||||
index 269ec577..bc6fd01b 100644
|
||||
index 269ec577..34eb7c34 100644
|
||||
--- a/core/iwasm/common/wasm_c_api.c
|
||||
+++ b/core/iwasm/common/wasm_c_api.c
|
||||
@@ -5389,3 +5389,8 @@ wasm_instance_get_wasm_func_exec_time(const wasm_instance_t *instance,
|
||||
@@ -3242,10 +3242,20 @@ wasm_func_copy(const wasm_func_t *func)
|
||||
|
||||
cloned->func_idx_rt = func->func_idx_rt;
|
||||
cloned->inst_comm_rt = func->inst_comm_rt;
|
||||
+ cloned->gas = func->gas;
|
||||
|
||||
RETURN_OBJ(cloned, wasm_func_delete)
|
||||
}
|
||||
|
||||
+uint32_t
|
||||
+wasm_func_set_gas(wasm_func_t *func, uint32_t gas)
|
||||
+{
|
||||
+ if(!func) return 0;
|
||||
+
|
||||
+ func->gas = gas;
|
||||
+ return gas;
|
||||
+}
|
||||
+
|
||||
own wasm_functype_t *
|
||||
wasm_func_type(const wasm_func_t *func)
|
||||
{
|
||||
@@ -4998,11 +5008,11 @@ wasm_instance_new_with_args_ex(wasm_store_t *store, const wasm_module_t *module,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+ WASMModuleInstance *wasm_module_inst = NULL;
|
||||
/* create the c-api func import list */
|
||||
#if WASM_ENABLE_INTERP != 0
|
||||
if (instance->inst_comm_rt->module_type == Wasm_Module_Bytecode) {
|
||||
- WASMModuleInstance *wasm_module_inst =
|
||||
- (WASMModuleInstance *)instance->inst_comm_rt;
|
||||
+ wasm_module_inst = (WASMModuleInstance *)instance->inst_comm_rt;
|
||||
p_func_imports = &(wasm_module_inst->c_api_func_imports);
|
||||
import_func_count = MODULE_INTERP(module)->import_function_count;
|
||||
}
|
||||
@@ -5052,6 +5062,13 @@ wasm_instance_new_with_args_ex(wasm_store_t *store, const wasm_module_t *module,
|
||||
}
|
||||
bh_assert(func_import->func_ptr_linked);
|
||||
|
||||
+ // fill gas
|
||||
+ if(wasm_module_inst) {
|
||||
+ WASMFunctionInstance *fi = wasm_module_inst->e->functions + func_host->func_idx_rt;
|
||||
+ if(fi) fi->gas = func_host->gas;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
func_import++;
|
||||
}
|
||||
|
||||
@@ -5389,3 +5406,8 @@ wasm_instance_get_wasm_func_exec_time(const wasm_instance_t *instance,
|
||||
return -1.0;
|
||||
#endif
|
||||
}
|
||||
+
|
||||
+wasm_exec_env_t wasm_instance_exec_env(const wasm_instance_t*instance)
|
||||
+wasm_exec_env_t wasm_instance_exec_env(const wasm_instance_t *instance)
|
||||
+{
|
||||
+ return wasm_runtime_get_exec_env_singleton(instance->inst_comm_rt);
|
||||
+}
|
||||
diff --git a/core/iwasm/common/wasm_c_api_internal.h b/core/iwasm/common/wasm_c_api_internal.h
|
||||
index 49a17a96..19a85980 100644
|
||||
--- a/core/iwasm/common/wasm_c_api_internal.h
|
||||
+++ b/core/iwasm/common/wasm_c_api_internal.h
|
||||
@@ -142,6 +142,10 @@ struct wasm_func_t {
|
||||
void (*finalizer)(void *);
|
||||
} cb_env;
|
||||
} u;
|
||||
+
|
||||
+ // gas cost for import func
|
||||
+ uint32 gas;
|
||||
+
|
||||
/*
|
||||
* an index in both functions runtime instance lists
|
||||
* of interpreter mode and aot mode
|
||||
diff --git a/core/iwasm/common/wasm_exec_env.c b/core/iwasm/common/wasm_exec_env.c
|
||||
index e33fd9f3..d1ff9c41 100644
|
||||
index 47752950..d5821c4f 100644
|
||||
--- a/core/iwasm/common/wasm_exec_env.c
|
||||
+++ b/core/iwasm/common/wasm_exec_env.c
|
||||
@@ -85,6 +85,12 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
|
||||
wasm_runtime_dump_exec_env_mem_consumption(exec_env);
|
||||
@@ -86,7 +86,9 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
|
||||
#endif
|
||||
|
||||
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
+ exec_env->instructions_to_execute = -1;
|
||||
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
- exec_env->instructions_to_execute = -1;
|
||||
+ exec_env->instructions_to_execute = INT64_MAX;
|
||||
+ for(int i = 0; i < 256; ++i)
|
||||
+ exec_env->instructions_schedule[i] = 1;
|
||||
+#endif
|
||||
+
|
||||
return exec_env;
|
||||
|
||||
#ifdef OS_ENABLE_HW_BOUND_CHECK
|
||||
diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h
|
||||
index ce0c1fa7..2713a092 100644
|
||||
--- a/core/iwasm/common/wasm_exec_env.h
|
||||
+++ b/core/iwasm/common/wasm_exec_env.h
|
||||
@@ -87,6 +87,12 @@ typedef struct WASMExecEnv {
|
||||
uint8 *bottom;
|
||||
} wasm_stack;
|
||||
|
||||
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
+ /* instructions to execute */
|
||||
+ int64 instructions_to_execute;
|
||||
+ int64 instructions_schedule[256];
|
||||
+#endif
|
||||
+
|
||||
#if WASM_ENABLE_FAST_JIT != 0
|
||||
/**
|
||||
* Cache for
|
||||
diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c
|
||||
index d33c0272..21bfdf29 100644
|
||||
--- a/core/iwasm/common/wasm_runtime_common.c
|
||||
+++ b/core/iwasm/common/wasm_runtime_common.c
|
||||
@@ -2285,6 +2285,31 @@ wasm_runtime_access_exce_check_guard_page()
|
||||
}
|
||||
#endif
|
||||
|
||||
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
+
|
||||
+void
|
||||
+wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
|
||||
return exec_env;
|
||||
diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h
|
||||
index 5d80312f..2713a092 100644
|
||||
--- a/core/iwasm/common/wasm_exec_env.h
|
||||
+++ b/core/iwasm/common/wasm_exec_env.h
|
||||
@@ -89,7 +89,8 @@ typedef struct WASMExecEnv {
|
||||
|
||||
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
/* instructions to execute */
|
||||
- int instructions_to_execute;
|
||||
+ int64 instructions_to_execute;
|
||||
+ int64 instructions_schedule[256];
|
||||
#endif
|
||||
|
||||
#if WASM_ENABLE_FAST_JIT != 0
|
||||
diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c
|
||||
index dcee0aea..2559be21 100644
|
||||
--- a/core/iwasm/common/wasm_runtime_common.c
|
||||
+++ b/core/iwasm/common/wasm_runtime_common.c
|
||||
@@ -2288,10 +2288,26 @@ wasm_runtime_access_exce_check_guard_page()
|
||||
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
void
|
||||
wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
|
||||
- int instructions_to_execute)
|
||||
+ int64 instructions_to_execute)
|
||||
+{
|
||||
+ exec_env->instructions_to_execute = instructions_to_execute;
|
||||
+}
|
||||
{
|
||||
+ if(instructions_to_execute == -1)
|
||||
+ instructions_to_execute = INT64_MAX;
|
||||
exec_env->instructions_to_execute = instructions_to_execute;
|
||||
}
|
||||
+
|
||||
+int64
|
||||
+wasm_runtime_get_instruction_count_limit(WASMExecEnv *exec_env)
|
||||
@@ -116,13 +146,10 @@ index d33c0272..21bfdf29 100644
|
||||
+ for(int i = 0; i < 256; ++i)
|
||||
+ exec_env->instructions_schedule[i] = instructions_schedule[i];
|
||||
+}
|
||||
+
|
||||
+#endif
|
||||
+
|
||||
#endif
|
||||
|
||||
WASMFuncType *
|
||||
wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function,
|
||||
uint32 module_type)
|
||||
@@ -7792,13 +7817,14 @@ wasm_runtime_get_module_name(wasm_module_t module)
|
||||
@@ -7805,13 +7821,14 @@ wasm_runtime_get_module_name(wasm_module_t module)
|
||||
bool
|
||||
wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env)
|
||||
{
|
||||
@@ -138,7 +165,7 @@ index d33c0272..21bfdf29 100644
|
||||
uint32 page_size = os_getpagesize();
|
||||
uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT;
|
||||
boundary = boundary + page_size * guard_page_count;
|
||||
@@ -7808,6 +7834,7 @@ wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env)
|
||||
@@ -7821,6 +7838,7 @@ wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env)
|
||||
"native stack overflow");
|
||||
return false;
|
||||
}
|
||||
@@ -146,7 +173,7 @@ index d33c0272..21bfdf29 100644
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -7830,7 +7857,7 @@ wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env,
|
||||
@@ -7843,7 +7861,7 @@ wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env,
|
||||
boundary = boundary - WASM_STACK_GUARD_SIZE + requested_size;
|
||||
if ((uint8 *)&boundary < boundary) {
|
||||
wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env),
|
||||
@@ -156,19 +183,14 @@ index d33c0272..21bfdf29 100644
|
||||
}
|
||||
return true;
|
||||
diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h
|
||||
index 8ac032bf..5ca5d489 100644
|
||||
index 64a6cd79..f4c55e2f 100644
|
||||
--- a/core/iwasm/common/wasm_runtime_common.h
|
||||
+++ b/core/iwasm/common/wasm_runtime_common.h
|
||||
@@ -791,9 +791,25 @@ WASM_RUNTIME_API_EXTERN void
|
||||
wasm_runtime_set_native_stack_boundary(WASMExecEnv *exec_env,
|
||||
uint8 *native_stack_boundary);
|
||||
|
||||
-#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0
|
||||
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
+
|
||||
@@ -795,7 +795,14 @@ wasm_runtime_set_native_stack_boundary(WASMExecEnv *exec_env,
|
||||
/* See wasm_export.h for description */
|
||||
WASM_RUNTIME_API_EXTERN void
|
||||
+wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
|
||||
wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
|
||||
- int instructions_to_execute);
|
||||
+ int64 instructions_to_execute);
|
||||
+WASM_RUNTIME_API_EXTERN int64
|
||||
+wasm_runtime_get_instruction_count_limit(WASMExecEnv *exec_env);
|
||||
@@ -177,17 +199,11 @@ index 8ac032bf..5ca5d489 100644
|
||||
+wasm_runtime_set_instruction_schedule(WASMExecEnv *exec_env,
|
||||
+ int64 const *instructions_schedule);
|
||||
+
|
||||
+#endif
|
||||
+
|
||||
+#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0
|
||||
+/* See wasm_export.h for description */
|
||||
+WASM_RUNTIME_API_EXTERN
|
||||
+void
|
||||
wasm_runtime_set_bounds_checks(WASMModuleInstanceCommon *module_inst,
|
||||
bool enable);
|
||||
#endif
|
||||
|
||||
#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0
|
||||
diff --git a/core/iwasm/include/wasm_c_api.h b/core/iwasm/include/wasm_c_api.h
|
||||
index 241a0eec..9eb0dde1 100644
|
||||
index 241a0eec..1141744c 100644
|
||||
--- a/core/iwasm/include/wasm_c_api.h
|
||||
+++ b/core/iwasm/include/wasm_c_api.h
|
||||
@@ -19,8 +19,10 @@
|
||||
@@ -202,7 +218,16 @@ index 241a0eec..9eb0dde1 100644
|
||||
#endif
|
||||
#else
|
||||
#define WASM_API_EXTERN
|
||||
@@ -701,6 +703,11 @@ WASM_API_EXTERN double wasm_instance_sum_wasm_exec_time(const wasm_instance_t*);
|
||||
@@ -592,6 +594,8 @@ 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);
|
||||
|
||||
+WASM_API_EXTERN own uint32_t wasm_func_set_gas(wasm_func_t*, uint32_t);
|
||||
+
|
||||
|
||||
// Global Instances
|
||||
|
||||
@@ -701,6 +705,11 @@ WASM_API_EXTERN double wasm_instance_sum_wasm_exec_time(const wasm_instance_t*);
|
||||
// func_name. If the function is not found, return 0.
|
||||
WASM_API_EXTERN double wasm_instance_get_wasm_func_exec_time(const wasm_instance_t*, const char *);
|
||||
|
||||
@@ -215,7 +240,7 @@ index 241a0eec..9eb0dde1 100644
|
||||
// Convenience
|
||||
|
||||
diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h
|
||||
index b73a0364..3fd0949f 100644
|
||||
index b4ab34be..3fd0949f 100644
|
||||
--- a/core/iwasm/include/wasm_export.h
|
||||
+++ b/core/iwasm/include/wasm_export.h
|
||||
@@ -20,8 +20,10 @@
|
||||
@@ -230,22 +255,11 @@ index b73a0364..3fd0949f 100644
|
||||
#endif
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define WASM_RUNTIME_API_EXTERN __attribute__((visibility("default")))
|
||||
@@ -1821,6 +1823,27 @@ WASM_RUNTIME_API_EXTERN void
|
||||
wasm_runtime_set_native_stack_boundary(wasm_exec_env_t exec_env,
|
||||
uint8_t *native_stack_boundary);
|
||||
|
||||
+/**
|
||||
+ * Set the instruction count limit to the execution environment.
|
||||
+ * By default the instruction count limit is -1, which means no limit.
|
||||
+ * However, if the instruction count limit is set to a positive value,
|
||||
+ * the execution will be terminated when the instruction count reaches
|
||||
+ * the limit.
|
||||
+ *
|
||||
+ * @param exec_env the execution environment
|
||||
+ * @param instruction_count the instruction count limit
|
||||
+ */
|
||||
+WASM_RUNTIME_API_EXTERN void
|
||||
+wasm_runtime_set_instruction_count_limit(wasm_exec_env_t exec_env,
|
||||
@@ -1833,7 +1835,14 @@ wasm_runtime_set_native_stack_boundary(wasm_exec_env_t exec_env,
|
||||
*/
|
||||
WASM_RUNTIME_API_EXTERN void
|
||||
wasm_runtime_set_instruction_count_limit(wasm_exec_env_t exec_env,
|
||||
- int instruction_count);
|
||||
+ int64_t instruction_count);
|
||||
+
|
||||
+WASM_RUNTIME_API_EXTERN int64_t
|
||||
@@ -254,132 +268,122 @@ index b73a0364..3fd0949f 100644
|
||||
+WASM_RUNTIME_API_EXTERN void
|
||||
+wasm_runtime_set_instruction_schedule(wasm_exec_env_t exec_env,
|
||||
+ int64_t const *instructions_schedule);
|
||||
+
|
||||
|
||||
/**
|
||||
* Dump runtime memory consumption, including:
|
||||
* Exec env memory consumption
|
||||
diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c
|
||||
index 41ac4c72..bd8f714a 100644
|
||||
index 1e98b0fa..ae24ff8b 100644
|
||||
--- a/core/iwasm/interpreter/wasm_interp_classic.c
|
||||
+++ b/core/iwasm/interpreter/wasm_interp_classic.c
|
||||
@@ -1516,10 +1516,13 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst,
|
||||
} \
|
||||
os_mutex_unlock(&exec_env->wait_lock); \
|
||||
} \
|
||||
+ CHECK_INSTRUCTION_LIMIT(); \
|
||||
goto *handle_table[*frame_ip++]; \
|
||||
} while (0)
|
||||
#else
|
||||
-#define HANDLE_OP_END() FETCH_OPCODE_AND_DISPATCH()
|
||||
+#define HANDLE_OP_END() \
|
||||
+ CHECK_INSTRUCTION_LIMIT(); \
|
||||
+ FETCH_OPCODE_AND_DISPATCH()
|
||||
#endif
|
||||
|
||||
#else /* else of WASM_ENABLE_LABELS_AS_VALUES */
|
||||
@@ -1542,9 +1545,12 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst,
|
||||
} \
|
||||
os_mutex_unlock(&exec_env->wait_lock); \
|
||||
} \
|
||||
+ CHECK_INSTRUCTION_LIMIT(); \
|
||||
continue;
|
||||
#else
|
||||
-#define HANDLE_OP_END() continue
|
||||
+#define HANDLE_OP_END() \
|
||||
+ CHECK_INSTRUCTION_LIMIT(); \
|
||||
+ continue;
|
||||
#endif
|
||||
|
||||
#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */
|
||||
@@ -1562,6 +1568,20 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global)
|
||||
#endif
|
||||
@@ -1569,13 +1569,14 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global)
|
||||
}
|
||||
|
||||
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
+#define CHECK_INSTRUCTION_LIMIT() \
|
||||
+ if (instructions_to_execute >= 0) \
|
||||
+ { \
|
||||
+ instructions_to_execute -= instructions_schedule[opcode];\
|
||||
+ if (instructions_to_execute < 0) { \
|
||||
+ wasm_set_exception(module, "instruction limit exceeded"); \
|
||||
+ goto got_exception; \
|
||||
+ } \
|
||||
+ }
|
||||
+#else
|
||||
+#define CHECK_INSTRUCTION_LIMIT() (void)0
|
||||
+#endif
|
||||
+
|
||||
static void
|
||||
wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
WASMExecEnv *exec_env,
|
||||
@@ -1605,6 +1625,17 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
uint32 local_idx, local_offset, global_idx;
|
||||
uint8 local_type, *global_addr;
|
||||
uint32 cache_index, type_index, param_cell_num, cell_num;
|
||||
+
|
||||
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
+ int64 instructions_to_execute = -1;
|
||||
+ int64 const *instructions_schedule = NULL;
|
||||
+ if(exec_env)
|
||||
+ {
|
||||
+ instructions_to_execute = exec_env->instructions_to_execute;
|
||||
+ instructions_schedule = exec_env->instructions_schedule;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
#if WASM_ENABLE_EXCE_HANDLING != 0
|
||||
int32_t exception_tag_index;
|
||||
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
-#define CHECK_INSTRUCTION_LIMIT() \
|
||||
- if (instructions_left == 0) { \
|
||||
- wasm_set_exception(module, "instruction limit exceeded"); \
|
||||
- goto got_exception; \
|
||||
- } \
|
||||
- else if (instructions_left > 0) \
|
||||
- instructions_left--;
|
||||
+#define CHECK_INSTRUCTION_LIMIT() \
|
||||
+ do { \
|
||||
+ instructions_left -= instructions_schedule[opcode]; \
|
||||
+ if (instructions_left < 0) { \
|
||||
+ wasm_set_exception(module, "instruction limit exceeded"); \
|
||||
+ goto got_exception; \
|
||||
+ } \
|
||||
+ } while (0)
|
||||
#else
|
||||
#define CHECK_INSTRUCTION_LIMIT() (void)0
|
||||
#endif
|
||||
@@ -6859,6 +6890,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
@@ -1625,9 +1626,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
uint32 cache_index, type_index, param_cell_num, cell_num;
|
||||
|
||||
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
- int instructions_left = -1;
|
||||
+ int64 instructions_left = INT64_MAX;
|
||||
+ int64 const *instructions_schedule = NULL;
|
||||
if (exec_env) {
|
||||
instructions_left = exec_env->instructions_to_execute;
|
||||
+ instructions_schedule = exec_env->instructions_schedule;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -6885,6 +6888,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
FREE_FRAME(exec_env, frame);
|
||||
wasm_exec_env_set_cur_frame(exec_env, prev_frame);
|
||||
|
||||
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
+ if(exec_env)
|
||||
+ exec_env->instructions_to_execute = instructions_to_execute;
|
||||
+ exec_env->instructions_to_execute = instructions_left;
|
||||
+#endif
|
||||
+
|
||||
if (!prev_frame->ip) {
|
||||
/* Called from native. */
|
||||
return;
|
||||
@@ -6899,6 +6935,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
@@ -6925,6 +6933,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
}
|
||||
#endif
|
||||
SYNC_ALL_TO_FRAME();
|
||||
+
|
||||
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
+ if(exec_env)
|
||||
+ exec_env->instructions_to_execute = instructions_to_execute;
|
||||
+ exec_env->instructions_to_execute = instructions_left;
|
||||
+#endif
|
||||
+
|
||||
return;
|
||||
|
||||
#if WASM_ENABLE_LABELS_AS_VALUES == 0
|
||||
diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c
|
||||
index f33ad60e..9cbf2010 100644
|
||||
index 4e5edf41..04c39e1f 100644
|
||||
--- a/core/iwasm/interpreter/wasm_interp_fast.c
|
||||
+++ b/core/iwasm/interpreter/wasm_interp_fast.c
|
||||
@@ -105,6 +105,19 @@ typedef float64 CellType_F64;
|
||||
goto unaligned_atomic; \
|
||||
@@ -106,14 +106,14 @@ typedef float64 CellType_F64;
|
||||
} while (0)
|
||||
|
||||
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
-#define CHECK_INSTRUCTION_LIMIT() \
|
||||
- if (instructions_left == 0) { \
|
||||
- wasm_set_exception(module, "instruction limit exceeded"); \
|
||||
- goto got_exception; \
|
||||
- } \
|
||||
- else if (instructions_left > 0) \
|
||||
- instructions_left--;
|
||||
-
|
||||
+#define CHECK_INSTRUCTION_LIMIT() \
|
||||
+ if (instructions_to_execute >= 0) { \
|
||||
+ instructions_to_execute -= instructions_schedule[opcode]; \
|
||||
+ if (instructions_to_execute < 0) { \
|
||||
+ do { \
|
||||
+ instructions_left -= instructions_schedule[opcode]; \
|
||||
+ if (instructions_left < 0) { \
|
||||
+ wasm_set_exception(module, "instruction limit exceeded"); \
|
||||
+ goto got_exception; \
|
||||
+ } \
|
||||
+ }
|
||||
+#else
|
||||
+#define CHECK_INSTRUCTION_LIMIT() (void)0
|
||||
+#endif
|
||||
+
|
||||
static inline uint32
|
||||
rotl32(uint32 n, uint32 c)
|
||||
{
|
||||
@@ -1466,12 +1479,14 @@ wasm_interp_dump_op_count()
|
||||
+ } while (0)
|
||||
#else
|
||||
#define CHECK_INSTRUCTION_LIMIT() (void)0
|
||||
#endif
|
||||
@@ -1454,7 +1454,6 @@ wasm_interp_dump_op_count()
|
||||
do { \
|
||||
const void *p_label_addr = *(void **)frame_ip; \
|
||||
frame_ip += sizeof(void *); \
|
||||
- CHECK_INSTRUCTION_LIMIT(); \
|
||||
goto *p_label_addr; \
|
||||
} while (0)
|
||||
#else
|
||||
@@ -1466,7 +1465,6 @@ wasm_interp_dump_op_count()
|
||||
/* int32 relative offset was emitted in 64-bit target */ \
|
||||
p_label_addr = label_base + (int32)LOAD_U32_WITH_2U16S(frame_ip); \
|
||||
frame_ip += sizeof(int32); \
|
||||
- CHECK_INSTRUCTION_LIMIT(); \
|
||||
goto *p_label_addr; \
|
||||
} while (0)
|
||||
#else
|
||||
@@ -1477,17 +1475,18 @@ wasm_interp_dump_op_count()
|
||||
/* uint32 label address was emitted in 32-bit target */ \
|
||||
p_label_addr = (void *)(uintptr_t)LOAD_U32_WITH_2U16S(frame_ip); \
|
||||
frame_ip += sizeof(int32); \
|
||||
- CHECK_INSTRUCTION_LIMIT(); \
|
||||
goto *p_label_addr; \
|
||||
} while (0)
|
||||
#endif
|
||||
#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */
|
||||
@@ -396,46 +400,91 @@ index f33ad60e..9cbf2010 100644
|
||||
|
||||
#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */
|
||||
|
||||
@@ -1538,6 +1553,16 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
uint8 *maddr = NULL;
|
||||
uint32 local_idx, local_offset, global_idx;
|
||||
@@ -1508,6 +1507,25 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global)
|
||||
#endif
|
||||
}
|
||||
|
||||
+static int64 const def_instructions_schedule[256] = {
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||
+};
|
||||
+
|
||||
static void
|
||||
wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
WASMExecEnv *exec_env,
|
||||
@@ -1556,9 +1574,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
uint8 opcode = 0, local_type, *global_addr;
|
||||
+
|
||||
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
+ int64 instructions_to_execute = -1;
|
||||
+ int64 const *instructions_schedule = NULL;
|
||||
+ if (exec_env) {
|
||||
+ instructions_to_execute = exec_env->instructions_to_execute;
|
||||
|
||||
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
- int instructions_left = -1;
|
||||
+ int64 instructions_left = INT64_MAX;
|
||||
+ int64 const *instructions_schedule = def_instructions_schedule;
|
||||
if (exec_env) {
|
||||
instructions_left = exec_env->instructions_to_execute;
|
||||
+ instructions_schedule = exec_env->instructions_schedule;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
}
|
||||
#endif
|
||||
#if !defined(OS_ENABLE_HW_BOUND_CHECK) \
|
||||
|| WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0
|
||||
#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0
|
||||
@@ -7761,6 +7786,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
@@ -7694,6 +7714,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
{
|
||||
wasm_interp_call_func_native(module, exec_env, cur_func,
|
||||
prev_frame);
|
||||
+ instructions_left -= cur_func->gas;
|
||||
}
|
||||
|
||||
#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0
|
||||
@@ -7806,6 +7827,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
FREE_FRAME(exec_env, frame);
|
||||
wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame *)prev_frame);
|
||||
|
||||
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
+ if (exec_env)
|
||||
+ exec_env->instructions_to_execute = instructions_to_execute;
|
||||
+ exec_env->instructions_to_execute = instructions_left;
|
||||
+#endif
|
||||
+
|
||||
if (!prev_frame->ip)
|
||||
/* Called from native. */
|
||||
return;
|
||||
@@ -7789,6 +7819,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
@@ -7834,6 +7860,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||
|
||||
got_exception:
|
||||
SYNC_ALL_TO_FRAME();
|
||||
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||
+ if (exec_env)
|
||||
+ exec_env->instructions_to_execute = instructions_to_execute;
|
||||
+ exec_env->instructions_to_execute = instructions_left;
|
||||
+#endif
|
||||
return;
|
||||
|
||||
#if WASM_ENABLE_LABELS_AS_VALUES == 0
|
||||
diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h
|
||||
index 8d38c883..a687ab89 100644
|
||||
--- a/core/iwasm/interpreter/wasm_runtime.h
|
||||
+++ b/core/iwasm/interpreter/wasm_runtime.h
|
||||
@@ -228,6 +228,10 @@ struct WASMFunctionInstance {
|
||||
WASMFunctionImport *func_import;
|
||||
WASMFunction *func;
|
||||
} u;
|
||||
+
|
||||
+ // gas cost for import func
|
||||
+ uint32 gas;
|
||||
+
|
||||
#if WASM_ENABLE_MULTI_MODULE != 0
|
||||
WASMModuleInstance *import_module_inst;
|
||||
WASMFunctionInstance *import_func_inst;
|
||||
diff --git a/core/shared/platform/include/platform_wasi_types.h b/core/shared/platform/include/platform_wasi_types.h
|
||||
index ac1a95ea..e23b500e 100644
|
||||
--- a/core/shared/platform/include/platform_wasi_types.h
|
||||
@@ -453,18 +502,3 @@ index ac1a95ea..e23b500e 100644
|
||||
#endif
|
||||
|
||||
assert_wasi_layout(_Alignof(int8_t) == 1, "non-wasi data layout");
|
||||
diff --git a/doc/build_wamr.md b/doc/build_wamr.md
|
||||
index 6425450b..94dd9628 100644
|
||||
--- a/doc/build_wamr.md
|
||||
+++ b/doc/build_wamr.md
|
||||
@@ -327,6 +327,10 @@ And the wasm app can calls below APIs to allocate/free memory from/to the shared
|
||||
- **WAMR_BUILD_SHRUNK_MEMORY**=1/0, default to enable if not set
|
||||
> Note: When enabled, this feature will reduce memory usage by decreasing the size of the linear memory, particularly when the `memory.grow` opcode is not used and memory usage is somewhat predictable.
|
||||
|
||||
+## **Instruction metering**
|
||||
+- **WAMR_BUILD_INSTRUCTION_METERING**=1/0, default to disable if not set
|
||||
+> Note: Enabling this feature allows limiting the number of instructions a wasm module instance can execute. Use the `wasm_runtime_set_instruction_count_limit(...)` API before calling `wasm_runtime_call_*(...)` APIs to enforce this limit.
|
||||
+
|
||||
## **Combination of configurations:**
|
||||
|
||||
We can combine the configurations. For example, if we want to disable interpreter, enable AOT and WASI, we can run command:
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <xrpld/app/misc/WasmHostFunc.h>
|
||||
#include <xrpld/app/misc/WasmVM.h>
|
||||
|
||||
#include <iwasm/wasm_c_api.h>
|
||||
#include <wasm_c_api.h>
|
||||
|
||||
#ifdef _DEBUG
|
||||
// #define DEBUG_OUTPUT 1
|
||||
@@ -71,97 +71,6 @@ getLedgerSqn_wrap(void* env, wasm_val_vec_t const*, wasm_val_vec_t* results)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct TestHostFunctionsOld : public HostFunctions
|
||||
{
|
||||
test::jtx::Env* env_;
|
||||
Bytes accountID_;
|
||||
Bytes data_;
|
||||
int clock_drift_ = 0;
|
||||
test::StreamSink sink_;
|
||||
beast::Journal jlog_;
|
||||
void const* rt_ = nullptr;
|
||||
|
||||
public:
|
||||
explicit TestHostFunctionsOld(test::jtx::Env* env, int cd = 0)
|
||||
: env_(env)
|
||||
, clock_drift_(cd)
|
||||
, sink_(beast::severities::kDebug)
|
||||
, jlog_(sink_)
|
||||
{
|
||||
std::string s = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
||||
accountID_ = Bytes{s.begin(), s.end()};
|
||||
std::string t = "10000";
|
||||
data_ = Bytes{t.begin(), t.end()};
|
||||
}
|
||||
|
||||
virtual void
|
||||
setRT(void const* rt) override
|
||||
{
|
||||
rt_ = rt;
|
||||
}
|
||||
|
||||
virtual void const*
|
||||
getRT() const override
|
||||
{
|
||||
return rt_;
|
||||
}
|
||||
|
||||
test::StreamSink&
|
||||
getSink()
|
||||
{
|
||||
return sink_;
|
||||
}
|
||||
|
||||
beast::Journal
|
||||
getJournal() override
|
||||
{
|
||||
return jlog_;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getLedgerSqn() override
|
||||
{
|
||||
return (int32_t)env_->current()->seq();
|
||||
}
|
||||
|
||||
int32_t
|
||||
getParentLedgerTime() override
|
||||
{
|
||||
return env_->current()->parentCloseTime().time_since_epoch().count() +
|
||||
clock_drift_;
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getTxField(SField const& fname) override
|
||||
{
|
||||
return accountID_;
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname) override
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getCurrentLedgerObjField(SField const& fname) override
|
||||
{
|
||||
if (fname.getName() == "Destination" || fname.getName() == "Account")
|
||||
return accountID_;
|
||||
else if (fname.getName() == "Data")
|
||||
return data_;
|
||||
else if (fname.getName() == "FinishAfter")
|
||||
{
|
||||
auto t =
|
||||
env_->current()->parentCloseTime().time_since_epoch().count();
|
||||
std::string s = std::to_string(t);
|
||||
return Bytes{s.begin(), s.end()};
|
||||
}
|
||||
|
||||
return Unexpected(-1);
|
||||
}
|
||||
};
|
||||
|
||||
struct TestHostFunctions : public HostFunctions
|
||||
{
|
||||
test::jtx::Env& env_;
|
||||
@@ -171,7 +80,7 @@ struct TestHostFunctions : public HostFunctions
|
||||
void const* rt_ = nullptr;
|
||||
|
||||
public:
|
||||
explicit TestHostFunctions(test::jtx::Env& env, int cd = 0)
|
||||
TestHostFunctions(test::jtx::Env& env, int cd = 0)
|
||||
: env_(env), clock_drift_(cd)
|
||||
{
|
||||
auto opt = parseBase58<AccountID>("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh");
|
||||
@@ -212,6 +121,12 @@ public:
|
||||
clock_drift_;
|
||||
}
|
||||
|
||||
Hash
|
||||
getParentLedgerHash() override
|
||||
{
|
||||
return env_.current()->info().parentHash;
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx) override
|
||||
{
|
||||
@@ -238,29 +153,6 @@ public:
|
||||
return Bytes();
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getTxNestedField(Slice const& locator) override
|
||||
{
|
||||
uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41,
|
||||
0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34,
|
||||
0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee,
|
||||
0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00};
|
||||
return Bytes(&a[0], &a[sizeof(a)]);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname) override
|
||||
{
|
||||
// auto const& sn = fname.getName();
|
||||
if (fname == sfBalance)
|
||||
{
|
||||
int64_t x = 10'000;
|
||||
uint8_t const* p = reinterpret_cast<uint8_t const*>(&x);
|
||||
return Bytes{p, p + sizeof(x)};
|
||||
}
|
||||
return data_;
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getCurrentLedgerObjField(SField const& fname) override
|
||||
{
|
||||
@@ -280,24 +172,97 @@ public:
|
||||
return Unexpected(-1);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname) override
|
||||
{
|
||||
// auto const& sn = fname.getName();
|
||||
if (fname == sfBalance)
|
||||
{
|
||||
int64_t x = 10'000;
|
||||
uint8_t const* p = reinterpret_cast<uint8_t const*>(&x);
|
||||
return Bytes{p, p + sizeof(x)};
|
||||
}
|
||||
return data_;
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getTxNestedField(Slice const& locator) override
|
||||
{
|
||||
uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41,
|
||||
0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34,
|
||||
0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee,
|
||||
0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00};
|
||||
return Bytes(&a[0], &a[sizeof(a)]);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getCurrentLedgerObjNestedField(Slice const& locator) override
|
||||
{
|
||||
uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41,
|
||||
0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34,
|
||||
0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee,
|
||||
0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00};
|
||||
return Bytes(&a[0], &a[sizeof(a)]);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override
|
||||
{
|
||||
uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41,
|
||||
0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34,
|
||||
0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee,
|
||||
0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00};
|
||||
return Bytes(&a[0], &a[sizeof(a)]);
|
||||
}
|
||||
|
||||
int32_t
|
||||
getTxArrayLen(SField const& fname) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getCurrentLedgerObjArrayLen(SField const& fname) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getTxNestedArrayLen(Slice const& locator) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getCurrentLedgerObjNestedArrayLen(Slice const& locator) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
int32_t
|
||||
updateData(Bytes const& data) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Hash
|
||||
computeSha512HalfHash(Bytes const& data) override
|
||||
{
|
||||
return env_.current()->info().parentHash;
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
accountKeylet(AccountID const& account) override
|
||||
{
|
||||
@@ -307,6 +272,49 @@ public:
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
credentialKeylet(
|
||||
AccountID const& subject,
|
||||
AccountID const& issuer,
|
||||
Bytes const& credentialType) override
|
||||
{
|
||||
if (!subject || !issuer || credentialType.empty())
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet =
|
||||
keylet::credential(subject, issuer, makeSlice(credentialType));
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
escrowKeylet(AccountID const& account, std::uint32_t seq) override
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::escrow(account, seq);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
oracleKeylet(AccountID const& account, std::uint32_t documentId) override
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::oracle(account, documentId);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getNFT(AccountID const& account, uint256 const& nftId) override
|
||||
{
|
||||
if (!account || !nftId)
|
||||
{
|
||||
return Unexpected(HF_ERR_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
std::string s = "https://ripple.com";
|
||||
return Bytes(s.begin(), s.end());
|
||||
}
|
||||
|
||||
int32_t
|
||||
trace(std::string const& msg, Bytes const& data, bool asHex) override
|
||||
{
|
||||
@@ -350,6 +358,33 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct TestHostFunctionsSink : public TestHostFunctions
|
||||
{
|
||||
test::StreamSink sink_;
|
||||
beast::Journal jlog_;
|
||||
void const* rt_ = nullptr;
|
||||
|
||||
public:
|
||||
explicit TestHostFunctionsSink(test::jtx::Env& env, int cd = 0)
|
||||
: TestHostFunctions(env, cd)
|
||||
, sink_(beast::severities::kDebug)
|
||||
, jlog_(sink_)
|
||||
{
|
||||
}
|
||||
|
||||
test::StreamSink&
|
||||
getSink()
|
||||
{
|
||||
return sink_;
|
||||
}
|
||||
|
||||
beast::Journal
|
||||
getJournal() override
|
||||
{
|
||||
return jlog_;
|
||||
}
|
||||
};
|
||||
|
||||
struct Wasm_test : public beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
@@ -361,9 +396,9 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
Bytes const wasm(ws.begin(), ws.end());
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto const r = engine.run(wasm, "fib", wasmParams(10));
|
||||
auto const re = engine.run(wasm, "fib", wasmParams(10));
|
||||
|
||||
BEAST_EXPECT(r.has_value() && (r->result == 55));
|
||||
BEAST_EXPECT(re.has_value() && (re->result == 55) && (re->cost == 755));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -375,10 +410,11 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
Bytes const wasm(ws.begin(), ws.end());
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto const r =
|
||||
auto const re =
|
||||
engine.run(wasm, "sha512_process", wasmParams(sha512PureHex));
|
||||
|
||||
BEAST_EXPECT(r.has_value() && (r->result == 34432));
|
||||
BEAST_EXPECT(
|
||||
re.has_value() && (re->result == 34432) && (re->cost == 157'452));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -396,9 +432,9 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
static_cast<std::uint32_t>(512),
|
||||
static_cast<std::uint32_t>(b58Hex.size()));
|
||||
auto const s = std::string_view(b58Hex.c_str(), minsz);
|
||||
auto const r = engine.run(wasm, "b58enco", wasmParams(outb, s));
|
||||
auto const re = engine.run(wasm, "b58enco", wasmParams(outb, s));
|
||||
|
||||
BEAST_EXPECT(r.has_value() && r->result);
|
||||
BEAST_EXPECT(re.has_value() && re->result && (re->cost == 3'066'129));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -409,9 +445,10 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
Bytes const wasm(ws.begin(), ws.end());
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto const r = engine.run(wasm, "sp1_groth16_verifier");
|
||||
auto const re = engine.run(wasm, "sp1_groth16_verifier");
|
||||
|
||||
BEAST_EXPECT(r.has_value() && r->result);
|
||||
BEAST_EXPECT(
|
||||
re.has_value() && re->result && (re->cost == 4'191'711'969ll));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -422,9 +459,9 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
Bytes const wasm(ws.begin(), ws.end());
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto const r = engine.run(wasm, "bellman_groth16_test");
|
||||
auto const re = engine.run(wasm, "bellman_groth16_test");
|
||||
|
||||
BEAST_EXPECT(r.has_value() && r->result);
|
||||
BEAST_EXPECT(re.has_value() && re->result && (re->cost == 332'205'984));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -442,24 +479,29 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
std::string const funcName("finish");
|
||||
|
||||
std::vector<WasmImportFunc> imports;
|
||||
WASM_IMPORT_FUNC(imports, getLedgerSqn, &ledgerDataProvider);
|
||||
WASM_IMPORT_FUNC(imports, getLedgerSqn, &ledgerDataProvider, 33);
|
||||
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto r = engine.run(
|
||||
auto re = engine.run(
|
||||
wasm, funcName, {}, imports, nullptr, 1'000'000, env.journal);
|
||||
if (BEAST_EXPECT(r.has_value()))
|
||||
BEAST_EXPECT(!r->result);
|
||||
|
||||
// code takes 4 gas + 1 getLedgerSqn call
|
||||
if (BEAST_EXPECT(re.has_value()))
|
||||
BEAST_EXPECT(!re->result && (re->cost == 37));
|
||||
|
||||
env.close();
|
||||
env.close();
|
||||
env.close();
|
||||
env.close();
|
||||
|
||||
// empty module - run the same module
|
||||
r = engine.run(
|
||||
// empty module - run the same instance
|
||||
re = engine.run(
|
||||
{}, funcName, {}, imports, nullptr, 1'000'000, env.journal);
|
||||
if (BEAST_EXPECT(r.has_value()))
|
||||
BEAST_EXPECT(r->result);
|
||||
|
||||
// code takes 8 gas + 2 getLedgerSqn calls
|
||||
if (BEAST_EXPECT(re.has_value()))
|
||||
BEAST_EXPECT(re->result && (re->cost == 74));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -479,7 +521,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
auto re = runEscrowWasm(
|
||||
wasm, funcName, wasmParams(data), nullptr, -1, env.journal);
|
||||
if (BEAST_EXPECT(re.has_value()))
|
||||
BEAST_EXPECT(re.value().result);
|
||||
BEAST_EXPECT(re.value().result && (re->cost == 838));
|
||||
}
|
||||
{
|
||||
std::string str = "rHb9CJAWyB4rj91VRWn96DkukG4bwdty00";
|
||||
@@ -487,7 +529,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
auto re = runEscrowWasm(
|
||||
wasm, funcName, wasmParams(data), nullptr, -1, env.journal);
|
||||
if (BEAST_EXPECT(re.has_value()))
|
||||
BEAST_EXPECT(!re.value().result);
|
||||
BEAST_EXPECT(!re.value().result && (re->cost == 822));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,7 +555,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
-1,
|
||||
env.journal);
|
||||
if (BEAST_EXPECT(re.has_value()))
|
||||
BEAST_EXPECT(re.value().result);
|
||||
BEAST_EXPECT(re.value().result && (re->cost == 42'212));
|
||||
|
||||
std::vector<uint8_t> const lo_data2(lo_js2.begin(), lo_js2.end());
|
||||
re = runEscrowWasm(
|
||||
@@ -524,7 +566,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
-1,
|
||||
env.journal);
|
||||
if (BEAST_EXPECT(re.has_value()))
|
||||
BEAST_EXPECT(!re.value().result);
|
||||
BEAST_EXPECT(!re.value().result && (re->cost == 41'496));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -566,11 +608,11 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
WasmImpFunc<Add_proto>(
|
||||
imports, "func-add", reinterpret_cast<void*>(&Add));
|
||||
|
||||
auto res = vm.run(wasm, "addTwo", wasmParams(1234, 5678), imports);
|
||||
auto re = vm.run(wasm, "addTwo", wasmParams(1234, 5678), imports);
|
||||
|
||||
// if (res) printf("invokeAdd get the result: %d\n", res.value());
|
||||
|
||||
BEAST_EXPECT(res.has_value() && res->result == 6912);
|
||||
BEAST_EXPECT(re.has_value() && re->result == 6912 && (re->cost == 2));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -587,7 +629,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
|
||||
std::string funcName("mock_escrow");
|
||||
auto re = runEscrowWasm(wasm, funcName, {}, &hfs, 15, env.journal);
|
||||
BEAST_EXPECT(re.error());
|
||||
BEAST_EXPECT(!re && re.error());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -616,10 +658,10 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
{
|
||||
TestHostFunctions nfs(env, 0);
|
||||
std::string funcName("finish");
|
||||
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000);
|
||||
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100'000);
|
||||
if (BEAST_EXPECT(re.has_value()))
|
||||
{
|
||||
BEAST_EXPECT(re.value().result);
|
||||
BEAST_EXPECT(!re->result && (re->cost == 487));
|
||||
// std::cout << "good case result " << re.value().result
|
||||
// << " cost: " << re.value().cost << std::endl;
|
||||
}
|
||||
@@ -636,7 +678,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000);
|
||||
if (BEAST_EXPECT(re.has_value()))
|
||||
{
|
||||
BEAST_EXPECT(!re.value().result);
|
||||
BEAST_EXPECT(!re->result && (re->cost == 487));
|
||||
// std::cout << "bad case (current time < escrow_finish_after "
|
||||
// "time) result "
|
||||
// << re.value().result << " cost: " <<
|
||||
@@ -654,13 +696,13 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
Expected<Bytes, int32_t>
|
||||
getTxField(SField const& fname) override
|
||||
{
|
||||
return Unexpected(-1);
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
}
|
||||
};
|
||||
BadTestHostFunctions nfs(env);
|
||||
std::string funcName("finish");
|
||||
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000);
|
||||
BEAST_EXPECT(re.error());
|
||||
BEAST_EXPECT(re.has_value() && !re->result && (re->cost == 23));
|
||||
// std::cout << "bad case (access nonexistent field) result "
|
||||
// << re.error() << std::endl;
|
||||
}
|
||||
@@ -680,7 +722,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
BadTestHostFunctions nfs(env);
|
||||
std::string funcName("finish");
|
||||
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000);
|
||||
BEAST_EXPECT(!re);
|
||||
BEAST_EXPECT(re.has_value() && !re->result && (re->cost == 23));
|
||||
// std::cout << "bad case (more than MAX_PAGES) result "
|
||||
// << re.error() << std::endl;
|
||||
}
|
||||
@@ -690,10 +732,10 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
auto wasmStr = boost::algorithm::unhex(std::string(wasmHex));
|
||||
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
|
||||
|
||||
TestHostFunctionsOld nfs(&env);
|
||||
TestHostFunctionsSink nfs(env);
|
||||
std::string funcName("recursive");
|
||||
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 1000'000'000);
|
||||
BEAST_EXPECT(re.error());
|
||||
BEAST_EXPECT(!re && re.error());
|
||||
// std::cout << "bad case (deep recursion) result " << re.error()
|
||||
// << std::endl;
|
||||
|
||||
@@ -712,13 +754,32 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
|
||||
BEAST_EXPECT(
|
||||
countSubstr(
|
||||
sink.messages().str(), "WAMR error: failed to call func") ==
|
||||
sink.messages().str(), "WAMR Error: failed to call func") ==
|
||||
1);
|
||||
BEAST_EXPECT(
|
||||
countSubstr(
|
||||
sink.messages().str(),
|
||||
"WAMR Exception: wasm operand stack overflow") == 1);
|
||||
}
|
||||
|
||||
{
|
||||
auto wasmStr = boost::algorithm::unhex(ledgerSqnHex);
|
||||
Bytes wasm(wasmStr.begin(), wasmStr.end());
|
||||
std::string const funcName("finish");
|
||||
TestLedgerDataProvider ledgerDataProvider(&env);
|
||||
|
||||
std::vector<WasmImportFunc> imports;
|
||||
WASM_IMPORT_FUNC2(
|
||||
imports, getLedgerSqn, "get_ledger_sqn2", &ledgerDataProvider);
|
||||
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto re = engine.run(
|
||||
wasm, funcName, {}, imports, nullptr, 1'000'000, env.journal);
|
||||
|
||||
// expected import not provided
|
||||
BEAST_EXPECT(!re);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -741,7 +802,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100'000);
|
||||
if (BEAST_EXPECT(re.has_value()))
|
||||
{
|
||||
BEAST_EXPECT(re->result);
|
||||
BEAST_EXPECT(re->result && (re->cost == 80));
|
||||
// std::cout << "good case result " << re.value().result
|
||||
// << " cost: " << re.value().cost << std::endl;
|
||||
}
|
||||
@@ -763,7 +824,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100'000);
|
||||
if (BEAST_EXPECT(re.has_value()))
|
||||
{
|
||||
BEAST_EXPECT(re->result);
|
||||
BEAST_EXPECT(re->result && (re->cost == 138));
|
||||
// std::cout << "good case result " << re.value().result
|
||||
// << " cost: " << re.value().cost << std::endl;
|
||||
}
|
||||
@@ -787,10 +848,10 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
|
||||
// runing too long
|
||||
// testWasmSP1Verifier();
|
||||
// testWasmBG16Verifier();
|
||||
testWasmBG16Verifier();
|
||||
|
||||
// TODO: needs fix for new host functions interface
|
||||
// testEscrowWasmDN1();
|
||||
// TODO: fix result
|
||||
testEscrowWasmDN1();
|
||||
testEscrowWasmDN2();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -529,6 +529,15 @@ ModuleWrapper::buildImports(
|
||||
reinterpret_cast<wasm_func_callback_with_env_t>(imp.wrap),
|
||||
imp.udata,
|
||||
nullptr);
|
||||
if (!func)
|
||||
throw std::runtime_error(
|
||||
"can't create import function " +
|
||||
imp.name); // LCOV_EXCL_LINE
|
||||
|
||||
if (imp.gas && !wasm_func_set_gas(func, imp.gas))
|
||||
throw std::runtime_error(
|
||||
"can't set gas for import function " +
|
||||
imp.name); // LCOV_EXCL_LINE
|
||||
|
||||
wimports.data[i] = wasm_func_as_extern(func);
|
||||
++impCnt;
|
||||
@@ -862,6 +871,10 @@ WamrEngine::runHlp(
|
||||
// auto j = j_.debug();
|
||||
// #endif
|
||||
|
||||
// currently only 1 module support, possible parallel UT run
|
||||
static std::mutex m;
|
||||
std::lock_guard<decltype(m)> lg(m);
|
||||
|
||||
// Create and instantiate the module.
|
||||
if (!wasmCode.empty())
|
||||
{
|
||||
@@ -894,7 +907,8 @@ WamrEngine::runHlp(
|
||||
}
|
||||
|
||||
assert(res.r.data[0].kind == WASM_I32);
|
||||
|
||||
if (gas == -1)
|
||||
gas = std::numeric_limits<decltype(gas)>::max();
|
||||
WasmResult<int32_t> const ret{res.r.data[0].of.i32, gas - module->getGas()};
|
||||
|
||||
// j << "WAMR Res: " << ret.result << " cost: " << ret.cost << std::endl;
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
#include <xrpld/app/misc/WasmVM.h>
|
||||
|
||||
#include <iwasm/wasm_c_api.h>
|
||||
#include <iwasm/wasm_export.h>
|
||||
#include <wasm_c_api.h>
|
||||
#include <wasm_export.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ struct WasmImportFunc
|
||||
void* udata = nullptr;
|
||||
// wasm_func_callback_with_env_t
|
||||
void* wrap = nullptr;
|
||||
uint32_t gas = 0;
|
||||
};
|
||||
|
||||
#define WASM_IMPORT_FUNC(v, f, ...) \
|
||||
@@ -139,12 +140,14 @@ WasmImpFunc(
|
||||
std::vector<WasmImportFunc>& v,
|
||||
std::string_view imp_name,
|
||||
void* f_wrap,
|
||||
void* data = nullptr)
|
||||
void* data = nullptr,
|
||||
uint32_t gas = 0)
|
||||
{
|
||||
WasmImportFunc e;
|
||||
e.name = imp_name;
|
||||
e.udata = data;
|
||||
e.wrap = f_wrap;
|
||||
e.gas = gas;
|
||||
WasmImpFuncHelper<F>(e);
|
||||
v.push_back(std::move(e));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user