standalone: Add wabt version

This commit is contained in:
Remko Tronçon 2022-11-15 18:50:52 +01:00
parent d999f24704
commit 0751ed383a
4 changed files with 316 additions and 0 deletions

5
src/standalone/wabt/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
/waforth
/waforth_core.h
/wasm-micro-runtime
/wasmtime-*
/*.tgz

View file

@ -0,0 +1,80 @@
.DEFAULT_GOAL := all
VERSION?=$(shell cat ../../../package.json | grep '"version"' | sed -e 's/.*:.*"\(.*\)".*/\1/')
ifeq ($(OS),Windows_NT)
CC=gcc
LIBS=
else
ifeq (, $(shell which gtar))
TAR := tar
else
# bsd-tar corrupts files on GitHub: https://github.com/actions/virtual-environments/issues/2619
TAR := gtar
endif
UNAME_S=$(shell uname -s)
UNAME_P=$(shell uname -p)
WABT_DIR = /usr/local/wabt
ifeq ($(UNAME_S),Darwin)
CXXFLAGS = -std=c++17
ifeq ($(UNAME_P),arm)
PACKAGE_SUFFIX=arm64-macos
else
PACKAGE_SUFFIX=x86_64-macos
endif
else
LIBS=-lpthread -lm -ldl
PACKAGE_SUFFIX=x86_64-linux
endif
package: waforth
$(TAR) czf waforth-v$(VERSION)-$(PACKAGE_SUFFIX).tgz waforth
endif
WABT_INCLUDE_DIR := $(WABT_DIR)/include
WABT_LIB_DIR := $(WABT_DIR)/lib
CXXFLAGS:= -I$(WABT_INCLUDE_DIR) $(CXXFLAGS)
LIBS:=$(WABT_LIB_DIR)/libwabt.a $(LIBS)
ifeq ($(DEBUG),1)
CXXFLAGS := $(CXXFLAGS) -g
else
CXXFLAGS := $(CXXFLAGS) -O2
endif
################################################
BIN2H=../../../scripts/bin2h
WAT2WASM=wat2wasm
WAT2WASM_FLAGS=--debug-names
CXXFLAGS:=-DVERSION='"$(VERSION)"' $(CXXFLAGS)
OBJECTS=main.o $(RESOURCE_OBJECTS)
all: waforth
waforth: $(OBJECTS)
$(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) $(LIBS)
main.o: waforth_core.h
waforth_core.wasm: ../../waforth.wat
$(WAT2WASM) $(WAT2WASM_FLAGS) -o $@ $<
waforth_core.h: waforth_core.wasm
$(BIN2H) $< $@
.PHONY: package
.PHONY: check
check:
-rm -f test.out
echo ": MAIN 1 2 3 + .S ; MAIN" | ./waforth | tee test.out
grep "1 5 ok" test.out
rm test.out
./waforth ../../examples/hello.fs | tee test.out
grep "Hello, Forth" test.out
rm test.out
.PHONY: clean
clean:
-rm -f waforth_core.wasm waforth_core.h $(OBJECTS) waforth *.exe *.tgz *.zip test.out

View file

@ -0,0 +1,2 @@
This is an experimental version of the standalone shell that uses the WABT interpreter.
When WABT implements the most recent version of the WASM C API, this can go away.

View file

@ -0,0 +1,229 @@
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#include <windows.h>
#else
#include <termios.h>
#endif
#include <cstdio>
#include <wabt/binary-reader.h>
#include <wabt/interp/binary-reader-interp.h>
#include <wabt/interp/interp-util.h>
#include <wabt/interp/interp.h>
#include <wabt/result.h>
#include "waforth_core.h"
namespace wabti = wabt::interp;
#ifndef VERSION
#define VERSION "dev"
#endif
#define CORE_RUN_EXPORT_INDEX 0
#define CORE_TABLE_EXPORT_INDEX 1
#define CORE_MEMORY_EXPORT_INDEX 2
#define CORE_ERROR_EXPORT_INDEX 9
#define ERR_UNKNOWN 0x1
#define ERR_QUIT 0x2
#define ERR_ABORT 0x3
#define ERR_EOI 0x4
#define ERR_BYE 0x5
static wabti::Store store;
static std::unique_ptr<wabt::FileStream> stderrStream;
static wabt::Features features;
static wabti::Memory::Ptr memory;
static wabti::Table::Ptr table;
static wabt::Errors errors;
FILE *input;
wabt::Result emit_cb(wabti::Thread &thread, const wabti::Values &params, wabti::Values &results, wabti::Trap::Ptr *trap) {
putchar(params[0].Get<wabti::s32>());
return wabt::Result::Ok;
}
wabt::Result key_cb(wabti::Thread &thread, const wabti::Values &params, wabti::Values &results, wabti::Trap::Ptr *trap) {
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
if (h == NULL) {
return trap_from_string("no console");
}
DWORD mode;
GetConsoleMode(h, &mode);
SetConsoleMode(h, mode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));
TCHAR ch = 0;
DWORD cc;
ReadConsole(h, &ch, 1, &cc, NULL);
SetConsoleMode(h, mode);
#else
struct termios old, current;
tcgetattr(0, &old);
current = old;
current.c_lflag &= ~ICANON;
current.c_lflag &= ~ECHO;
tcsetattr(0, TCSANOW, &current);
char ch = getchar();
tcsetattr(0, TCSANOW, &old);
#endif
results[0].Set((wabti::u32)ch);
return wabt::Result::Ok;
}
wabt::Result read_cb(wabti::Thread &thread, const wabti::Values &params, wabti::Values &results, wabti::Trap::Ptr *trap) {
auto addr = (char *)memory->UnsafeData() + params[0].Get<wabti::s32>();
auto size = params[1].Get<wabti::s32>();
*addr = 0;
fgets(addr, size, input);
int n = strlen(addr);
results[0].Set((wabti::u32)n);
return wabt::Result::Ok;
}
wabt::Result load_cb(wabti::Thread &thread, const wabti::Values &params, wabti::Values &results, wabti::Trap::Ptr *trap) {
auto addr = params[0].Get<wabti::s32>();
auto size = params[1].Get<wabti::s32>();
wabti::ModuleDesc desc;
CHECK_RESULT(wabti::ReadBinaryInterp("word.wasm", memory->UnsafeData() + addr, size, wabt::ReadBinaryOptions(features, nullptr, true, true, true),
&errors, &desc));
auto mod = wabti::Module::New(store, desc);
wabti::RefVec imports = {table.ref(), memory.ref()};
auto modi = wabti::Instance::Instantiate(store, mod.ref(), imports, trap);
if (!modi) {
printf("error instantiating word module\n");
return wabt::Result::Error;
;
}
return wabt::Result::Ok;
}
wabt::Result call_cb(wabti::Thread &thread, const wabti::Values &params, wabti::Values &results, wabti::Trap::Ptr *trap) {
printf("`call` is not available in standalone\n");
return wabt::Result::Error;
}
wabt::Result run(bool interactive) {
stderrStream = wabt::FileStream::CreateStderr();
// Load core module
wabti::ModuleDesc desc;
CHECK_RESULT(wabti::ReadBinaryInterp("waforth.wasm", waforth_core, sizeof(waforth_core),
wabt::ReadBinaryOptions(features, nullptr, true, true, true), &errors, &desc));
auto core = wabti::Module::New(store, desc);
// Core Exports
wabti::Func::Ptr errorFn;
wabti::Func::Ptr runFn;
// Bind core imports
wabti::RefVec imports;
for (auto &&import : core->desc().imports) {
if (import.type.type->kind == wabti::ExternKind::Func && import.type.module == "shell") {
auto ft = *wabt::cast<wabti::FuncType>(import.type.type.get());
wabti::HostFunc::Callback cb;
if (import.type.name == "emit") {
cb = emit_cb;
} else if (import.type.name == "read") {
cb = read_cb;
} else if (import.type.name == "key") {
cb = key_cb;
} else if (import.type.name == "load") {
cb = load_cb;
} else if (import.type.name == "call") {
cb = call_cb;
} else {
printf("Unknown import: %s\n", import.type.name.c_str());
return wabt::Result::Error;
}
auto func = wabti::HostFunc::New(store, ft, cb);
imports.push_back(func.ref());
continue;
}
imports.push_back(wabti::Ref::Null);
}
// Instantiate module
wabti::Trap::Ptr trap;
auto corei = wabti::Instance::Instantiate(store, core.ref(), imports, &trap);
if (!corei) {
printf("error instantiating module\n");
if (trap) {
wabti::WriteTrap(stderrStream.get(), " error ", trap);
}
return wabt::Result::Error;
}
// Load exports
for (auto &&export_ : core->desc().exports) {
if (export_.type.type->kind == wabt::ExternalKind::Memory) {
memory = store.UnsafeGet<wabti::Memory>(corei->memories()[export_.index]);
} else if (export_.type.type->kind == wabt::ExternalKind::Table) {
table = store.UnsafeGet<wabti::Table>(corei->tables()[export_.index]);
} else if (export_.type.name == "run") {
runFn = store.UnsafeGet<wabti::Func>(corei->funcs()[export_.index]);
} else if (export_.type.name == "error") {
errorFn = store.UnsafeGet<wabti::Func>(corei->funcs()[export_.index]);
}
}
// Run
wabti::Values runParams = {wabti::Value::Make(interactive ? 0 : 1)};
wabti::Values runResults;
wabti::Values errorParams;
wabti::Values errorResults;
for (int stopped = false; !stopped;) {
auto runRes = runFn->Call(store, runParams, runResults, &trap, nullptr);
CHECK_RESULT(errorFn->Call(store, errorParams, errorResults, &trap, nullptr));
switch (errorResults[0].Get<wabti::s32>()) {
case ERR_QUIT:
case ERR_ABORT:
assert(!Succeeded(runRes));
break;
case ERR_EOI:
assert(Succeeded(runRes));
stopped = true;
break;
case ERR_BYE:
assert(!Succeeded(runRes));
stopped = true;
break;
case ERR_UNKNOWN:
assert(!Succeeded(runRes));
if (trap) {
wabti::WriteTrap(stderrStream.get(), " error ", trap);
} else {
printf("unknown error\n");
}
break;
default:
printf("unknown error code\n");
if (trap) {
wabti::WriteTrap(stderrStream.get(), " error ", trap);
}
assert(false);
}
}
return wabt::Result::Ok;
}
int main(int argc, char *argv[]) {
if (argc >= 2) {
input = fopen(argv[1], "r");
} else {
input = stdin;
}
if (input == stdin) {
printf("WAForth (" VERSION ")\n");
}
auto result = run(input == stdin);
if (input != stdin) {
fclose(input);
}
return Succeeded(result) ? 0 : 1;
}