start calling into wasm2c version of platform
This commit is contained in:
8
Makefile
8
Makefile
@@ -1,7 +1,7 @@
|
||||
WASM3_C := $(wildcard wasm3/source/*.c)
|
||||
WASM3_O := $(WASM3_C:.c=.o)
|
||||
|
||||
uw8-wasm3: main.o $(WASM3_O)
|
||||
uw8-wasm3: main.o $(WASM3_O) platform.o wasm-rt-impl.o
|
||||
gcc -g -lm -lSDL2 -o uw8-wasm3 $^
|
||||
|
||||
run: uw8-wasm3 .PHONY
|
||||
@@ -11,10 +11,10 @@ run-ts:
|
||||
deno run --allow-read main.ts
|
||||
|
||||
wasm3/source/%.o: wasm3/source/%.c
|
||||
gcc -g -O2 -c -o $@ $^
|
||||
gcc -g -O2 -c -o $@ $<
|
||||
|
||||
main.o: main.c
|
||||
gcc -g -O2 -c -o main.o main.c
|
||||
%.o: %.c platform.h wasm-rt.h wasm-rt-impl.h
|
||||
gcc -g -O2 -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm uw8-wasm3 main.o $(WASM3_O)
|
||||
|
||||
BIN
loader.wasm
BIN
loader.wasm
Binary file not shown.
65
main.c
65
main.c
@@ -1,5 +1,6 @@
|
||||
#include "wasm3/source/wasm3.h"
|
||||
#include "wasm3/source/m3_env.h"
|
||||
#include "platform.h"
|
||||
#include "SDL2/SDL.h"
|
||||
#include "SDL2/SDL_video.h"
|
||||
#include "SDL2/SDL_render.h"
|
||||
@@ -21,6 +22,27 @@ void* loadFile(size_t* sizeOut, const char* filename) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#define MATH1(name) \
|
||||
f32 Z_envZ_##name(struct Z_env_instance_t* i, f32 v) { \
|
||||
return name##f(v); \
|
||||
}
|
||||
#define MATH2(name) \
|
||||
f32 Z_envZ_##name(struct Z_env_instance_t* i, f32 a, f32 b) { \
|
||||
return name##f(a, b); \
|
||||
}
|
||||
MATH1(acos); MATH1(asin); MATH1(atan); MATH2(atan2);
|
||||
MATH1(cos); MATH1(sin); MATH1(tan);
|
||||
MATH1(exp); MATH2(pow);
|
||||
void Z_envZ_logChar(struct Z_env_instance_t* i, u32 c) {}
|
||||
|
||||
u32 reservedGlobal;
|
||||
#define G_RESERVED(n) u32* Z_envZ_g_reserved##n(struct Z_env_instance_t* i) { return &reservedGlobal; }
|
||||
G_RESERVED(0); G_RESERVED(1); G_RESERVED(2); G_RESERVED(3);
|
||||
G_RESERVED(4); G_RESERVED(5); G_RESERVED(6); G_RESERVED(7);
|
||||
G_RESERVED(8); G_RESERVED(9); G_RESERVED(10); G_RESERVED(11);
|
||||
G_RESERVED(12); G_RESERVED(13); G_RESERVED(14); G_RESERVED(15);
|
||||
wasm_rt_memory_t* Z_envZ_memory(struct Z_env_instance_t* i) { return (wasm_rt_memory_t*)i; }
|
||||
|
||||
void verifyM3(IM3Runtime runtime, M3Result result) {
|
||||
if (result != m3Err_none) {
|
||||
M3ErrorInfo info;
|
||||
@@ -86,6 +108,25 @@ m3ApiRawFunction(platformTrampoline) {
|
||||
m3ApiSuccess();
|
||||
}
|
||||
|
||||
m3ApiRawFunction(callCircle) {
|
||||
Z_platformZ_circle((Z_platform_instance_t*)_ctx->userdata, *(f32*)&_sp[0], *(f32*)&_sp[1], *(f32*)&_sp[2], _sp[3]);
|
||||
m3ApiSuccess();
|
||||
}
|
||||
|
||||
m3ApiRawFunction(callBlitSprite) {
|
||||
Z_platformZ_blitSprite((Z_platform_instance_t*)_ctx->userdata, _sp[0], _sp[1], _sp[2], _sp[3], _sp[4]);
|
||||
m3ApiSuccess();
|
||||
}
|
||||
|
||||
struct {
|
||||
const char* name;
|
||||
const char* signature;
|
||||
M3RawCall function;
|
||||
} cPlatformFunctions[] = {
|
||||
{ "circle", "v(fffi)", callCircle },
|
||||
{ "blitSprite", "v(iiiii)", callBlitSprite }
|
||||
};
|
||||
|
||||
void appendType(char* signature, M3ValueType type) {
|
||||
if(type == c_m3Type_i32) {
|
||||
strcat(signature, "i");
|
||||
@@ -99,10 +140,18 @@ void appendType(char* signature, M3ValueType type) {
|
||||
}
|
||||
}
|
||||
|
||||
void linkPlatformFunctions(IM3Runtime runtime, IM3Module cartMod, IM3Module platformMod) {
|
||||
void linkPlatformFunctions(IM3Runtime runtime, IM3Module cartMod, IM3Module platformMod, Z_platform_instance_t* platformInstance) {
|
||||
for(u32 functionIndex = 0; functionIndex < platformMod->numFunctions; ++functionIndex) {
|
||||
M3Function function = platformMod->functions[functionIndex];
|
||||
if(function.export_name != NULL) {
|
||||
bool foundCImpl = false;
|
||||
for(int i = 0; i * sizeof(cPlatformFunctions[0]) < sizeof(cPlatformFunctions); ++i) {
|
||||
if(strcmp(function.export_name, cPlatformFunctions[i].name) == 0) {
|
||||
m3_LinkRawFunctionEx(cartMod, "env", function.export_name, cPlatformFunctions[i].signature, cPlatformFunctions[i].function, platformInstance);
|
||||
foundCImpl = true;
|
||||
}
|
||||
}
|
||||
if(!foundCImpl) {
|
||||
IM3Function iFunc;
|
||||
verifyM3(runtime, m3_FindFunction(&iFunc, runtime, function.export_name));
|
||||
char signature[128] = { 0 };
|
||||
@@ -120,6 +169,7 @@ void linkPlatformFunctions(IM3Runtime runtime, IM3Module cartMod, IM3Module plat
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* loadUw8(uint32_t* sizeOut, IM3Runtime runtime, IM3Function loadFunc, const char* filename) {
|
||||
size_t uw8Size;
|
||||
@@ -141,6 +191,8 @@ const uint32_t uw8buttonScanCodes[] = {
|
||||
typedef struct {
|
||||
IM3Runtime runtime;
|
||||
IM3Module platform;
|
||||
wasm_rt_memory_t memory_c;
|
||||
Z_platform_instance_t platform_c;
|
||||
IM3Module cart;
|
||||
} Uw8Runtime;
|
||||
|
||||
@@ -150,6 +202,12 @@ void initRuntime(Uw8Runtime* runtime, IM3Environment env,
|
||||
runtime->runtime->memory.maxPages = 4;
|
||||
verifyM3(runtime->runtime, ResizeMemory(runtime->runtime, 4));
|
||||
|
||||
runtime->memory_c.data = m3_GetMemory(runtime->runtime, NULL, 0);
|
||||
runtime->memory_c.max_pages = 4;
|
||||
runtime->memory_c.pages = 4;
|
||||
runtime->memory_c.size = 256*1024;
|
||||
Z_platform_instantiate(&runtime->platform_c, (struct Z_env_instance_t*)&runtime->memory_c);
|
||||
|
||||
verifyM3(runtime->runtime, m3_ParseModule(env, &runtime->platform, platform, platformSize));
|
||||
runtime->platform->memoryImported = true;
|
||||
verifyM3(runtime->runtime, m3_LoadModule(runtime->runtime, runtime->platform));
|
||||
@@ -161,7 +219,7 @@ void initRuntime(Uw8Runtime* runtime, IM3Environment env,
|
||||
runtime->platform->memoryImported = true;
|
||||
verifyM3(runtime->runtime, m3_LoadModule(runtime->runtime, runtime->cart));
|
||||
linkSystemFunctions(runtime->runtime, runtime->cart);
|
||||
linkPlatformFunctions(runtime->runtime, runtime->cart, runtime->platform);
|
||||
linkPlatformFunctions(runtime->runtime, runtime->cart, runtime->platform, &runtime->platform_c);
|
||||
verifyM3(runtime->runtime, m3_CompileModule(runtime->cart));
|
||||
verifyM3(runtime->runtime, m3_RunStart(runtime->cart));
|
||||
}
|
||||
@@ -226,6 +284,9 @@ int main(int argc, const char** argv) {
|
||||
|
||||
m3_FreeRuntime(loaderRuntime);
|
||||
|
||||
wasm_rt_init();
|
||||
Z_platform_init_module();
|
||||
|
||||
bool quit = false;
|
||||
while(!quit) {
|
||||
Uw8Runtime runtime;
|
||||
|
||||
4242
platform.c
Normal file
4242
platform.c
Normal file
File diff suppressed because it is too large
Load Diff
147
platform.h
Normal file
147
platform.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/* Automatically generated by wasm2c */
|
||||
#ifndef PLATFORM_H_GENERATED_
|
||||
#define PLATFORM_H_GENERATED_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "wasm-rt.h"
|
||||
|
||||
/* TODO(binji): only use stdint.h types in header */
|
||||
#ifndef WASM_RT_CORE_TYPES_DEFINED
|
||||
#define WASM_RT_CORE_TYPES_DEFINED
|
||||
typedef uint8_t u8;
|
||||
typedef int8_t s8;
|
||||
typedef uint16_t u16;
|
||||
typedef int16_t s16;
|
||||
typedef uint32_t u32;
|
||||
typedef int32_t s32;
|
||||
typedef uint64_t u64;
|
||||
typedef int64_t s64;
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Z_env_instance_t;
|
||||
extern wasm_rt_memory_t* Z_envZ_memory(struct Z_env_instance_t*);
|
||||
|
||||
typedef struct Z_platform_instance_t {
|
||||
struct Z_env_instance_t* Z_env_instance;
|
||||
/* import: 'env' 'memory' */
|
||||
wasm_rt_memory_t *Z_envZ_memory;
|
||||
u64 w2c_g0;
|
||||
u32 w2c_g1;
|
||||
u32 w2c_g2;
|
||||
u32 w2c_g3;
|
||||
u32 w2c_g4;
|
||||
u32 w2c_g5;
|
||||
u32 w2c_g6;
|
||||
} Z_platform_instance_t;
|
||||
|
||||
void Z_platform_init_module(void);
|
||||
void Z_platform_instantiate(Z_platform_instance_t*, struct Z_env_instance_t*);
|
||||
void Z_platform_free(Z_platform_instance_t*);
|
||||
|
||||
/* import: 'env' 'cos' */
|
||||
f32 Z_envZ_cos(struct Z_env_instance_t*, f32);
|
||||
/* import: 'env' 'exp' */
|
||||
f32 Z_envZ_exp(struct Z_env_instance_t*, f32);
|
||||
/* import: 'env' 'logChar' */
|
||||
void Z_envZ_logChar(struct Z_env_instance_t*, u32);
|
||||
/* import: 'env' 'pow' */
|
||||
f32 Z_envZ_pow(struct Z_env_instance_t*, f32, f32);
|
||||
/* import: 'env' 'sin' */
|
||||
f32 Z_envZ_sin(struct Z_env_instance_t*, f32);
|
||||
|
||||
/* export: 'time' */
|
||||
f32 Z_platformZ_time(Z_platform_instance_t*);
|
||||
|
||||
/* export: 'isButtonPressed' */
|
||||
u32 Z_platformZ_isButtonPressed(Z_platform_instance_t*, u32);
|
||||
|
||||
/* export: 'isButtonTriggered' */
|
||||
u32 Z_platformZ_isButtonTriggered(Z_platform_instance_t*, u32);
|
||||
|
||||
/* export: 'random' */
|
||||
u32 Z_platformZ_random(Z_platform_instance_t*);
|
||||
|
||||
/* export: 'random64' */
|
||||
u64 Z_platformZ_random64(Z_platform_instance_t*);
|
||||
|
||||
/* export: 'randomf' */
|
||||
f32 Z_platformZ_randomf(Z_platform_instance_t*);
|
||||
|
||||
/* export: 'randomSeed' */
|
||||
void Z_platformZ_randomSeed(Z_platform_instance_t*, u32);
|
||||
|
||||
/* export: 'fmod' */
|
||||
f32 Z_platformZ_fmod(Z_platform_instance_t*, f32, f32);
|
||||
|
||||
/* export: 'cls' */
|
||||
void Z_platformZ_cls(Z_platform_instance_t*, u32);
|
||||
|
||||
/* export: 'setPixel' */
|
||||
void Z_platformZ_setPixel(Z_platform_instance_t*, u32, u32, u32);
|
||||
|
||||
/* export: 'getPixel' */
|
||||
u32 Z_platformZ_getPixel(Z_platform_instance_t*, u32, u32);
|
||||
|
||||
/* export: 'hline' */
|
||||
void Z_platformZ_hline(Z_platform_instance_t*, u32, u32, u32, u32);
|
||||
|
||||
/* export: 'rectangle' */
|
||||
void Z_platformZ_rectangle(Z_platform_instance_t*, f32, f32, f32, f32, u32);
|
||||
|
||||
/* export: 'rectangleOutline' */
|
||||
void Z_platformZ_rectangleOutline(Z_platform_instance_t*, f32, f32, f32, f32, u32);
|
||||
|
||||
/* export: 'circle' */
|
||||
void Z_platformZ_circle(Z_platform_instance_t*, f32, f32, f32, u32);
|
||||
|
||||
/* export: 'circleOutline' */
|
||||
void Z_platformZ_circleOutline(Z_platform_instance_t*, f32, f32, f32, u32);
|
||||
|
||||
/* export: 'line' */
|
||||
void Z_platformZ_line(Z_platform_instance_t*, f32, f32, f32, f32, u32);
|
||||
|
||||
/* export: 'blitSprite' */
|
||||
void Z_platformZ_blitSprite(Z_platform_instance_t*, u32, u32, u32, u32, u32);
|
||||
|
||||
/* export: 'grabSprite' */
|
||||
void Z_platformZ_grabSprite(Z_platform_instance_t*, u32, u32, u32, u32, u32);
|
||||
|
||||
/* export: 'printChar' */
|
||||
void Z_platformZ_printChar(Z_platform_instance_t*, u32);
|
||||
|
||||
/* export: 'printString' */
|
||||
void Z_platformZ_printString(Z_platform_instance_t*, u32);
|
||||
|
||||
/* export: 'printInt' */
|
||||
void Z_platformZ_printInt(Z_platform_instance_t*, u32);
|
||||
|
||||
/* export: 'setTextColor' */
|
||||
void Z_platformZ_setTextColor(Z_platform_instance_t*, u32);
|
||||
|
||||
/* export: 'setBackgroundColor' */
|
||||
void Z_platformZ_setBackgroundColor(Z_platform_instance_t*, u32);
|
||||
|
||||
/* export: 'setCursorPosition' */
|
||||
void Z_platformZ_setCursorPosition(Z_platform_instance_t*, u32, u32);
|
||||
|
||||
/* export: 'playNote' */
|
||||
void Z_platformZ_playNote(Z_platform_instance_t*, u32, u32);
|
||||
|
||||
/* export: 'endFrame' */
|
||||
void Z_platformZ_endFrame(Z_platform_instance_t*);
|
||||
|
||||
/* export: 'sndGes' */
|
||||
f32 Z_platformZ_sndGes(Z_platform_instance_t*, u32);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PLATFORM_H_GENERATED_ */
|
||||
BIN
platform.uw8
BIN
platform.uw8
Binary file not shown.
504
wasm-rt-impl.c
Normal file
504
wasm-rt-impl.c
Normal file
@@ -0,0 +1,504 @@
|
||||
/*
|
||||
* Copyright 2018 WebAssembly Community Group participants
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "wasm-rt-impl.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY && \
|
||||
!defined(_WIN32)
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if _MSC_VER
|
||||
#include <malloc.h>
|
||||
#define alloca _alloca
|
||||
#endif
|
||||
|
||||
#define PAGE_SIZE 65536
|
||||
#define MAX_EXCEPTION_SIZE PAGE_SIZE
|
||||
|
||||
typedef struct FuncType {
|
||||
wasm_rt_type_t* params;
|
||||
wasm_rt_type_t* results;
|
||||
uint32_t param_count;
|
||||
uint32_t result_count;
|
||||
} FuncType;
|
||||
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
|
||||
static bool g_signal_handler_installed = false;
|
||||
#ifdef _WIN32
|
||||
static void* g_sig_handler_handle = 0;
|
||||
#else
|
||||
static char* g_alt_stack = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if WASM_RT_USE_STACK_DEPTH_COUNT
|
||||
uint32_t wasm_rt_call_stack_depth;
|
||||
uint32_t wasm_rt_saved_call_stack_depth;
|
||||
#endif
|
||||
|
||||
static FuncType* g_func_types;
|
||||
static uint32_t g_func_type_count;
|
||||
|
||||
jmp_buf wasm_rt_jmp_buf;
|
||||
|
||||
static uint32_t g_active_exception_tag;
|
||||
static uint8_t g_active_exception[MAX_EXCEPTION_SIZE];
|
||||
static uint32_t g_active_exception_size;
|
||||
|
||||
static jmp_buf* g_unwind_target;
|
||||
|
||||
void wasm_rt_trap(wasm_rt_trap_t code) {
|
||||
assert(code != WASM_RT_TRAP_NONE);
|
||||
#if WASM_RT_USE_STACK_DEPTH_COUNT
|
||||
wasm_rt_call_stack_depth = wasm_rt_saved_call_stack_depth;
|
||||
#endif
|
||||
|
||||
#ifdef WASM_RT_TRAP_HANDLER
|
||||
WASM_RT_TRAP_HANDLER(code);
|
||||
wasm_rt_unreachable();
|
||||
#else
|
||||
WASM_RT_LONGJMP(wasm_rt_jmp_buf, code);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool func_types_are_equal(FuncType* a, FuncType* b) {
|
||||
if (a->param_count != b->param_count || a->result_count != b->result_count)
|
||||
return 0;
|
||||
uint32_t i;
|
||||
for (i = 0; i < a->param_count; ++i)
|
||||
if (a->params[i] != b->params[i])
|
||||
return 0;
|
||||
for (i = 0; i < a->result_count; ++i)
|
||||
if (a->results[i] != b->results[i])
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t wasm_rt_register_func_type(uint32_t param_count,
|
||||
uint32_t result_count,
|
||||
...) {
|
||||
size_t param_size = param_count * sizeof(wasm_rt_type_t);
|
||||
size_t result_size = result_count * sizeof(wasm_rt_type_t);
|
||||
FuncType func_type;
|
||||
func_type.param_count = param_count;
|
||||
func_type.params = alloca(param_size);
|
||||
func_type.result_count = result_count;
|
||||
func_type.results = alloca(result_size);
|
||||
|
||||
va_list args;
|
||||
va_start(args, result_count);
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < param_count; ++i)
|
||||
func_type.params[i] = va_arg(args, wasm_rt_type_t);
|
||||
for (i = 0; i < result_count; ++i)
|
||||
func_type.results[i] = va_arg(args, wasm_rt_type_t);
|
||||
va_end(args);
|
||||
|
||||
for (i = 0; i < g_func_type_count; ++i)
|
||||
if (func_types_are_equal(&g_func_types[i], &func_type))
|
||||
return i + 1;
|
||||
|
||||
// This is a new/unseed type. Copy our stack allocated params/results into
|
||||
// permanent heap allocated space.
|
||||
wasm_rt_type_t* params = malloc(param_size);
|
||||
wasm_rt_type_t* results = malloc(result_size);
|
||||
memcpy(params, func_type.params, param_size);
|
||||
memcpy(results, func_type.results, result_size);
|
||||
func_type.params = params;
|
||||
func_type.results = results;
|
||||
|
||||
uint32_t idx = g_func_type_count++;
|
||||
g_func_types = realloc(g_func_types, g_func_type_count * sizeof(FuncType));
|
||||
g_func_types[idx] = func_type;
|
||||
return idx + 1;
|
||||
}
|
||||
|
||||
uint32_t wasm_rt_register_tag(uint32_t size) {
|
||||
static uint32_t s_tag_count = 0;
|
||||
|
||||
if (size > MAX_EXCEPTION_SIZE) {
|
||||
wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION);
|
||||
}
|
||||
return s_tag_count++;
|
||||
}
|
||||
|
||||
void wasm_rt_load_exception(uint32_t tag, uint32_t size, const void* values) {
|
||||
assert(size <= MAX_EXCEPTION_SIZE);
|
||||
|
||||
g_active_exception_tag = tag;
|
||||
g_active_exception_size = size;
|
||||
|
||||
if (size) {
|
||||
memcpy(g_active_exception, values, size);
|
||||
}
|
||||
}
|
||||
|
||||
WASM_RT_NO_RETURN void wasm_rt_throw(void) {
|
||||
WASM_RT_LONGJMP(*g_unwind_target, WASM_RT_TRAP_UNCAUGHT_EXCEPTION);
|
||||
}
|
||||
|
||||
WASM_RT_UNWIND_TARGET* wasm_rt_get_unwind_target(void) {
|
||||
return g_unwind_target;
|
||||
}
|
||||
|
||||
void wasm_rt_set_unwind_target(WASM_RT_UNWIND_TARGET* target) {
|
||||
g_unwind_target = target;
|
||||
}
|
||||
|
||||
uint32_t wasm_rt_exception_tag(void) {
|
||||
return g_active_exception_tag;
|
||||
}
|
||||
|
||||
uint32_t wasm_rt_exception_size(void) {
|
||||
return g_active_exception_size;
|
||||
}
|
||||
|
||||
void* wasm_rt_exception(void) {
|
||||
return g_active_exception;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static void* os_mmap(size_t size) {
|
||||
void* ret = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int os_munmap(void* addr, size_t size) {
|
||||
// Windows can only unmap the whole mapping
|
||||
(void)size; /* unused */
|
||||
BOOL succeeded = VirtualFree(addr, 0, MEM_RELEASE);
|
||||
return succeeded ? 0 : -1;
|
||||
}
|
||||
|
||||
static int os_mprotect(void* addr, size_t size) {
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
void* ret = VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
if (ret == addr) {
|
||||
return 0;
|
||||
}
|
||||
VirtualFree(addr, 0, MEM_RELEASE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void os_print_last_error(const char* msg) {
|
||||
DWORD errorMessageID = GetLastError();
|
||||
if (errorMessageID != 0) {
|
||||
LPSTR messageBuffer = 0;
|
||||
// The api creates the buffer that holds the message
|
||||
size_t size = FormatMessageA(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPSTR)&messageBuffer, 0, NULL);
|
||||
(void)size;
|
||||
printf("%s. %s\n", msg, messageBuffer);
|
||||
LocalFree(messageBuffer);
|
||||
} else {
|
||||
printf("%s. No error code.\n", msg);
|
||||
}
|
||||
}
|
||||
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
|
||||
|
||||
static LONG os_signal_handler(PEXCEPTION_POINTERS info) {
|
||||
if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
|
||||
wasm_rt_trap(WASM_RT_TRAP_OOB);
|
||||
} else if (info->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
|
||||
wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION);
|
||||
}
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
static void os_install_signal_handler(void) {
|
||||
g_sig_handler_handle =
|
||||
AddVectoredExceptionHandler(1 /* CALL_FIRST */, os_signal_handler);
|
||||
}
|
||||
|
||||
static void os_cleanup_signal_handler(void) {
|
||||
RemoveVectoredExceptionHandler(g_sig_handler_handle);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
static void* os_mmap(size_t size) {
|
||||
int map_prot = PROT_NONE;
|
||||
int map_flags = MAP_ANONYMOUS | MAP_PRIVATE;
|
||||
uint8_t* addr = mmap(NULL, size, map_prot, map_flags, -1, 0);
|
||||
if (addr == MAP_FAILED)
|
||||
return NULL;
|
||||
return addr;
|
||||
}
|
||||
|
||||
static int os_munmap(void* addr, size_t size) {
|
||||
return munmap(addr, size);
|
||||
}
|
||||
|
||||
static int os_mprotect(void* addr, size_t size) {
|
||||
return mprotect(addr, size, PROT_READ | PROT_WRITE);
|
||||
}
|
||||
|
||||
static void os_print_last_error(const char* msg) {
|
||||
perror(msg);
|
||||
}
|
||||
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
|
||||
static void os_signal_handler(int sig, siginfo_t* si, void* unused) {
|
||||
if (si->si_code == SEGV_ACCERR) {
|
||||
wasm_rt_trap(WASM_RT_TRAP_OOB);
|
||||
} else {
|
||||
wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION);
|
||||
}
|
||||
}
|
||||
|
||||
static void os_install_signal_handler(void) {
|
||||
/* Use alt stack to handle SIGSEGV from stack overflow */
|
||||
g_alt_stack = malloc(SIGSTKSZ);
|
||||
if (g_alt_stack == NULL) {
|
||||
perror("malloc failed");
|
||||
abort();
|
||||
}
|
||||
|
||||
stack_t ss;
|
||||
ss.ss_sp = g_alt_stack;
|
||||
ss.ss_flags = 0;
|
||||
ss.ss_size = SIGSTKSZ;
|
||||
if (sigaltstack(&ss, NULL) != 0) {
|
||||
perror("sigaltstack failed");
|
||||
abort();
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_sigaction = os_signal_handler;
|
||||
|
||||
/* Install SIGSEGV and SIGBUS handlers, since macOS seems to use SIGBUS. */
|
||||
if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL) != 0) {
|
||||
perror("sigaction failed");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void os_cleanup_signal_handler(void) {
|
||||
/* Undo what was done in os_install_signal_handler */
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = SIG_DFL;
|
||||
if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL)) {
|
||||
perror("sigaction failed");
|
||||
abort();
|
||||
}
|
||||
|
||||
if (sigaltstack(NULL, NULL) != 0) {
|
||||
perror("sigaltstack failed");
|
||||
abort();
|
||||
}
|
||||
|
||||
free(g_alt_stack);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
void wasm_rt_init(void) {
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
|
||||
if (!g_signal_handler_installed) {
|
||||
g_signal_handler_installed = true;
|
||||
os_install_signal_handler();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool wasm_rt_is_initialized(void) {
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
|
||||
return g_signal_handler_installed;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void wasm_rt_free(void) {
|
||||
for (uint32_t i = 0; i < g_func_type_count; ++i) {
|
||||
free(g_func_types[i].params);
|
||||
free(g_func_types[i].results);
|
||||
}
|
||||
|
||||
g_func_type_count = 0;
|
||||
free(g_func_types);
|
||||
g_func_types = NULL;
|
||||
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
|
||||
os_cleanup_signal_handler();
|
||||
#endif
|
||||
}
|
||||
|
||||
void wasm_rt_allocate_memory(wasm_rt_memory_t* memory,
|
||||
uint32_t initial_pages,
|
||||
uint32_t max_pages) {
|
||||
uint32_t byte_length = initial_pages * PAGE_SIZE;
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER
|
||||
/* Reserve 8GiB. */
|
||||
void* addr = os_mmap(0x200000000ul);
|
||||
|
||||
if (!addr) {
|
||||
os_print_last_error("os_mmap failed.");
|
||||
abort();
|
||||
}
|
||||
int ret = os_mprotect(addr, byte_length);
|
||||
if (ret != 0) {
|
||||
os_print_last_error("os_mprotect failed.");
|
||||
abort();
|
||||
}
|
||||
memory->data = addr;
|
||||
#else
|
||||
memory->data = calloc(byte_length, 1);
|
||||
#endif
|
||||
memory->size = byte_length;
|
||||
memory->pages = initial_pages;
|
||||
memory->max_pages = max_pages;
|
||||
}
|
||||
|
||||
uint32_t wasm_rt_grow_memory(wasm_rt_memory_t* memory, uint32_t delta) {
|
||||
uint32_t old_pages = memory->pages;
|
||||
uint32_t new_pages = memory->pages + delta;
|
||||
if (new_pages == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (new_pages < old_pages || new_pages > memory->max_pages) {
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
uint32_t old_size = old_pages * PAGE_SIZE;
|
||||
uint32_t new_size = new_pages * PAGE_SIZE;
|
||||
uint32_t delta_size = delta * PAGE_SIZE;
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER
|
||||
uint8_t* new_data = memory->data;
|
||||
int ret = os_mprotect(new_data + old_size, delta_size);
|
||||
if (ret != 0) {
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
#else
|
||||
uint8_t* new_data = realloc(memory->data, new_size);
|
||||
if (new_data == NULL) {
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
#if !WABT_BIG_ENDIAN
|
||||
memset(new_data + old_size, 0, delta_size);
|
||||
#endif
|
||||
#endif
|
||||
#if WABT_BIG_ENDIAN
|
||||
memmove(new_data + new_size - old_size, new_data, old_size);
|
||||
memset(new_data, 0, delta_size);
|
||||
#endif
|
||||
memory->pages = new_pages;
|
||||
memory->size = new_size;
|
||||
memory->data = new_data;
|
||||
return old_pages;
|
||||
}
|
||||
|
||||
void wasm_rt_free_memory(wasm_rt_memory_t* memory) {
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER
|
||||
os_munmap(memory->data, memory->size); // ignore error?
|
||||
#else
|
||||
free(memory->data);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define DEFINE_TABLE_OPS(type) \
|
||||
void wasm_rt_allocate_##type##_table(wasm_rt_##type##_table_t* table, \
|
||||
uint32_t elements, \
|
||||
uint32_t max_elements) { \
|
||||
table->size = elements; \
|
||||
table->max_size = max_elements; \
|
||||
table->data = calloc(table->size, sizeof(wasm_rt_##type##_t)); \
|
||||
} \
|
||||
void wasm_rt_free_##type##_table(wasm_rt_##type##_table_t* table) { \
|
||||
free(table->data); \
|
||||
} \
|
||||
uint32_t wasm_rt_grow_##type##_table(wasm_rt_##type##_table_t* table, \
|
||||
uint32_t delta, \
|
||||
wasm_rt_##type##_t init) { \
|
||||
uint32_t old_elems = table->size; \
|
||||
uint64_t new_elems = (uint64_t)table->size + delta; \
|
||||
if (new_elems == 0) { \
|
||||
return 0; \
|
||||
} \
|
||||
if ((new_elems < old_elems) || (new_elems > table->max_size)) { \
|
||||
return (uint32_t)-1; \
|
||||
} \
|
||||
void* new_data = \
|
||||
realloc(table->data, new_elems * sizeof(wasm_rt_##type##_t)); \
|
||||
if (!new_data) { \
|
||||
return (uint32_t)-1; \
|
||||
} \
|
||||
table->data = new_data; \
|
||||
table->size = new_elems; \
|
||||
for (uint32_t i = old_elems; i < new_elems; i++) { \
|
||||
table->data[i] = init; \
|
||||
} \
|
||||
return old_elems; \
|
||||
}
|
||||
|
||||
DEFINE_TABLE_OPS(funcref)
|
||||
DEFINE_TABLE_OPS(externref)
|
||||
|
||||
const char* wasm_rt_strerror(wasm_rt_trap_t trap) {
|
||||
switch (trap) {
|
||||
case WASM_RT_TRAP_NONE:
|
||||
return "No error";
|
||||
case WASM_RT_TRAP_OOB:
|
||||
#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS
|
||||
return "Out-of-bounds access in linear memory or a table, or call stack "
|
||||
"exhausted";
|
||||
#else
|
||||
return "Out-of-bounds access in linear memory or a table";
|
||||
case WASM_RT_TRAP_EXHAUSTION:
|
||||
return "Call stack exhausted";
|
||||
#endif
|
||||
case WASM_RT_TRAP_INT_OVERFLOW:
|
||||
return "Integer overflow on divide or truncation";
|
||||
case WASM_RT_TRAP_DIV_BY_ZERO:
|
||||
return "Integer divide by zero";
|
||||
case WASM_RT_TRAP_INVALID_CONVERSION:
|
||||
return "Conversion from NaN to integer";
|
||||
case WASM_RT_TRAP_UNREACHABLE:
|
||||
return "Unreachable instruction executed";
|
||||
case WASM_RT_TRAP_CALL_INDIRECT:
|
||||
return "Invalid call_indirect";
|
||||
case WASM_RT_TRAP_UNCAUGHT_EXCEPTION:
|
||||
return "Uncaught exception";
|
||||
}
|
||||
return "invalid trap code";
|
||||
}
|
||||
72
wasm-rt-impl.h
Normal file
72
wasm-rt-impl.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2018 WebAssembly Community Group participants
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef WASM_RT_IMPL_H_
|
||||
#define WASM_RT_IMPL_H_
|
||||
|
||||
#include "wasm-rt.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** A setjmp buffer used for handling traps. */
|
||||
extern jmp_buf wasm_rt_jmp_buf;
|
||||
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !defined(_WIN32)
|
||||
#define WASM_RT_LONGJMP(buf, val) siglongjmp(buf, val)
|
||||
#else
|
||||
#define WASM_RT_LONGJMP(buf, val) longjmp(buf, val)
|
||||
#endif
|
||||
|
||||
#if WASM_RT_USE_STACK_DEPTH_COUNT
|
||||
/** Saved call stack depth that will be restored in case a trap occurs. */
|
||||
extern uint32_t wasm_rt_saved_call_stack_depth;
|
||||
#define WASM_RT_SAVE_STACK_DEPTH() \
|
||||
wasm_rt_saved_call_stack_depth = wasm_rt_call_stack_depth
|
||||
#else
|
||||
#define WASM_RT_SAVE_STACK_DEPTH() (void)0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Convenience macro to use before calling a wasm function. On first execution
|
||||
* it will return `WASM_RT_TRAP_NONE` (i.e. 0). If the function traps, it will
|
||||
* jump back and return the trap that occurred.
|
||||
*
|
||||
* ```
|
||||
* wasm_rt_trap_t code = wasm_rt_impl_try();
|
||||
* if (code != 0) {
|
||||
* printf("A trap occurred with code: %d\n", code);
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* // Call the potentially-trapping function.
|
||||
* my_wasm_func();
|
||||
* ```
|
||||
*/
|
||||
#define wasm_rt_impl_try() \
|
||||
(WASM_RT_SAVE_STACK_DEPTH(), wasm_rt_set_unwind_target(&wasm_rt_jmp_buf), \
|
||||
WASM_RT_SETJMP(wasm_rt_jmp_buf))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WASM_RT_IMPL_H_ */
|
||||
409
wasm-rt.h
Normal file
409
wasm-rt.h
Normal file
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* Copyright 2018 WebAssembly Community Group participants
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef WASM_RT_H_
|
||||
#define WASM_RT_H_
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_expect)
|
||||
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
#define LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
#else
|
||||
#define UNLIKELY(x) (x)
|
||||
#define LIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_memcpy)
|
||||
#define wasm_rt_memcpy __builtin_memcpy
|
||||
#else
|
||||
#define wasm_rt_memcpy memcpy
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_unreachable)
|
||||
#define wasm_rt_unreachable __builtin_unreachable
|
||||
#else
|
||||
#define wasm_rt_unreachable abort
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Enable memory checking via a signal handler via the following definition:
|
||||
*
|
||||
* #define WASM_RT_MEMCHECK_SIGNAL_HANDLER 1
|
||||
*
|
||||
* This is usually 10%-25% faster, but requires OS-specific support.
|
||||
*/
|
||||
|
||||
#ifndef WASM_RT_SKIP_SIGNAL_RECOVERY
|
||||
#define WASM_RT_SKIP_SIGNAL_RECOVERY 0
|
||||
#endif
|
||||
|
||||
/** Signal handler not supported on 32-bit platforms. */
|
||||
#if UINTPTR_MAX > 0xffffffff
|
||||
|
||||
#define WASM_RT_SIGNAL_RECOVERY_SUPPORTED 1
|
||||
|
||||
/* Signal handler is supported. Use it by default. */
|
||||
#ifndef WASM_RT_MEMCHECK_SIGNAL_HANDLER
|
||||
#define WASM_RT_MEMCHECK_SIGNAL_HANDLER 1
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define WASM_RT_SIGNAL_RECOVERY_SUPPORTED 0
|
||||
|
||||
/* Signal handler is not supported. */
|
||||
#ifndef WASM_RT_MEMCHECK_SIGNAL_HANDLER
|
||||
#define WASM_RT_MEMCHECK_SIGNAL_HANDLER 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && \
|
||||
(!WASM_RT_SKIP_SIGNAL_RECOVERY && !WASM_RT_SIGNAL_RECOVERY_SUPPORTED)
|
||||
/* The signal handler is not supported, error out if the user was trying to
|
||||
* enable it. */
|
||||
#error "Signal handler is not supported for this OS/Architecture!"
|
||||
#endif
|
||||
|
||||
#ifndef WASM_RT_USE_STACK_DEPTH_COUNT
|
||||
/* The signal handler on POSIX can detect call stack overflows. On windows, or
|
||||
* platforms without a signal handler, we use stack depth counting. */
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !defined(_WIN32)
|
||||
#define WASM_RT_USE_STACK_DEPTH_COUNT 0
|
||||
#else
|
||||
#define WASM_RT_USE_STACK_DEPTH_COUNT 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if WASM_RT_USE_STACK_DEPTH_COUNT
|
||||
/**
|
||||
* When the signal handler cannot be used to detect stack overflows, stack depth
|
||||
* is limited explicitly. The maximum stack depth before trapping can be
|
||||
* configured by defining this symbol before including wasm-rt when building the
|
||||
* generated c files, for example:
|
||||
*
|
||||
* ```
|
||||
* cc -c -DWASM_RT_MAX_CALL_STACK_DEPTH=100 my_module.c -o my_module.o
|
||||
* ```
|
||||
*/
|
||||
#ifndef WASM_RT_MAX_CALL_STACK_DEPTH
|
||||
#define WASM_RT_MAX_CALL_STACK_DEPTH 500
|
||||
#endif
|
||||
|
||||
/** Current call stack depth. */
|
||||
extern uint32_t wasm_rt_call_stack_depth;
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define WASM_RT_NO_RETURN __declspec(noreturn)
|
||||
#else
|
||||
#define WASM_RT_NO_RETURN __attribute__((noreturn))
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && WASM_RT_MEMCHECK_SIGNAL_HANDLER
|
||||
#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 1
|
||||
#else
|
||||
#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 0
|
||||
#endif
|
||||
|
||||
/** Reason a trap occurred. Provide this to `wasm_rt_trap`. */
|
||||
typedef enum {
|
||||
WASM_RT_TRAP_NONE, /** No error. */
|
||||
WASM_RT_TRAP_OOB, /** Out-of-bounds access in linear memory or a table. */
|
||||
WASM_RT_TRAP_INT_OVERFLOW, /** Integer overflow on divide or truncation. */
|
||||
WASM_RT_TRAP_DIV_BY_ZERO, /** Integer divide by zero. */
|
||||
WASM_RT_TRAP_INVALID_CONVERSION, /** Conversion from NaN to integer. */
|
||||
WASM_RT_TRAP_UNREACHABLE, /** Unreachable instruction executed. */
|
||||
WASM_RT_TRAP_CALL_INDIRECT, /** Invalid call_indirect, for any reason. */
|
||||
WASM_RT_TRAP_UNCAUGHT_EXCEPTION, /* Exception thrown and not caught */
|
||||
#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS
|
||||
WASM_RT_TRAP_EXHAUSTION = WASM_RT_TRAP_OOB,
|
||||
#else
|
||||
WASM_RT_TRAP_EXHAUSTION, /** Call stack exhausted. */
|
||||
#endif
|
||||
} wasm_rt_trap_t;
|
||||
|
||||
/** Value types. Used to define function signatures. */
|
||||
typedef enum {
|
||||
WASM_RT_I32,
|
||||
WASM_RT_I64,
|
||||
WASM_RT_F32,
|
||||
WASM_RT_F64,
|
||||
WASM_RT_FUNCREF,
|
||||
WASM_RT_EXTERNREF,
|
||||
} wasm_rt_type_t;
|
||||
|
||||
/**
|
||||
* A generic function pointer type, both for Wasm functions (`code`)
|
||||
* and host functions (`hostcode`). All function pointers are stored
|
||||
* in this canonical form, but must be cast to their proper signature
|
||||
* to call.
|
||||
*/
|
||||
typedef void (*wasm_rt_function_ptr_t)(void);
|
||||
|
||||
/** A function instance (the runtime representation of a function).
|
||||
* These can be stored in tables of type funcref, or used as values. */
|
||||
typedef struct {
|
||||
/** The index as returned from `wasm_rt_register_func_type`. */
|
||||
uint32_t func_type;
|
||||
/** The function. The embedder must know the actual C signature of the
|
||||
* function and cast to it before calling. */
|
||||
wasm_rt_function_ptr_t func;
|
||||
/** A function instance is a closure of the function over an instance
|
||||
* of the originating module. The module_instance element will be passed into
|
||||
* the function at runtime. */
|
||||
void* module_instance;
|
||||
} wasm_rt_funcref_t;
|
||||
|
||||
/** Default (null) value of a funcref */
|
||||
static const wasm_rt_funcref_t wasm_rt_funcref_null_value = {0, NULL, NULL};
|
||||
|
||||
/** The type of an external reference (opaque to WebAssembly). */
|
||||
typedef void* wasm_rt_externref_t;
|
||||
|
||||
/** Default (null) value of an externref */
|
||||
static const wasm_rt_externref_t wasm_rt_externref_null_value = NULL;
|
||||
|
||||
/** A Memory object. */
|
||||
typedef struct {
|
||||
/** The linear memory data, with a byte length of `size`. */
|
||||
uint8_t* data;
|
||||
/** The current and maximum page count for this Memory object. If there is no
|
||||
* maximum, `max_pages` is 0xffffffffu (i.e. UINT32_MAX). */
|
||||
uint32_t pages, max_pages;
|
||||
/** The current size of the linear memory, in bytes. */
|
||||
uint32_t size;
|
||||
} wasm_rt_memory_t;
|
||||
|
||||
/** A Table of type funcref. */
|
||||
typedef struct {
|
||||
/** The table element data, with an element count of `size`. */
|
||||
wasm_rt_funcref_t* data;
|
||||
/** The maximum element count of this Table object. If there is no maximum,
|
||||
* `max_size` is 0xffffffffu (i.e. UINT32_MAX). */
|
||||
uint32_t max_size;
|
||||
/** The current element count of the table. */
|
||||
uint32_t size;
|
||||
} wasm_rt_funcref_table_t;
|
||||
|
||||
/** A Table of type externref. */
|
||||
typedef struct {
|
||||
/** The table element data, with an element count of `size`. */
|
||||
wasm_rt_externref_t* data;
|
||||
/** The maximum element count of this Table object. If there is no maximum,
|
||||
* `max_size` is 0xffffffffu (i.e. UINT32_MAX). */
|
||||
uint32_t max_size;
|
||||
/** The current element count of the table. */
|
||||
uint32_t size;
|
||||
} wasm_rt_externref_table_t;
|
||||
|
||||
/** Initialize the runtime. */
|
||||
void wasm_rt_init(void);
|
||||
|
||||
/** Is the runtime initialized? */
|
||||
bool wasm_rt_is_initialized(void);
|
||||
|
||||
/** Free the runtime's state. */
|
||||
void wasm_rt_free(void);
|
||||
|
||||
/**
|
||||
* Stop execution immediately and jump back to the call to `wasm_rt_impl_try`.
|
||||
* The result of `wasm_rt_impl_try` will be the provided trap reason.
|
||||
*
|
||||
* This is typically called by the generated code, and not the embedder.
|
||||
*/
|
||||
WASM_RT_NO_RETURN void wasm_rt_trap(wasm_rt_trap_t);
|
||||
|
||||
/**
|
||||
* Return a human readable error string based on a trap type.
|
||||
*/
|
||||
const char* wasm_rt_strerror(wasm_rt_trap_t trap);
|
||||
|
||||
/**
|
||||
* Register a function type with the given signature. The returned function
|
||||
* index is guaranteed to be the same for all calls with the same signature.
|
||||
* The following varargs must all be of type `wasm_rt_type_t`, first the
|
||||
* params` and then the `results`.
|
||||
*
|
||||
* ```
|
||||
* // Register (func (param i32 f32) (result i64)).
|
||||
* wasm_rt_register_func_type(2, 1, WASM_RT_I32, WASM_RT_F32, WASM_RT_I64);
|
||||
* => returns 1
|
||||
*
|
||||
* // Register (func (result i64)).
|
||||
* wasm_rt_register_func_type(0, 1, WASM_RT_I32);
|
||||
* => returns 2
|
||||
*
|
||||
* // Register (func (param i32 f32) (result i64)) again.
|
||||
* wasm_rt_register_func_type(2, 1, WASM_RT_I32, WASM_RT_F32, WASM_RT_I64);
|
||||
* => returns 1
|
||||
* ```
|
||||
*/
|
||||
uint32_t wasm_rt_register_func_type(uint32_t params, uint32_t results, ...);
|
||||
|
||||
/**
|
||||
* Register a tag with the given size. Returns the tag.
|
||||
*/
|
||||
uint32_t wasm_rt_register_tag(uint32_t size);
|
||||
|
||||
/**
|
||||
* Set the active exception to given tag, size, and contents.
|
||||
*/
|
||||
void wasm_rt_load_exception(uint32_t tag, uint32_t size, const void* values);
|
||||
|
||||
/**
|
||||
* Throw the active exception.
|
||||
*/
|
||||
WASM_RT_NO_RETURN void wasm_rt_throw(void);
|
||||
|
||||
/**
|
||||
* The type of an unwind target if an exception is thrown and caught.
|
||||
*/
|
||||
#define WASM_RT_UNWIND_TARGET jmp_buf
|
||||
|
||||
/**
|
||||
* Get the current unwind target if an exception is thrown.
|
||||
*/
|
||||
WASM_RT_UNWIND_TARGET* wasm_rt_get_unwind_target(void);
|
||||
|
||||
/**
|
||||
* Set the unwind target if an exception is thrown.
|
||||
*/
|
||||
void wasm_rt_set_unwind_target(WASM_RT_UNWIND_TARGET* target);
|
||||
|
||||
/**
|
||||
* Tag of the active exception.
|
||||
*/
|
||||
uint32_t wasm_rt_exception_tag(void);
|
||||
|
||||
/**
|
||||
* Size of the active exception.
|
||||
*/
|
||||
uint32_t wasm_rt_exception_size(void);
|
||||
|
||||
/**
|
||||
* Contents of the active exception.
|
||||
*/
|
||||
void* wasm_rt_exception(void);
|
||||
|
||||
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !defined(_WIN32)
|
||||
#define WASM_RT_SETJMP(buf) sigsetjmp(buf, 1)
|
||||
#else
|
||||
#define WASM_RT_SETJMP(buf) setjmp(buf)
|
||||
#endif
|
||||
|
||||
#define wasm_rt_try(target) WASM_RT_SETJMP(target)
|
||||
|
||||
/**
|
||||
* Initialize a Memory object with an initial page size of `initial_pages` and
|
||||
* a maximum page size of `max_pages`.
|
||||
*
|
||||
* ```
|
||||
* wasm_rt_memory_t my_memory;
|
||||
* // 1 initial page (65536 bytes), and a maximum of 2 pages.
|
||||
* wasm_rt_allocate_memory(&my_memory, 1, 2);
|
||||
* ```
|
||||
*/
|
||||
void wasm_rt_allocate_memory(wasm_rt_memory_t*,
|
||||
uint32_t initial_pages,
|
||||
uint32_t max_pages);
|
||||
|
||||
/**
|
||||
* Grow a Memory object by `pages`, and return the previous page count. If
|
||||
* this new page count is greater than the maximum page count, the grow fails
|
||||
* and 0xffffffffu (UINT32_MAX) is returned instead.
|
||||
*
|
||||
* ```
|
||||
* wasm_rt_memory_t my_memory;
|
||||
* ...
|
||||
* // Grow memory by 10 pages.
|
||||
* uint32_t old_page_size = wasm_rt_grow_memory(&my_memory, 10);
|
||||
* if (old_page_size == UINT32_MAX) {
|
||||
* // Failed to grow memory.
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
uint32_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint32_t pages);
|
||||
|
||||
/**
|
||||
* Free a Memory object.
|
||||
*/
|
||||
void wasm_rt_free_memory(wasm_rt_memory_t*);
|
||||
|
||||
/**
|
||||
* Initialize a funcref Table object with an element count of `elements` and a
|
||||
* maximum size of `max_elements`.
|
||||
*
|
||||
* ```
|
||||
* wasm_rt_funcref_table_t my_table;
|
||||
* // 5 elements and a maximum of 10 elements.
|
||||
* wasm_rt_allocate_funcref_table(&my_table, 5, 10);
|
||||
* ```
|
||||
*/
|
||||
void wasm_rt_allocate_funcref_table(wasm_rt_funcref_table_t*,
|
||||
uint32_t elements,
|
||||
uint32_t max_elements);
|
||||
|
||||
/**
|
||||
* Free a funcref Table object.
|
||||
*/
|
||||
void wasm_rt_free_funcref_table(wasm_rt_funcref_table_t*);
|
||||
|
||||
/**
|
||||
* Initialize an externref Table object with an element count
|
||||
* of `elements` and a maximum size of `max_elements`.
|
||||
* Usage as per wasm_rt_allocate_funcref_table.
|
||||
*/
|
||||
void wasm_rt_allocate_externref_table(wasm_rt_externref_table_t*,
|
||||
uint32_t elements,
|
||||
uint32_t max_elements);
|
||||
|
||||
/**
|
||||
* Free an externref Table object.
|
||||
*/
|
||||
void wasm_rt_free_externref_table(wasm_rt_externref_table_t*);
|
||||
|
||||
/**
|
||||
* Grow a Table object by `delta` elements (giving the new elements the value
|
||||
* `init`), and return the previous element count. If this new element count is
|
||||
* greater than the maximum element count, the grow fails and 0xffffffffu
|
||||
* (UINT32_MAX) is returned instead.
|
||||
*/
|
||||
uint32_t wasm_rt_grow_funcref_table(wasm_rt_funcref_table_t*,
|
||||
uint32_t delta,
|
||||
wasm_rt_funcref_t init);
|
||||
uint32_t wasm_rt_grow_externref_table(wasm_rt_externref_table_t*,
|
||||
uint32_t delta,
|
||||
wasm_rt_externref_t init);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WASM_RT_H_ */
|
||||
Reference in New Issue
Block a user