Skip to content

Instantly share code, notes, and snippets.

@andrewmd5
Created April 13, 2025 14:38
Show Gist options
  • Save andrewmd5/197efb527ef40131c34ca12fd6d0a61e to your computer and use it in GitHub Desktop.
Save andrewmd5/197efb527ef40131c34ca12fd6d0a61e to your computer and use it in GitHub Desktop.
hako in go
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