github.com/taubyte/vm-wasm-utils@v1.0.2/call_context.go (about) 1 package wasm 2 3 import ( 4 "context" 5 "fmt" 6 "sync/atomic" 7 8 internalsys "github.com/taubyte/vm-wasm-utils/sys" 9 "github.com/tetratelabs/wazero/api" 10 "github.com/tetratelabs/wazero/sys" 11 ) 12 13 // compile time check to ensure CallContext implements api.Module 14 var _ api.Module = &CallContext{} 15 16 func NewCallContext(ns *Namespace, instance *ModuleInstance, sys *internalsys.Context) *CallContext { 17 zero := uint64(0) 18 return &CallContext{memory: instance.Memory, module: instance, ns: ns, Sys: sys, closed: &zero} 19 } 20 21 // CallContext is a function call context bound to a module. This is important as one module's functions can call 22 // imported functions, but all need to effect the same memory. 23 // 24 // Note: This does not include the context.Context because doing so risks caching the wrong context which can break 25 // functionality like trace propagation. 26 // Note: this also implements api.Module in order to simplify usage as a host function parameter. 27 type CallContext struct { 28 // TODO: We've never found a great name for this. It is only used for function calls, hence CallContext, but it 29 // moves on a different axis than, for example, the context.Context. context.Context is the same root for the whole 30 // call stack, where the CallContext can change depending on where memory is defined and who defines the calling 31 // function. When we rename this again, we should try to capture as many key points possible on the docs. 32 33 module *ModuleInstance 34 // memory is returned by Memory and overridden WithMemory 35 memory api.Memory 36 ns *Namespace 37 38 // Sys is exposed for use in special imports such as WASI, assemblyscript 39 // and wasm_exec. 40 // 41 // # Notes 42 // 43 // - This is a part of CallContext so that scope and Close is coherent. 44 // - This is not exposed outside this repository (as a host function 45 // parameter) because we haven't thought through capabilities based 46 // security implications. 47 Sys *internalsys.Context 48 49 // closed is the pointer used both to guard moduleEngine.CloseWithExitCode and to store the exit code. 50 // 51 // The update value is 1 + exitCode << 32. This ensures an exit code of zero isn't mistaken for never closed. 52 // 53 // Note: Exclusively reading and updating this with atomics guarantees cross-goroutine observations. 54 // See /RATIONALE.md 55 closed *uint64 56 57 // CodeCloser is non-nil when the code should be closed after this module. 58 CodeCloser api.Closer 59 } 60 61 // FailIfClosed returns a sys.ExitError if CloseWithExitCode was called. 62 func (m *CallContext) FailIfClosed() error { 63 if closed := atomic.LoadUint64(m.closed); closed != 0 { 64 return sys.NewExitError(uint32(closed >> 32)) // Unpack the high order bits as the exit code. 65 } 66 return nil 67 } 68 69 // Name implements the same method as documented on api.Module 70 func (m *CallContext) Name() string { 71 return m.module.Name 72 } 73 74 // WithMemory allows overriding memory without re-allocation when the result would be the same. 75 func (m *CallContext) WithMemory(memory *MemoryInstance) *CallContext { 76 if memory != nil && memory != m.memory { // only re-allocate if it will change the effective memory 77 return &CallContext{module: m.module, memory: memory, Sys: m.Sys, closed: m.closed} 78 } 79 return m 80 } 81 82 // String implements the same method as documented on api.Module 83 func (m *CallContext) String() string { 84 return fmt.Sprintf("Module[%s]", m.Name()) 85 } 86 87 // Close implements the same method as documented on api.Module. 88 func (m *CallContext) Close(ctx context.Context) (err error) { 89 return m.CloseWithExitCode(ctx, 0) 90 } 91 92 // CloseWithExitCode implements the same method as documented on api.Module. 93 func (m *CallContext) CloseWithExitCode(ctx context.Context, exitCode uint32) error { 94 closed, err := m.close(ctx, exitCode) 95 if !closed { 96 return nil 97 } 98 m.ns.deleteModule(m.Name()) 99 if m.CodeCloser == nil { 100 return err 101 } 102 if e := m.CodeCloser.Close(ctx); e != nil && err == nil { 103 err = e 104 } 105 return err 106 } 107 108 // close marks this CallContext as closed and releases underlying system resources. 109 // 110 // Note: The caller is responsible for removing the module from the Namespace. 111 func (m *CallContext) close(ctx context.Context, exitCode uint32) (c bool, err error) { 112 // Note: If you use the context.Context param, don't forget to coerce nil to context.Background()! 113 114 closed := uint64(1) + uint64(exitCode)<<32 // Store exitCode as high-order bits. 115 if !atomic.CompareAndSwapUint64(m.closed, 0, closed) { 116 return false, nil 117 } 118 c = true 119 if sysCtx := m.Sys; sysCtx != nil { // nil if from ModuleBuilder 120 err = sysCtx.FS(ctx).Close(ctx) 121 } 122 return 123 } 124 125 // Memory implements the same method as documented on api.Module. 126 func (m *CallContext) Memory() api.Memory { 127 return m.module.Memory 128 } 129 130 // ExportedMemory implements the same method as documented on api.Module. 131 func (m *CallContext) ExportedMemory(name string) api.Memory { 132 exp, err := m.module.getExport(name, ExternTypeMemory) 133 if err != nil { 134 return nil 135 } 136 return exp.Memory 137 } 138 139 // ExportedFunction implements the same method as documented on api.Module. 140 func (m *CallContext) ExportedFunction(name string) api.Function { 141 exp, err := m.module.getExport(name, ExternTypeFunc) 142 if err != nil { 143 return nil 144 } 145 if exp.Function.Module == m.module { 146 return exp.Function 147 } else { 148 return &importedFn{importingModule: m, importedFn: exp.Function} 149 } 150 } 151 152 func (m *CallContext) ExportedMemoryDefinitions() map[string]api.MemoryDefinition { 153 // TODO: Not necessary at the moment, but need to implement 154 return nil 155 } 156 157 func (m *CallContext) ExportedFunctionDefinitions() map[string]api.FunctionDefinition { 158 definitions := make(map[string]api.FunctionDefinition, 0) 159 for _, v := range m.module.Functions { 160 definitions[v.definition.Name()] = v.definition 161 } 162 return definitions 163 } 164 165 // importedFn implements api.Function and ensures the call context of an imported function is the importing module. 166 type importedFn struct { 167 importingModule *CallContext 168 importedFn *FunctionInstance 169 } 170 171 // Definition implements the same method as documented on api.Function. 172 func (f *importedFn) Definition() api.FunctionDefinition { 173 return f.importedFn.definition 174 } 175 176 // Call implements the same method as documented on api.Function. 177 func (f *importedFn) Call(ctx context.Context, params ...uint64) (ret []uint64, err error) { 178 if f.importedFn.IsHostFunction { 179 return nil, fmt.Errorf("directly calling host function is not supported") 180 } 181 if ctx == nil { 182 ctx = context.Background() 183 } 184 mod := f.importingModule 185 return f.importedFn.Module.Engine.Call(ctx, mod, f.importedFn, params...) 186 } 187 188 // Call implements the same method as documented on api.Function. 189 func (f *FunctionInstance) Call(ctx context.Context, params ...uint64) (ret []uint64, err error) { 190 if f.IsHostFunction { 191 return nil, fmt.Errorf("directly calling host function is not supported") 192 } 193 if ctx == nil { 194 ctx = context.Background() 195 } 196 mod := f.Module 197 ret, err = mod.Engine.Call(ctx, mod.CallCtx, f, params...) 198 return 199 } 200 201 // ExportedGlobal implements the same method as documented on api.Module. 202 func (m *CallContext) ExportedGlobal(name string) api.Global { 203 exp, err := m.module.getExport(name, ExternTypeGlobal) 204 if err != nil { 205 return nil 206 } 207 if exp.Global.Type.Mutable { 208 return &mutableGlobal{exp.Global} 209 } 210 valType := exp.Global.Type.ValType 211 switch valType { 212 case ValueTypeI32: 213 return globalI32(exp.Global.Val) 214 case ValueTypeI64: 215 return globalI64(exp.Global.Val) 216 case ValueTypeF32: 217 return globalF32(exp.Global.Val) 218 case ValueTypeF64: 219 return globalF64(exp.Global.Val) 220 default: 221 panic(fmt.Errorf("BUG: unknown value type %X", valType)) 222 } 223 }