mirror of
https://github.com/hanzoai/base.git
synced 2026-10-21 03:41:50 +00:00
Foundation for pluggable extension runtimes. One Runtime interface that wazero, v8go, goja, and native-Go all satisfy. Extensions declare their preferred runtime in extension.json; the loader dispatches. This sits alongside plugins/jsvm — goja-via-the-existing-jsvm-path remains the default for .base.js hooks. The new path is for users who want hard-sandboxed WASM extensions or full V8 perf. Files: - plugins/extruntime/extruntime.go — Runtime + Module interfaces, Manifest - plugins/extruntime/manifest.go — JSON manifest loader (stdlib only) - plugins/extruntime/native.go — reference impl, registers Go funcs Concrete runtimes land in sibling commits: - plugins/wasmvm — wazero (pure Go, hard sandbox) - plugins/v8vm — v8go (cgo, -tags v8vm) - plugins/gojavm — goja adapter (no new deps) - plugins/extbench — fixtures + benchmarks across all 5
92 lines
2.4 KiB
Go
92 lines
2.4 KiB
Go
package extruntime
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
// NativeFunc is the signature every native extension function must satisfy.
|
|
type NativeFunc func(ctx context.Context, payload []byte) ([]byte, error)
|
|
|
|
// NativeRegistry is a process-wide registry of compiled-in native Go
|
|
// extension functions, keyed by "<extension-name>:<fn-name>". The native
|
|
// runtime resolves Invoke() against this map. Tests and benchmarks
|
|
// register fixtures here; production code links extensions in at build
|
|
// time the same way.
|
|
var NativeRegistry = struct {
|
|
mu sync.RWMutex
|
|
m map[string]NativeFunc
|
|
}{m: map[string]NativeFunc{}}
|
|
|
|
// RegisterNative adds a native function to the registry. Safe to call
|
|
// from init() in extension packages.
|
|
func RegisterNative(ext, fn string, f NativeFunc) {
|
|
NativeRegistry.mu.Lock()
|
|
defer NativeRegistry.mu.Unlock()
|
|
NativeRegistry.m[ext+":"+fn] = f
|
|
}
|
|
|
|
// NewNative returns a native Go runtime. Code is linked into the binary
|
|
// at compile time; the runtime just looks functions up.
|
|
func NewNative() Runtime { return &nativeRuntime{} }
|
|
|
|
type nativeRuntime struct{}
|
|
|
|
func (*nativeRuntime) Name() string { return "native" }
|
|
func (*nativeRuntime) Capabilities() Capabilities {
|
|
return Capabilities{
|
|
AcceptsLanguages: []string{"go"},
|
|
HardSandbox: false,
|
|
Cgo: false,
|
|
SupportsAbort: false,
|
|
}
|
|
}
|
|
|
|
func (*nativeRuntime) Load(ctx context.Context, dir string) (Module, error) {
|
|
m, err := LoadManifest(dir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if m.Runtime != "native" {
|
|
return nil, fmt.Errorf("%w: native runtime cannot load %q runtime", ErrUnsupported, m.Runtime)
|
|
}
|
|
return &nativeModule{name: m.Name, exports: m.Exports}, nil
|
|
}
|
|
func (*nativeRuntime) Close() error { return nil }
|
|
|
|
type nativeModule struct {
|
|
name string
|
|
exports []string
|
|
closed bool
|
|
mu sync.Mutex
|
|
}
|
|
|
|
func (m *nativeModule) Name() string { return m.name }
|
|
func (m *nativeModule) Runtime() string { return "native" }
|
|
func (m *nativeModule) Exports() []string { return m.exports }
|
|
|
|
func (m *nativeModule) Invoke(ctx context.Context, fn string, payload []byte) ([]byte, error) {
|
|
m.mu.Lock()
|
|
if m.closed {
|
|
m.mu.Unlock()
|
|
return nil, ErrClosed
|
|
}
|
|
m.mu.Unlock()
|
|
|
|
NativeRegistry.mu.RLock()
|
|
f, ok := NativeRegistry.m[m.name+":"+fn]
|
|
NativeRegistry.mu.RUnlock()
|
|
if !ok {
|
|
return nil, fmt.Errorf("%w: %s:%s", ErrUnknownFn, m.name, fn)
|
|
}
|
|
return f(ctx, payload)
|
|
}
|
|
|
|
func (m *nativeModule) Close() error {
|
|
m.mu.Lock()
|
|
m.closed = true
|
|
m.mu.Unlock()
|
|
return nil
|
|
}
|