Created
April 13, 2025 14:38
-
-
Save andrewmd5/197efb527ef40131c34ca12fd6d0a61e to your computer and use it in GitHub Desktop.
hako in go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"os" | |
"github.com/bytecodealliance/wasmtime-go/v31" | |
) | |
// Default memory configuration (matching JavaScript defaults) | |
const ( | |
defaultInitialMemory = 25165824 // 24MB | |
defaultMaximumMemory = 268435456 // 256MB | |
) | |
func main() { | |
// Load the WebAssembly module | |
wasmBytes, err := os.ReadFile("/Users/andrew/projects/hako/bridge/build/hako.wasm") | |
check(err) | |
// Set up the Wasmtime environment | |
engine := wasmtime.NewEngine() | |
store := wasmtime.NewStore(engine) | |
// Create a linker for imports | |
linker := wasmtime.NewLinker(engine) | |
// Set up WASI | |
wasiConfig := wasmtime.NewWasiConfig() | |
store.SetWasi(wasiConfig) | |
// Define WASI imports | |
err = linker.DefineWasi() | |
check(err) | |
// Create memory configuration (convert bytes to pages) | |
initialPages := defaultInitialMemory / 65536 | |
maximumPages := defaultMaximumMemory / 65536 | |
// Create memory type and memory instance | |
memoryType := wasmtime.NewMemoryType(uint32(initialPages), true, uint32(maximumPages), true) | |
memory, err := wasmtime.NewMemory(store, memoryType) | |
check(err) | |
// Define memory in the "env" namespace | |
err = linker.Define(store, "env", "memory", memory) | |
check(err) | |
// Define the Hako callback functions in the "hako" namespace | |
err = linker.Define( | |
store, | |
"hako", | |
"call_function", | |
wasmtime.WrapFunc( | |
store, | |
func(ctxPtr int32, thisPtr int32, argc int32, argv int32, funcId int32) int32 { | |
return 0 | |
}, | |
), | |
) | |
check(err) | |
err = linker.Define( | |
store, | |
"hako", | |
"interrupt_handler", | |
wasmtime.WrapFunc( | |
store, | |
func(rtPtr int32, ctxPtr int32, opaque int32) int32 { | |
return 0 | |
}, | |
), | |
) | |
check(err) | |
err = linker.Define( | |
store, | |
"hako", | |
"load_module_source", | |
wasmtime.WrapFunc( | |
store, | |
func(rtPtr int32, ctxPtr int32, moduleNamePtr int32) int32 { | |
return 0 | |
}, | |
), | |
) | |
check(err) | |
err = linker.Define( | |
store, | |
"hako", | |
"normalize_module", | |
wasmtime.WrapFunc( | |
store, | |
func(rtPtr int32, ctxPtr int32, baseNamePtr int32, moduleNamePtr int32) int32 { | |
return moduleNamePtr | |
}, | |
), | |
) | |
check(err) | |
err = linker.Define( | |
store, | |
"hako", | |
"profile_function_start", | |
wasmtime.WrapFunc( | |
store, | |
func(ctxPtr int32, eventPtr int32, opaque int32) { | |
}, | |
), | |
) | |
check(err) | |
err = linker.Define( | |
store, | |
"hako", | |
"profile_function_end", | |
wasmtime.WrapFunc( | |
store, | |
func(ctxPtr int32, eventPtr int32, opaque int32) { | |
}, | |
), | |
) | |
check(err) | |
// Create the module | |
module, err := wasmtime.NewModule(engine, wasmBytes) | |
check(err) | |
// Instantiate the module | |
instance, err := linker.Instantiate(store, module) | |
check(err) | |
// Call the initialization function if it exists | |
initFunc := instance.GetFunc(store, "_initialize") | |
if initFunc != nil { | |
_, err = initFunc.Call(store) | |
check(err) | |
fmt.Println("Module initialized successfully") | |
} else { | |
fmt.Println("Module loaded successfully") | |
} | |
// Get memory management functions | |
mallocFunc := instance.GetFunc(store, "malloc") | |
if mallocFunc == nil { | |
panic("malloc function not found") | |
} | |
freeFunc := instance.GetFunc(store, "free") | |
if freeFunc == nil { | |
panic("free function not found") | |
} | |
// Create a new runtime | |
newRuntimeFunc := instance.GetFunc(store, "HAKO_NewRuntime") | |
if newRuntimeFunc == nil { | |
panic("HAKO_NewRuntime function not found") | |
} | |
rtPtr, err := newRuntimeFunc.Call(store) | |
check(err) | |
runtimePtr := rtPtr.(int32) | |
// Enable all intrinsics for full JavaScript support | |
allIntrinsics := 0xFFFF | |
// Create a new context | |
newContextFunc := instance.GetFunc(store, "HAKO_NewContext") | |
if newContextFunc == nil { | |
panic("HAKO_NewContext function not found") | |
} | |
ctxPtr, err := newContextFunc.Call(store, runtimePtr, allIntrinsics) | |
check(err) | |
contextPtr := ctxPtr.(int32) | |
// Get necessary functions for evaluation | |
evalFunc := instance.GetFunc(store, "HAKO_Eval") | |
if evalFunc == nil { | |
panic("HAKO_Eval function not found") | |
} | |
getStringFunc := instance.GetFunc(store, "HAKO_ToCString") | |
if getStringFunc == nil { | |
panic("HAKO_ToCString function not found") | |
} | |
freeValueFunc := instance.GetFunc(store, "HAKO_FreeValuePointer") | |
if freeValueFunc == nil { | |
panic("HAKO_FreeValuePointer function not found") | |
} | |
freeCStringFunc := instance.GetFunc(store, "HAKO_FreeCString") | |
if freeCStringFunc == nil { | |
panic("HAKO_FreeCString function not found") | |
} | |
// JavaScript code to evaluate | |
jsCode := "const message = 'Hello from Hako!'; message;" | |
// Allocate memory for the code string using malloc | |
codeStrAlloc, err := mallocFunc.Call(store, len(jsCode)+1) // +1 for null terminator | |
check(err) | |
codeStrPtr := codeStrAlloc.(int32) | |
defer func() { | |
_, err := freeFunc.Call(store, codeStrPtr) | |
check(err) | |
}() | |
// Write the string to memory | |
data := memory.UnsafeData(store) | |
for i := 0; i < len(jsCode); i++ { | |
data[codeStrPtr+int32(i)] = jsCode[i] | |
} | |
data[codeStrPtr+int32(len(jsCode))] = 0 // Null terminator | |
// Allocate memory for the filename | |
filenameStr := "file://eval" | |
filenameAlloc, err := mallocFunc.Call(store, len(filenameStr)+1) | |
check(err) | |
filenamePtr := filenameAlloc.(int32) | |
defer func() { | |
_, err := freeFunc.Call(store, filenamePtr) | |
check(err) | |
}() | |
// Write the filename to memory | |
for i := 0; i < len(filenameStr); i++ { | |
data[filenamePtr+int32(i)] = filenameStr[i] | |
} | |
data[filenamePtr+int32(len(filenameStr))] = 0 // Null terminator | |
// Call HAKO_Eval to evaluate the JavaScript code | |
resultPtr, err := evalFunc.Call(store, contextPtr, codeStrPtr, len(jsCode), filenamePtr, 0, 0) | |
check(err) | |
valuePtr := resultPtr.(int32) | |
// Get the result as a string | |
strPtr, err := getStringFunc.Call(store, contextPtr, valuePtr) | |
check(err) | |
resultStrPtr := strPtr.(int32) | |
if resultStrPtr != 0 { | |
result := readString(store, memory, resultStrPtr) | |
fmt.Printf("Evaluation result: %s\n", result) | |
// Free the string | |
_, err = freeCStringFunc.Call(store, contextPtr, resultStrPtr) | |
check(err) | |
} | |
// Free the eval result | |
_, err = freeValueFunc.Call(store, contextPtr, valuePtr) | |
check(err) | |
// Clean up the context and runtime | |
freeContextFunc := instance.GetFunc(store, "HAKO_FreeContext") | |
if freeContextFunc != nil { | |
_, err = freeContextFunc.Call(store, contextPtr) | |
check(err) | |
} | |
freeRuntimeFunc := instance.GetFunc(store, "HAKO_FreeRuntime") | |
if freeRuntimeFunc != nil { | |
_, err = freeRuntimeFunc.Call(store, runtimePtr) | |
check(err) | |
} | |
} | |
// Helper function to read a string from WebAssembly memory | |
func readString(store *wasmtime.Store, memory *wasmtime.Memory, ptr int32) string { | |
if ptr == 0 { | |
return "" | |
} | |
// Get the memory data | |
data := memory.UnsafeData(store) | |
// Find the null terminator to determine string length | |
length := 0 | |
for ptr+int32(length) < int32(len(data)) && data[ptr+int32(length)] != 0 { | |
length++ | |
} | |
// Convert the bytes to a string | |
bytes := make([]byte, length) | |
for i := 0; i < length; i++ { | |
bytes[i] = data[ptr+int32(i)] | |
} | |
return string(bytes) | |
} | |
func check(e error) { | |
if e != nil { | |
panic(e) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment