github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/vm/wasmtime/instance_runtime.go (about) 1 package wasmtime 2 3 import ( 4 "context" 5 "encoding/binary" 6 7 "github.com/bytecodealliance/wasmtime-go/v17" 8 "github.com/pkg/errors" 9 10 "github.com/machinefi/w3bstream/pkg/depends/kit/logr" 11 "github.com/machinefi/w3bstream/pkg/types" 12 ) 13 14 var ( 15 ErrNotLinked = errors.New("not linked") 16 ErrAlreadyInstantiated = errors.New("already instantiated") 17 ErrNotInstantiated = errors.New("not instantiated") 18 ErrFuncNotImported = errors.New("func not imported") 19 ErrAlreadyLinked = errors.New("already linked") 20 ) 21 22 type ( 23 Runtime struct { 24 module *wasmtime.Module 25 linker *wasmtime.Linker 26 store *wasmtime.Store 27 instance *wasmtime.Instance 28 engine *wasmtime.Engine 29 } 30 ) 31 32 func NewRuntime() *Runtime { 33 config := wasmtime.NewConfig() 34 config.SetConsumeFuel(true) 35 engine := wasmtime.NewEngineWithConfig(config) 36 return &Runtime{ 37 engine: engine, 38 } 39 } 40 41 func (rt *Runtime) Link(lk ABILinker, code []byte) error { 42 if rt.module != nil { 43 return ErrAlreadyLinked 44 } 45 linker := wasmtime.NewLinker(rt.engine) 46 if err := lk.LinkABI(func(module, name string, fn interface{}) error { 47 return linker.FuncWrap(module, name, fn) 48 }); err != nil { 49 return err 50 } 51 if err := linker.DefineWasi(); err != nil { 52 return err 53 } 54 rt.linker = linker 55 module, err := wasmtime.NewModule(rt.engine, code) 56 if err != nil { 57 return err 58 } 59 rt.module = module 60 return nil 61 } 62 63 func (rt *Runtime) Instantiate(ctx context.Context) error { 64 ctx, l := logr.Start(ctx, "modules.vm.wasmtime.Runtime.Instantiate") 65 defer l.End() 66 67 if rt.module == nil { 68 return ErrNotLinked 69 } 70 if rt.instance != nil { 71 return ErrAlreadyInstantiated 72 } 73 store := wasmtime.NewStore(rt.engine) 74 store.SetWasi(wasmtime.NewWasiConfig()) 75 if fuel, _ := types.MaxWasmConsumeFuelFromContext(ctx); fuel > 0 { 76 if err := store.SetFuel(fuel); err != nil { 77 return err 78 } 79 } 80 81 instance, err := rt.linker.Instantiate(store, rt.module) 82 if err != nil { 83 return err 84 } 85 rt.instance = instance 86 rt.store = store 87 88 return nil 89 } 90 91 func (rt *Runtime) Deinstantiate(ctx context.Context) { 92 ctx, l := logr.Start(ctx, "modules.vm.wasmtime.Runtime.Deinstantiate") 93 defer l.End() 94 95 rt.instance = nil 96 rt.store = nil 97 } 98 99 func (rt *Runtime) newMemory() []byte { 100 return rt.instance.GetExport(rt.store, "memory").Memory().UnsafeData(rt.store) 101 } 102 103 func (rt *Runtime) alloc(size int32) (int32, []byte, error) { 104 fn := rt.instance.GetExport(rt.store, "alloc") 105 if fn == nil { 106 return 0, nil, errors.New("alloc is nil") 107 } 108 result, err := fn.Func().Call(rt.store, size) 109 if err != nil { 110 return 0, nil, err 111 } 112 return result.(int32), rt.newMemory(), nil 113 } 114 115 func putUint32Le(buf []byte, vmAddr int32, val uint32) error { 116 if int32(len(buf)) < vmAddr+4 { 117 return errors.New("overflow") 118 } 119 binary.LittleEndian.PutUint32(buf[vmAddr:], val) 120 return nil 121 } 122 123 func (rt *Runtime) Call(ctx context.Context, name string, args ...interface{}) (interface{}, error) { 124 ctx, l := logr.Start(ctx, "modules.vm.wasmtime.Runtime.Call", "func", name) 125 defer l.End() 126 127 if rt.module == nil { 128 return nil, ErrNotLinked 129 } 130 if rt.instance == nil { 131 return nil, ErrNotInstantiated 132 } 133 fn := rt.instance.GetFunc(rt.store, name) 134 if fn == nil { 135 return nil, ErrFuncNotImported 136 } 137 return fn.Call(rt.store, args...) 138 } 139 140 func (rt *Runtime) Read(addr, size int32) ([]byte, error) { 141 if rt.module == nil { 142 return nil, ErrNotLinked 143 } 144 if rt.instance == nil { 145 return nil, ErrNotInstantiated 146 } 147 mem := rt.newMemory() 148 if addr > int32(len(mem)) || addr+size > int32(len(mem)) { 149 return nil, errors.New("overflow") 150 } 151 buf := make([]byte, size) 152 if copied := copy(buf, mem[addr:addr+size]); int32(copied) != size { 153 return nil, errors.New("overflow") 154 } 155 return buf, nil 156 } 157 158 func (rt *Runtime) Copy(hostData []byte, vmAddrPtr, vmSizePtr int32) error { 159 if rt.module == nil { 160 return ErrNotLinked 161 } 162 if rt.instance == nil { 163 return ErrNotInstantiated 164 } 165 size := len(hostData) 166 addr, mem, err := rt.alloc(int32(size)) 167 if err != nil { 168 return err 169 } 170 if copied := copy(mem[addr:], hostData); copied != size { 171 return errors.New("fail to copy data") 172 } 173 if err = putUint32Le(mem, vmAddrPtr, uint32(addr)); err != nil { 174 return err 175 } 176 177 return putUint32Le(mem, vmSizePtr, uint32(size)) 178 }