-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplugin.go
More file actions
162 lines (148 loc) · 4.91 KB
/
Copy pathplugin.go
File metadata and controls
162 lines (148 loc) · 4.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package component
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"github.com/wago-org/component-model/internal/engine"
"github.com/wago-org/component-model/internal/instance"
"github.com/wago-org/wago"
wagoplugin "github.com/wago-org/wago/plugin"
)
// PluginID is the canonical Component Model plugin ID.
const PluginID = "github.com/wago-org/component-model"
const (
// Wago charges an unbounded memory32 declaration at its finite 65,535-page
// implementation reservation. Sixteen GiB therefore admits four ordinary
// unbounded-memory modules while the separate slot limit leaves room for
// memoryless adapters and linker shims.
requestedMaxCoreInstances = 64
requestedMaxCoreMemoryBytes = 16 << 30
)
// Contract is the major-versioned Component Model execution service consumed
// by WASI and other component-world plugins.
var Contract = wagoplugin.NewContract[Service](PluginID+"/runtime", 1)
// Service is the Component Model plugin's cross-plugin execution boundary.
// WithInstance keeps the service and every core resource it creates inside the
// caller's contract lease. The instance is closed before WithInstance returns
// and must not be retained by fn.
type Service interface {
WithInstance(context.Context, []byte, func(*Instance) error, ...Option) error
}
var configSchema = json.RawMessage(`{
"type": "object",
"additionalProperties": false,
"maxProperties": 0
}`)
// Definition returns fresh immutable metadata for the explicit provider.
func Definition() wago.PluginDefinition {
return wago.PluginDefinition{
ID: PluginID,
Name: "Wago Component Model",
Version: "0.1.0",
Description: "WebAssembly Component Model execution and Canonical ABI linking for Wago.",
Stability: wago.Experimental,
Compatibility: wago.Compatibility{
Engines: map[string]string{"wago": ">=0.1.0"},
},
Provenance: wago.PluginProvenance{
Homepage: "https://github.com/wago-org/component-model#readme",
Repository: "https://github.com/wago-org/component-model",
License: "Apache-2.0",
Authors: []string{"Jairus Tanaka"},
},
Authorities: []wago.AuthorityRequest{
{
Name: wago.AuthorityCoreModuleCompile,
Mode: wago.AuthorityRequired,
Reason: "compile the core WebAssembly modules embedded in a component",
},
{
Name: wago.AuthorityCoreInstanceInstantiate,
Mode: wago.AuthorityRequired,
Reason: "instantiate and own the bounded core-module graph behind a component instance",
Scope: wago.AuthorityScope{
MaxInstances: requestedMaxCoreInstances,
MaxMemoryBytes: requestedMaxCoreMemoryBytes,
},
},
{
Name: wago.AuthorityCoreFuncRefCreate,
Mode: wago.AuthorityRequired,
Reason: "bridge Canonical ABI lifts and lowers through typed host function references",
},
},
ConfigSchema: append(json.RawMessage(nil), configSchema...),
Provides: []wago.ContractSpec{Contract.Spec()},
}
}
// Provider is the side-effect-free catalog entry for Component Model support.
func Provider() wago.PluginProvider {
return wago.PluginProvider{
Definition: Definition(),
New: func() wago.Plugin { return new(componentPlugin) },
ValidateConfig: validateConfig,
}
}
func validateConfig(raw json.RawMessage) error {
if len(raw) == 0 {
raw = json.RawMessage(`{}`)
}
dec := json.NewDecoder(bytes.NewReader(raw))
dec.DisallowUnknownFields()
var cfg struct{}
if err := dec.Decode(&cfg); err != nil {
return fmt.Errorf("component: config: %w", err)
}
if bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return fmt.Errorf("component: config must be an object")
}
if err := dec.Decode(new(any)); !errors.Is(err, io.EOF) {
return fmt.Errorf("component: config has a trailing JSON value")
}
return nil
}
type componentPlugin struct{}
func (*componentPlugin) Register(reg *wago.Registrar) error {
var cfg struct{}
if err := reg.Config(&cfg); err != nil {
return err
}
compiler, err := reg.CoreModuleCompiler()
if err != nil {
return err
}
instantiator, err := reg.CoreInstanceInstantiator()
if err != nil {
return err
}
funcrefs, err := reg.CoreFuncRefFactory()
if err != nil {
return err
}
service := &runtimeService{engine: engine.Wrap(compiler, instantiator, funcrefs)}
return wagoplugin.Provide(reg, Contract, Service(service))
}
type runtimeService struct {
engine engine.Runtime
}
func (r *runtimeService) WithInstance(ctx context.Context, componentBytes []byte, fn func(*Instance) error, opts ...Option) (err error) {
if r == nil || r.engine == nil {
return fmt.Errorf("component: inactive component service")
}
if ctx == nil {
return fmt.Errorf("component: nil context")
}
if fn == nil {
return fmt.Errorf("component: nil instance callback")
}
in, err := instance.Instantiate(ctx, r.engine, componentBytes, opts...)
if err != nil {
return err
}
closeCtx := context.WithoutCancel(ctx)
defer func() { err = errors.Join(err, in.Close(closeCtx)) }()
return fn(in)
}