github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/iextengine/wazero/impl.go (about) 1 /* 2 - Copyright (c) 2023-present unTill Software Development Group B.V. 3 @author Michael Saigachenko 4 */ 5 6 package iextenginewazero 7 8 import ( 9 "context" 10 "errors" 11 "fmt" 12 "math" 13 "os" 14 "runtime" 15 "strings" 16 17 "github.com/tetratelabs/wazero" 18 "github.com/tetratelabs/wazero/api" 19 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 20 21 "github.com/voedger/voedger/pkg/appdef" 22 "github.com/voedger/voedger/pkg/iextengine" 23 "github.com/voedger/voedger/pkg/istructs" 24 safe "github.com/voedger/voedger/pkg/state/isafestateapi" 25 "github.com/voedger/voedger/pkg/state/safestate" 26 ) 27 28 type limitedWriter struct { 29 limit int 30 buf []byte 31 } 32 33 type wazeroExtPkg struct { 34 moduleCfg wazero.ModuleConfig 35 compiled wazero.CompiledModule 36 module api.Module 37 exts map[string]api.Function 38 wasmData []byte 39 40 funcMalloc api.Function 41 funcFree api.Function 42 43 funcVer api.Function 44 funcGetHeapInuse api.Function 45 funcGetHeapSys api.Function 46 funcGetMallocs api.Function 47 funcGetFrees api.Function 48 funcGc api.Function 49 funcOnReadValue api.Function 50 51 allocatedBufs []*allocatedBuf 52 stdout limitedWriter 53 // memBackup *api.MemoryBackup 54 // recoverMem []byte 55 } 56 57 type wazeroExtEngine struct { 58 app istructs.AppQName 59 60 compile bool 61 config *iextengine.ExtEngineConfig 62 modules map[string]*wazeroExtPkg 63 host api.Module 64 rtm wazero.Runtime 65 66 wasiCloser api.Closer 67 68 // Invoke-related! 69 safeApi safe.IStateSafeAPI 70 71 ctx context.Context 72 pkg *wazeroExtPkg 73 autoRecover bool 74 numAutoRecovers int 75 } 76 77 type allocatedBuf struct { 78 addr uint32 79 offs uint32 80 cap uint32 81 } 82 83 type extensionEngineFactory struct { 84 compile bool 85 } 86 87 func newLimitedWriter(limit int) limitedWriter { 88 return limitedWriter{limit: limit} 89 } 90 91 func (w *limitedWriter) Write(p []byte) (n int, err error) { 92 if len(w.buf)+len(p) > w.limit { 93 w.buf = append(w.buf, p[:w.limit-len(w.buf)]...) 94 } else { 95 w.buf = append(w.buf, p...) 96 } 97 return len(p), nil 98 } 99 100 func (f extensionEngineFactory) New(ctx context.Context, app istructs.AppQName, packages []iextengine.ExtensionPackage, config *iextengine.ExtEngineConfig, numEngines int) (engines []iextengine.IExtensionEngine, err error) { 101 for i := 0; i < numEngines; i++ { 102 engine := &wazeroExtEngine{ 103 app: app, 104 modules: make(map[string]*wazeroExtPkg), 105 config: config, 106 compile: f.compile, 107 autoRecover: true, 108 } 109 err = engine.init(ctx) 110 if err != nil { 111 return engines, err 112 } 113 engines = append(engines, engine) 114 } 115 116 for _, pkg := range packages { 117 if pkg.ModuleUrl.Scheme == "file" && (pkg.ModuleUrl.Host == "" || strings.EqualFold("localhost", pkg.ModuleUrl.Scheme)) { 118 path := pkg.ModuleUrl.Path 119 if runtime.GOOS == "windows" { 120 path = strings.TrimPrefix(path, "/") 121 } 122 123 wasmdata, err := os.ReadFile(path) 124 125 if err != nil { 126 return nil, err 127 } 128 129 for _, eng := range engines { 130 err = eng.(*wazeroExtEngine).initModule(ctx, pkg.QualifiedName, wasmdata, pkg.ExtensionNames) 131 if err != nil { 132 return nil, err 133 } 134 } 135 } else { 136 return nil, fmt.Errorf("unsupported URL: " + pkg.ModuleUrl.String()) 137 } 138 } 139 return engines, nil 140 } 141 142 func (f *wazeroExtEngine) SetLimits(limits iextengine.ExtensionLimits) { 143 // f.cep.Duration = limits.ExecutionInterval 144 } 145 146 func (f *wazeroExtPkg) importFuncs(funcs map[string]*api.Function) error { 147 148 for k, v := range funcs { 149 *v = f.module.ExportedFunction(k) 150 if *v == nil { 151 return fmt.Errorf("missing exported function: %s", k) 152 } 153 } 154 return nil 155 } 156 157 func (f *wazeroExtEngine) init(ctx context.Context) error { 158 var err error 159 var memPages = f.config.MemoryLimitPages 160 if memPages == 0 { 161 memPages = iextengine.DefaultMemoryLimitPages 162 } 163 if memPages > maxMemoryPages { 164 return errors.New("maximum allowed MemoryLimitPages is 0xffff") 165 } 166 // Total amount of memory must be at least 170% of WasmPreallocatedBufferSize 167 const memoryLimitCoef = 1.7 168 memoryLimit := memPages * iextengine.MemoryPageSize 169 limit := math.Trunc(float64(WasmPreallocatedBufferSize) * float64(memoryLimitCoef)) 170 if uint32(memoryLimit) <= uint32(limit) { 171 return fmt.Errorf("the minimum limit of memory is: %.1f bytes, requested limit is: %.1f", limit, float32(memoryLimit)) 172 } 173 174 var rtConf wazero.RuntimeConfig 175 176 if f.compile { 177 rtConf = wazero.NewRuntimeConfigCompiler() 178 } else { 179 rtConf = wazero.NewRuntimeConfigInterpreter() 180 } 181 rtConf = rtConf. 182 WithCoreFeatures(api.CoreFeatureBulkMemoryOperations). 183 WithCloseOnContextDone(true). 184 WithMemoryCapacityFromMax(true). 185 WithMemoryLimitPages(uint32(memPages)) 186 187 f.rtm = wazero.NewRuntimeWithConfig(ctx, rtConf) 188 f.wasiCloser, err = wasi_snapshot_preview1.Instantiate(ctx, f.rtm) 189 190 if err != nil { 191 return err 192 } 193 194 f.host, err = f.rtm.NewHostModuleBuilder("env"). 195 NewFunctionBuilder().WithFunc(f.hostGetKey).Export("hostGetKey"). 196 NewFunctionBuilder().WithFunc(f.hostMustExist).Export("hostGetValue"). 197 NewFunctionBuilder().WithFunc(f.hostCanExist).Export("hostQueryValue"). 198 NewFunctionBuilder().WithFunc(f.hostReadValues).Export("hostReadValues"). 199 // IKey 200 NewFunctionBuilder().WithFunc(f.hostKeyAsString).Export("hostKeyAsString"). 201 NewFunctionBuilder().WithFunc(f.hostKeyAsBytes).Export("hostKeyAsBytes"). 202 NewFunctionBuilder().WithFunc(f.hostKeyAsInt32).Export("hostKeyAsInt32"). 203 NewFunctionBuilder().WithFunc(f.hostKeyAsInt64).Export("hostKeyAsInt64"). 204 NewFunctionBuilder().WithFunc(f.hostKeyAsFloat32).Export("hostKeyAsFloat32"). 205 NewFunctionBuilder().WithFunc(f.hostKeyAsFloat64).Export("hostKeyAsFloat64"). 206 NewFunctionBuilder().WithFunc(f.hostKeyAsBool).Export("hostKeyAsBool"). 207 NewFunctionBuilder().WithFunc(f.hostKeyAsQNamePkg).Export("hostKeyAsQNamePkg"). 208 NewFunctionBuilder().WithFunc(f.hostKeyAsQNameEntity).Export("hostKeyAsQNameEntity"). 209 // IValue 210 NewFunctionBuilder().WithFunc(f.hostValueLength).Export("hostValueLength"). 211 NewFunctionBuilder().WithFunc(f.hostValueAsValue).Export("hostValueAsValue"). 212 NewFunctionBuilder().WithFunc(f.hostValueAsString).Export("hostValueAsString"). 213 NewFunctionBuilder().WithFunc(f.hostValueAsBytes).Export("hostValueAsBytes"). 214 NewFunctionBuilder().WithFunc(f.hostValueAsInt32).Export("hostValueAsInt32"). 215 NewFunctionBuilder().WithFunc(f.hostValueAsInt64).Export("hostValueAsInt64"). 216 NewFunctionBuilder().WithFunc(f.hostValueAsFloat32).Export("hostValueAsFloat32"). 217 NewFunctionBuilder().WithFunc(f.hostValueAsFloat64).Export("hostValueAsFloat64"). 218 NewFunctionBuilder().WithFunc(f.hostValueAsQNamePkg).Export("hostValueAsQNamePkg"). 219 NewFunctionBuilder().WithFunc(f.hostValueAsQNameEntity).Export("hostValueAsQNameEntity"). 220 NewFunctionBuilder().WithFunc(f.hostValueAsBool).Export("hostValueAsBool"). 221 NewFunctionBuilder().WithFunc(f.hostValueGetAsBytes).Export("hostValueGetAsBytes"). 222 NewFunctionBuilder().WithFunc(f.hostValueGetAsString).Export("hostValueGetAsString"). 223 NewFunctionBuilder().WithFunc(f.hostValueGetAsInt32).Export("hostValueGetAsInt32"). 224 NewFunctionBuilder().WithFunc(f.hostValueGetAsInt64).Export("hostValueGetAsInt64"). 225 NewFunctionBuilder().WithFunc(f.hostValueGetAsFloat32).Export("hostValueGetAsFloat32"). 226 NewFunctionBuilder().WithFunc(f.hostValueGetAsFloat64).Export("hostValueGetAsFloat64"). 227 NewFunctionBuilder().WithFunc(f.hostValueGetAsValue).Export("hostValueGetAsValue"). 228 NewFunctionBuilder().WithFunc(f.hostValueGetAsQNamePkg).Export("hostValueGetAsQNamePkg"). 229 NewFunctionBuilder().WithFunc(f.hostValueGetAsQNameEntity).Export("hostValueGetAsQNameEntity"). 230 NewFunctionBuilder().WithFunc(f.hostValueGetAsBool).Export("hostValueGetAsBool"). 231 // Intents 232 NewFunctionBuilder().WithFunc(f.hostNewValue).Export("hostNewValue"). 233 NewFunctionBuilder().WithFunc(f.hostUpdateValue).Export("hostUpdateValue"). 234 // RowWriters 235 NewFunctionBuilder().WithFunc(f.hostRowWriterPutString).Export("hostRowWriterPutString"). 236 NewFunctionBuilder().WithFunc(f.hostRowWriterPutBytes).Export("hostRowWriterPutBytes"). 237 NewFunctionBuilder().WithFunc(f.hostRowWriterPutInt32).Export("hostRowWriterPutInt32"). 238 NewFunctionBuilder().WithFunc(f.hostRowWriterPutInt64).Export("hostRowWriterPutInt64"). 239 NewFunctionBuilder().WithFunc(f.hostRowWriterPutFloat32).Export("hostRowWriterPutFloat32"). 240 NewFunctionBuilder().WithFunc(f.hostRowWriterPutFloat64).Export("hostRowWriterPutFloat64"). 241 NewFunctionBuilder().WithFunc(f.hostRowWriterPutBool).Export("hostRowWriterPutBool"). 242 NewFunctionBuilder().WithFunc(f.hostRowWriterPutQName).Export("hostRowWriterPutQName"). 243 //ExportFunction("printstr", f.printStr). 244 245 Instantiate(ctx) 246 if err != nil { 247 return err 248 } 249 250 return nil 251 252 } 253 254 func (f *wazeroExtEngine) resetModule(ctx context.Context, ePkg *wazeroExtPkg) (err error) { 255 if ePkg.module != nil { 256 err = ePkg.module.Close(ctx) 257 if err != nil { 258 return err 259 } 260 ePkg.stdout.buf = ePkg.stdout.buf[:0] 261 } 262 if f.compile { 263 ePkg.module, err = f.rtm.InstantiateModule(ctx, ePkg.compiled, ePkg.moduleCfg) 264 } else { 265 ePkg.module, err = f.rtm.InstantiateWithConfig(ctx, ePkg.wasmData, ePkg.moduleCfg) 266 } 267 268 if err != nil { 269 return err 270 } 271 272 err = ePkg.importFuncs(map[string]*api.Function{ 273 "malloc": &ePkg.funcMalloc, 274 "free": &ePkg.funcFree, 275 "WasmAbiVersion_0_0_1": &ePkg.funcVer, 276 "WasmGetHeapInuse": &ePkg.funcGetHeapInuse, 277 "WasmGetHeapSys": &ePkg.funcGetHeapSys, 278 "WasmGetMallocs": &ePkg.funcGetMallocs, 279 "WasmGetFrees": &ePkg.funcGetFrees, 280 "WasmGC": &ePkg.funcGc, 281 "WasmOnReadValue": &ePkg.funcOnReadValue, 282 }) 283 if err != nil { 284 return err 285 } 286 287 res, err := ePkg.funcMalloc.Call(ctx, uint64(WasmPreallocatedBufferSize)) 288 if err != nil { 289 return err 290 } 291 ePkg.allocatedBufs = append(ePkg.allocatedBufs, &allocatedBuf{ 292 addr: uint32(res[0]), 293 offs: 0, 294 cap: WasmPreallocatedBufferSize, 295 }) 296 297 for name := range ePkg.exts { 298 expFunc := ePkg.module.ExportedFunction(name) 299 if expFunc != nil { 300 ePkg.exts[name] = expFunc 301 } else { 302 return missingExportedFunction(name) 303 } 304 } 305 306 return nil 307 } 308 309 func (f *wazeroExtEngine) initModule(ctx context.Context, pkgName string, wasmdata []byte, extNames []string) (err error) { 310 ePkg := &wazeroExtPkg{} 311 312 ePkg.stdout = newLimitedWriter(maxStdErrSize) 313 ePkg.moduleCfg = wazero.NewModuleConfig().WithName("wasm").WithStdout(&ePkg.stdout) 314 315 if f.compile { 316 ePkg.compiled, err = f.rtm.CompileModule(ctx, wasmdata) 317 if err != nil { 318 return err 319 } 320 } else { 321 ePkg.wasmData = wasmdata 322 } 323 324 ePkg.exts = make(map[string]api.Function) 325 326 for _, name := range extNames { 327 if !strings.HasPrefix(name, "Wasm") && name != "alloc" && name != "free" && 328 name != "calloc" && name != "realloc" && name != "malloc" && name != "_start" && name != "memory" { 329 ePkg.exts[name] = nil // put to map to init later 330 } else { 331 return incorrectExtensionName(name) 332 } 333 } 334 335 if err = f.resetModule(ctx, ePkg); err != nil { 336 return err 337 } 338 339 // Check WASM SDK version 340 _, err = ePkg.funcVer.Call(ctx) 341 if err != nil { 342 return errors.New("unsupported WASM version") 343 } 344 345 f.modules[pkgName] = ePkg 346 347 return nil 348 } 349 350 func (f *wazeroExtEngine) Close(ctx context.Context) { 351 for _, m := range f.modules { 352 if m.module != nil { 353 m.module.Close(ctx) 354 } 355 } 356 if f.host != nil { 357 f.host.Close(ctx) 358 } 359 if f.wasiCloser != nil { 360 f.wasiCloser.Close(ctx) 361 } 362 } 363 364 func (f *wazeroExtEngine) recover(ctx context.Context) { 365 if err := f.resetModule(ctx, f.pkg); err != nil { 366 panic(err) 367 } 368 } 369 370 func (f *wazeroExtEngine) selectModule(pkgPath string) error { 371 pkg, ok := f.modules[pkgPath] 372 if !ok { 373 return errUndefinedPackage(pkgPath) 374 } 375 f.pkg = pkg 376 return nil 377 } 378 379 func (f *wazeroExtEngine) isMemoryOverflow(err error) bool { 380 return strings.Contains(err.Error(), "runtime.alloc") 381 } 382 383 func (f *wazeroExtEngine) isPanic(err error) bool { 384 return strings.Contains(err.Error(), "wasm error: unreachable") 385 } 386 387 func (f *wazeroExtEngine) invoke(ctx context.Context, extension appdef.FullQName, io iextengine.IExtensionIO) (err error) { 388 var ok bool 389 f.pkg, ok = f.modules[extension.PkgPath()] 390 if !ok { 391 return errUndefinedPackage(extension.PkgPath()) 392 } 393 394 funct := f.pkg.exts[extension.Entity()] 395 if funct == nil { 396 return invalidExtensionName(extension.Entity()) 397 } 398 399 f.safeApi = safestate.Provide(io, f.safeApi) 400 f.ctx = ctx 401 402 for i := range f.pkg.allocatedBufs { 403 f.pkg.allocatedBufs[i].offs = 0 // reuse pre-allocated memory 404 } 405 406 // fmt.Print(funct) 407 _, err = funct.Call(ctx) 408 409 return err 410 } 411 412 func (f *wazeroExtEngine) Invoke(ctx context.Context, extension appdef.FullQName, io iextengine.IExtensionIO) (err error) { 413 err = f.invoke(ctx, extension, io) 414 if err != nil && f.isMemoryOverflow(err) && f.autoRecover { 415 f.numAutoRecovers++ 416 f.recover(ctx) 417 err = f.invoke(ctx, extension, io) 418 } 419 if err != nil && f.isPanic(err) && len(f.pkg.stdout.buf) > 0 { 420 stdout := string(f.pkg.stdout.buf) 421 if strings.HasPrefix(stdout, "panic: ") { 422 return errors.New(strings.TrimSpace(stdout)) 423 } 424 } 425 return err 426 } 427 428 func (f *wazeroExtEngine) decodeStr(ptr, size uint32) string { 429 if bytes, ok := f.pkg.module.Memory().Read(ptr, size); ok { 430 return string(bytes) 431 } 432 panic(ErrUnableToReadMemory) 433 } 434 435 func (f *wazeroExtEngine) hostGetKey(storagePtr, storageSize, entityPtr, entitySize uint32) (res uint64) { 436 storageFull := f.decodeStr(storagePtr, storageSize) 437 entitystr := f.decodeStr(entityPtr, entitySize) 438 return uint64(f.safeApi.KeyBuilder(storageFull, entitystr)) 439 } 440 441 func (f *wazeroExtEngine) hostReadValues(keyId uint64) { 442 f.safeApi.ReadValues(safe.TKeyBuilder(keyId), func(key safe.TKey, value safe.TValue) { 443 _, err := f.pkg.funcOnReadValue.Call(f.ctx, uint64(key), uint64(value)) 444 if err != nil { 445 panic(err.Error()) 446 } 447 }) 448 } 449 450 func (f *wazeroExtEngine) hostMustExist(keyId uint64) (result uint64) { 451 return uint64(f.safeApi.MustGetValue(safe.TKeyBuilder(keyId))) 452 } 453 454 const maxUint64 = ^uint64(0) 455 456 func (f *wazeroExtEngine) hostCanExist(keyId uint64) (result uint64) { 457 v, ok := f.safeApi.QueryValue(safe.TKeyBuilder(keyId)) 458 if !ok { 459 return maxUint64 460 } 461 return uint64(v) 462 } 463 464 func (f *wazeroExtEngine) allocAndSend(buf []byte) (result uint64) { 465 addrPkg, e := f.allocBuf(uint32(len(buf))) 466 if e != nil { 467 panic(e) 468 } 469 if !f.pkg.module.Memory().Write(addrPkg, buf) { 470 panic(errMemoryOutOfRange) 471 } 472 return (uint64(addrPkg) << uint64(bitsInFourBytes)) | uint64(len(buf)) 473 } 474 475 func (f *wazeroExtEngine) hostKeyAsString(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 476 v := f.safeApi.KeyAsString(safe.TKey(id), f.decodeStr(namePtr, nameSize)) 477 return f.allocAndSend([]byte(v)) 478 } 479 480 func (f *wazeroExtEngine) hostKeyAsBytes(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 481 v := f.safeApi.KeyAsBytes(safe.TKey(id), f.decodeStr(namePtr, nameSize)) 482 return f.allocAndSend(v) 483 } 484 485 func (f *wazeroExtEngine) hostKeyAsInt32(id uint64, namePtr uint32, nameSize uint32) (result uint32) { 486 return uint32(f.safeApi.KeyAsInt32(safe.TKey(id), f.decodeStr(namePtr, nameSize))) 487 } 488 489 func (f *wazeroExtEngine) hostKeyAsInt64(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 490 return uint64(f.safeApi.KeyAsInt64(safe.TKey(id), f.decodeStr(namePtr, nameSize))) 491 } 492 493 func (f *wazeroExtEngine) hostKeyAsBool(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 494 b := f.safeApi.KeyAsBool(safe.TKey(id), f.decodeStr(namePtr, nameSize)) 495 if b { 496 return uint64(1) 497 } 498 return uint64(0) 499 } 500 501 func (f *wazeroExtEngine) hostKeyAsQNamePkg(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 502 qname := f.safeApi.KeyAsQName(safe.TKey(id), f.decodeStr(namePtr, nameSize)) 503 return f.allocAndSend([]byte(qname.FullPkgName)) 504 } 505 506 func (f *wazeroExtEngine) hostKeyAsQNameEntity(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 507 qname := f.safeApi.KeyAsQName(safe.TKey(id), f.decodeStr(namePtr, nameSize)) 508 return f.allocAndSend([]byte(qname.Entity)) 509 } 510 511 func (f *wazeroExtEngine) hostKeyAsFloat32(key uint64, namePtr uint32, nameSize uint32) (result float32) { 512 return f.safeApi.KeyAsFloat32(safe.TKey(key), f.decodeStr(namePtr, nameSize)) 513 } 514 515 func (f *wazeroExtEngine) hostKeyAsFloat64(key uint64, namePtr uint32, nameSize uint32) (result float64) { 516 return f.safeApi.KeyAsFloat64(safe.TKey(key), f.decodeStr(namePtr, nameSize)) 517 } 518 519 func (f *wazeroExtEngine) hostValueGetAsString(value uint64, index uint32) (result uint64) { 520 v := f.safeApi.ValueGetAsString(safe.TValue(value), int(index)) 521 return f.allocAndSend([]byte(v)) 522 } 523 524 func (f *wazeroExtEngine) hostValueGetAsQNameEntity(value uint64, index uint32) (result uint64) { 525 qname := f.safeApi.ValueGetAsQName(safe.TValue(value), int(index)) 526 return f.allocAndSend([]byte(qname.Entity)) 527 } 528 529 func (f *wazeroExtEngine) hostValueGetAsQNamePkg(value uint64, index uint32) (result uint64) { 530 qname := f.safeApi.ValueGetAsQName(safe.TValue(value), int(index)) 531 return f.allocAndSend([]byte(qname.FullPkgName)) 532 } 533 534 func (f *wazeroExtEngine) hostValueGetAsBytes(value uint64, index uint32) (result uint64) { 535 return f.allocAndSend(f.safeApi.ValueGetAsBytes(safe.TValue(value), int(index))) 536 } 537 538 func (f *wazeroExtEngine) hostValueGetAsBool(value uint64, index uint32) (result uint64) { 539 b := f.safeApi.ValueGetAsBool(safe.TValue(value), int(index)) 540 if b { 541 return 1 542 } 543 return 0 544 } 545 546 func (f *wazeroExtEngine) hostValueGetAsInt32(value uint64, index uint32) (result int32) { 547 return f.safeApi.ValueGetAsInt32(safe.TValue(value), int(index)) 548 } 549 550 func (f *wazeroExtEngine) hostValueGetAsInt64(value uint64, index uint32) (result uint64) { 551 return uint64(f.safeApi.ValueGetAsInt64(safe.TValue(value), int(index))) 552 } 553 554 func (f *wazeroExtEngine) hostValueGetAsFloat32(id uint64, index uint32) float32 { 555 return f.safeApi.ValueGetAsFloat32(safe.TValue(id), int(index)) 556 } 557 558 func (f *wazeroExtEngine) hostValueGetAsFloat64(id uint64, index uint32) float64 { 559 return f.safeApi.ValueGetAsFloat64(safe.TValue(id), int(index)) 560 } 561 562 func (f *wazeroExtEngine) hostValueGetAsValue(val uint64, index uint32) (result uint64) { 563 return uint64(f.safeApi.ValueGetAsValue(safe.TValue(val), int(index))) 564 } 565 566 func (f *wazeroExtEngine) hostValueAsString(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 567 s := f.safeApi.ValueAsString(safe.TValue(id), f.decodeStr(namePtr, nameSize)) 568 return f.allocAndSend([]byte(s)) 569 } 570 571 func (f *wazeroExtEngine) hostValueAsBytes(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 572 b := f.safeApi.ValueAsBytes(safe.TValue(id), f.decodeStr(namePtr, nameSize)) 573 return f.allocAndSend(b) 574 } 575 576 func (f *wazeroExtEngine) hostValueAsInt32(id uint64, namePtr uint32, nameSize uint32) (result int32) { 577 return f.safeApi.ValueAsInt32(safe.TValue(id), f.decodeStr(namePtr, nameSize)) 578 } 579 580 func (f *wazeroExtEngine) hostValueAsInt64(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 581 return uint64(f.safeApi.ValueAsInt64(safe.TValue(id), f.decodeStr(namePtr, nameSize))) 582 } 583 584 func (f *wazeroExtEngine) hostValueAsBool(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 585 b := f.safeApi.ValueAsBool(safe.TValue(id), f.decodeStr(namePtr, nameSize)) 586 if b { 587 return 1 588 } 589 return 0 590 } 591 592 func (f *wazeroExtEngine) hostValueAsFloat32(id uint64, namePtr, nameSize uint32) float32 { 593 return f.safeApi.ValueAsFloat32(safe.TValue(id), f.decodeStr(namePtr, nameSize)) 594 } 595 596 func (f *wazeroExtEngine) hostValueAsFloat64(id uint64, namePtr, nameSize uint32) float64 { 597 return f.safeApi.ValueAsFloat64(safe.TValue(id), f.decodeStr(namePtr, nameSize)) 598 } 599 600 func (f *wazeroExtEngine) hostValueAsQNameEntity(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 601 qname := f.safeApi.ValueAsQName(safe.TValue(id), f.decodeStr(namePtr, nameSize)) 602 return f.allocAndSend([]byte(qname.Entity)) 603 } 604 605 func (f *wazeroExtEngine) hostValueAsQNamePkg(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 606 qname := f.safeApi.ValueAsQName(safe.TValue(id), f.decodeStr(namePtr, nameSize)) 607 return f.allocAndSend([]byte(qname.FullPkgName)) 608 } 609 610 func (f *wazeroExtEngine) hostValueAsValue(id uint64, namePtr uint32, nameSize uint32) (result uint64) { 611 return uint64(f.safeApi.ValueAsValue(safe.TValue(id), f.decodeStr(namePtr, nameSize))) 612 } 613 614 func (f *wazeroExtEngine) hostValueLength(id uint64) (result uint32) { 615 return uint32(f.safeApi.ValueLen(safe.TValue(id))) 616 } 617 618 func (f *wazeroExtEngine) allocBuf(size uint32) (addr uint32, err error) { 619 for i := range f.pkg.allocatedBufs { 620 if f.pkg.allocatedBufs[i].cap-f.pkg.allocatedBufs[i].offs >= size { 621 addr = f.pkg.allocatedBufs[i].addr + f.pkg.allocatedBufs[i].offs 622 f.pkg.allocatedBufs[i].offs += size 623 return 624 } 625 } 626 // no space in the allocated buffers 627 628 var newBufferSize uint32 = WasmPreallocatedBufferIncrease 629 if size > newBufferSize { 630 newBufferSize = size 631 } 632 633 var res []uint64 634 res, err = f.pkg.funcMalloc.Call(f.ctx, uint64(newBufferSize)) 635 if err != nil { 636 return 0, err 637 } 638 addr = uint32(res[0]) 639 f.pkg.allocatedBufs = append(f.pkg.allocatedBufs, &allocatedBuf{ 640 addr: addr, 641 offs: 0, 642 cap: newBufferSize, 643 }) 644 return addr, nil 645 } 646 647 func (f *wazeroExtEngine) getFrees(packagePath string, ctx context.Context) (uint64, error) { 648 pkg, ok := f.modules[packagePath] 649 if !ok { 650 return 0, errUndefinedPackage(packagePath) 651 } 652 res, err := pkg.funcGetFrees.Call(ctx) 653 if err != nil { 654 return 0, err 655 } 656 return res[0], nil 657 } 658 659 func (f *wazeroExtEngine) gc(packagePath string, ctx context.Context) error { 660 pkg, ok := f.modules[packagePath] 661 if !ok { 662 return errUndefinedPackage(packagePath) 663 } 664 _, err := pkg.funcGc.Call(ctx) 665 if err != nil { 666 return err 667 } 668 return nil 669 } 670 671 func (f *wazeroExtEngine) getHeapinuse(packagePath string, ctx context.Context) (uint64, error) { 672 pkg, ok := f.modules[packagePath] 673 if !ok { 674 return 0, errUndefinedPackage(packagePath) 675 } 676 res, err := pkg.funcGetHeapInuse.Call(ctx) 677 if err != nil { 678 return 0, err 679 } 680 return res[0], nil 681 } 682 683 func (f *wazeroExtEngine) getHeapSys(packagePath string, ctx context.Context) (uint64, error) { 684 pkg, ok := f.modules[packagePath] 685 if !ok { 686 return 0, errUndefinedPackage(packagePath) 687 } 688 res, err := pkg.funcGetHeapSys.Call(ctx) 689 if err != nil { 690 return 0, err 691 } 692 return res[0], nil 693 } 694 695 func (f *wazeroExtEngine) getMallocs(packagePath string, ctx context.Context) (uint64, error) { 696 pkg, ok := f.modules[packagePath] 697 if !ok { 698 return 0, errUndefinedPackage(packagePath) 699 } 700 res, err := pkg.funcGetMallocs.Call(ctx) 701 if err != nil { 702 return 0, err 703 } 704 return res[0], nil 705 } 706 707 func (f *wazeroExtEngine) hostNewValue(keyId uint64) uint64 { 708 return uint64(f.safeApi.NewValue(safe.TKeyBuilder(keyId))) 709 } 710 711 func (f *wazeroExtEngine) hostUpdateValue(keyId, existingValueId uint64) (result uint64) { 712 return uint64(f.safeApi.UpdateValue(safe.TKeyBuilder(keyId), safe.TValue(existingValueId))) 713 } 714 715 func (f *wazeroExtEngine) hostRowWriterPutString(id uint64, typ uint32, namePtr uint32, nameSize, valuePtr, valueSize uint32) { 716 if typ == 0 { 717 f.safeApi.KeyBuilderPutString(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), f.decodeStr(valuePtr, valueSize)) 718 } else { 719 f.safeApi.IntentPutString(safe.TIntent(id), f.decodeStr(namePtr, nameSize), f.decodeStr(valuePtr, valueSize)) 720 } 721 } 722 723 func (f *wazeroExtEngine) hostRowWriterPutBytes(id uint64, typ uint32, namePtr uint32, nameSize, valuePtr, valueSize uint32) { 724 var bytes []byte 725 var ok bool 726 bytes, ok = f.pkg.module.Memory().Read(valuePtr, valueSize) 727 if !ok { 728 panic(ErrUnableToReadMemory) 729 } 730 if typ == 0 { 731 f.safeApi.KeyBuilderPutBytes(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), bytes) 732 } else { 733 f.safeApi.IntentPutBytes(safe.TIntent(id), f.decodeStr(namePtr, nameSize), bytes) 734 } 735 } 736 737 func (f *wazeroExtEngine) hostRowWriterPutInt32(id uint64, typ uint32, namePtr uint32, nameSize uint32, value int32) { 738 if typ == 0 { 739 f.safeApi.KeyBuilderPutInt32(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), value) 740 } else { 741 f.safeApi.IntentPutInt32(safe.TIntent(id), f.decodeStr(namePtr, nameSize), value) 742 } 743 } 744 745 func (f *wazeroExtEngine) hostRowWriterPutInt64(id uint64, typ uint32, namePtr uint32, nameSize uint32, value int64) { 746 if typ == 0 { 747 f.safeApi.KeyBuilderPutInt64(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), value) 748 } else { 749 f.safeApi.IntentPutInt64(safe.TIntent(id), f.decodeStr(namePtr, nameSize), value) 750 } 751 } 752 753 func (f *wazeroExtEngine) hostRowWriterPutQName(id uint64, typ uint32, namePtr uint32, nameSize uint32, pkgPtr, pkgSize, entityPtr, entitySize uint32) { 754 qname := safe.QName{ 755 FullPkgName: f.decodeStr(pkgPtr, pkgSize), 756 Entity: f.decodeStr(entityPtr, entitySize), 757 } 758 if typ == 0 { 759 f.safeApi.KeyBuilderPutQName(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), qname) 760 } else { 761 f.safeApi.IntentPutQName(safe.TIntent(id), f.decodeStr(namePtr, nameSize), qname) 762 } 763 } 764 765 func (f *wazeroExtEngine) hostRowWriterPutBool(id uint64, typ uint32, namePtr uint32, nameSize uint32, value int32) { 766 if typ == 0 { 767 f.safeApi.KeyBuilderPutBool(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), value > 0) 768 } else { 769 f.safeApi.IntentPutBool(safe.TIntent(id), f.decodeStr(namePtr, nameSize), value > 0) 770 } 771 } 772 773 func (f *wazeroExtEngine) hostRowWriterPutFloat32(id uint64, typ uint32, namePtr uint32, nameSize uint32, value float32) { 774 if typ == 0 { 775 f.safeApi.KeyBuilderPutFloat32(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), value) 776 } else { 777 f.safeApi.IntentPutFloat32(safe.TIntent(id), f.decodeStr(namePtr, nameSize), value) 778 } 779 } 780 781 func (f *wazeroExtEngine) hostRowWriterPutFloat64(id uint64, typ uint32, namePtr, nameSize uint32, value float64) { 782 if typ == 0 { 783 f.safeApi.KeyBuilderPutFloat64(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), value) 784 } else { 785 f.safeApi.IntentPutFloat64(safe.TIntent(id), f.decodeStr(namePtr, nameSize), value) 786 } 787 }