diff --git a/cmd/wit-bindgen-go/cmd/generate/generate.go b/cmd/wit-bindgen-go/cmd/generate/generate.go index c207a00e..1d445f97 100644 --- a/cmd/wit-bindgen-go/cmd/generate/generate.go +++ b/cmd/wit-bindgen-go/cmd/generate/generate.go @@ -28,7 +28,7 @@ var Command = &cli.Command{ Value: "", OnlyOnce: true, Config: cli.StringConfig{TrimSpace: true}, - Usage: "WIT world to generate, otherwise generate all worlds", + Usage: "WIT world to generate, otherwise generate the first world", }, &cli.StringFlag{ Name: "out", @@ -52,11 +52,15 @@ var Command = &cli.Command{ Value: "", OnlyOnce: true, Config: cli.StringConfig{TrimSpace: true}, - Usage: "Import path for the Component Model utility package, e.g. go.bytecodealliance.org/cm", + Usage: "import path for the Component Model utility package, e.g. go.bytecodealliance.org/cm", }, &cli.BoolFlag{ Name: "versioned", - Usage: "emit versioned Go package(s) for each WIT version", + Usage: "emit versioned Go package(s) corresponding to WIT package version", + }, + &cli.BoolFlag{ + Name: "generate-wit", + Usage: "generate a WIT file for each generated Go package corresponding to each WIT world or interface", }, &cli.BoolFlag{ Name: "dry-run", @@ -68,16 +72,17 @@ var Command = &cli.Command{ // Config is the configuration for the `generate` command. type config struct { - logger logging.Logger - dryRun bool - out string - outPerm os.FileMode - pkgRoot string - world string - cm string - versioned bool - forceWIT bool - path string + logger logging.Logger + dryRun bool + out string + outPerm os.FileMode + pkgRoot string + world string + cm string + versioned bool + generateWIT bool + forceWIT bool + path string } func action(ctx context.Context, cmd *cli.Command) error { @@ -94,10 +99,11 @@ func action(ctx context.Context, cmd *cli.Command) error { packages, err := bindgen.Go(res, bindgen.GeneratedBy(cmd.Root().Name), bindgen.Logger(cfg.logger), - bindgen.World(cfg.world), bindgen.PackageRoot(cfg.pkgRoot), - bindgen.Versioned(cfg.versioned), + bindgen.World(cfg.world), bindgen.CMPackage(cfg.cm), + bindgen.Versioned(cfg.versioned), + bindgen.WIT(cfg.generateWIT), ) if err != nil { return err @@ -141,6 +147,7 @@ func parseFlags(_ context.Context, cmd *cli.Command) (*config, error) { cmd.String("world"), cmd.String("cm"), cmd.Bool("versioned"), + cmd.Bool("generate-wit"), cmd.Bool("force-wit"), path, }, nil diff --git a/cmd/wit-bindgen-go/cmd/wit/wit.go b/cmd/wit-bindgen-go/cmd/wit/wit.go index 06e55ac3..2d7c86a4 100644 --- a/cmd/wit-bindgen-go/cmd/wit/wit.go +++ b/cmd/wit-bindgen-go/cmd/wit/wit.go @@ -21,7 +21,15 @@ var Command = &cli.Command{ Value: "", OnlyOnce: true, Config: cli.StringConfig{TrimSpace: true}, - Usage: "WIT world to generate, otherwise generate all worlds", + Usage: "WIT world to emit, otherwise emit all worlds", + }, + &cli.StringFlag{ + Name: "interface", + Aliases: []string{"i"}, + Value: "", + OnlyOnce: true, + Config: cli.StringConfig{TrimSpace: true}, + Usage: "WIT interface to emit, otherwise emit all interfaces", }, }, Action: action, @@ -32,19 +40,30 @@ func action(ctx context.Context, cmd *cli.Command) error { if err != nil { return err } + res, err := witcli.LoadWIT(ctx, path, cmd.Reader, cmd.Bool("force-wit")) if err != nil { return err } + var w *wit.World - world := cmd.String("world") - if world != "" { + if world := cmd.String("world"); world != "" { w = findWorld(res, world) if w == nil { return fmt.Errorf("world %s not found", world) } } - fmt.Print(res.WIT(w, "")) + + var i *wit.Interface + if face := cmd.String("interface"); face != "" { + i = findInterface(res, face) + if i == nil { + return fmt.Errorf("interface %s not found", face) + } + } + + filter := wit.Filter(w, i) + fmt.Print(res.WIT(filter, "")) return nil } @@ -56,3 +75,12 @@ func findWorld(r *wit.Resolve, pattern string) *wit.World { } return nil } + +func findInterface(r *wit.Resolve, pattern string) *wit.Interface { + for _, i := range r.Interfaces { + if i.Match(pattern) { + return i + } + } + return nil +} diff --git a/internal/memoize/memoize.go b/internal/memoize/memoize.go new file mode 100644 index 00000000..da9013d8 --- /dev/null +++ b/internal/memoize/memoize.go @@ -0,0 +1,16 @@ +package memoize + +// Function memoizes f, caching unique values of k. +// Initial calls to the resulting function will call f(k), then cache and return v. +// Subsequent calls will return the cached value for k. +func Function[F func(K) V, K comparable, V any](f F) F { + m := make(map[K]V) + return func(k K) V { + if v, ok := m[k]; ok { + return v + } + v := f(k) + m[k] = v + return v + } +} diff --git a/internal/wasm/section.go b/internal/wasm/section.go index 18666c16..f94b5181 100644 --- a/internal/wasm/section.go +++ b/internal/wasm/section.go @@ -1,5 +1,11 @@ package wasm +import ( + "bytes" + + "go.bytecodealliance.org/internal/wasm/uleb128" +) + // SectionID represents a WebAssembly [section SectionID]. // // [section SectionID]: https://webassembly.github.io/spec/core/binary/modules.html#sections @@ -47,6 +53,34 @@ func (*CustomSection) SectionID() SectionID { // SectionContents implements the [Section] interface. func (s *CustomSection) SectionContents() ([]byte, error) { - // TODO: encode name correctly - return append([]byte(s.Name), s.Contents...), nil + var buf bytes.Buffer + _, err := WriteString(&buf, s.Name) + if err != nil { + return nil, err + } + _, err = buf.Write(s.Contents) + return buf.Bytes(), err +} + +type LinkingSection struct{} + +// SectionID implements the [Section] interface. +func (*LinkingSection) SectionID() SectionID { + return SectionCustom +} + +// SectionContents implements the [Section] interface. +func (s *LinkingSection) SectionContents() ([]byte, error) { + var buf bytes.Buffer + custom := &CustomSection{Name: "linking"} + contents, err := custom.SectionContents() + if err != nil { + return nil, err + } + _, err = buf.Write(contents) + if err != nil { + return nil, err + } + _, err = uleb128.Write(&buf, 2) // linking section version 2 + return buf.Bytes(), err } diff --git a/wit/bindgen/generator.go b/wit/bindgen/generator.go index 178ed027..fd18dd14 100644 --- a/wit/bindgen/generator.go +++ b/wit/bindgen/generator.go @@ -9,6 +9,7 @@ import ( "fmt" "go/token" "io" + "os/exec" "path" "path/filepath" "runtime" @@ -20,6 +21,7 @@ import ( "go.bytecodealliance.org/internal/codec" "go.bytecodealliance.org/internal/go/gen" "go.bytecodealliance.org/internal/stringio" + "go.bytecodealliance.org/internal/wasm" "go.bytecodealliance.org/wit" "go.bytecodealliance.org/wit/logging" ) @@ -83,8 +85,9 @@ type typeUse struct { } type generator struct { - opts options - res *wit.Resolve + opts options + res *wit.Resolve + world *wit.World // optional, can be nil // versioned is set to true if there are multiple versions of a WIT package in res, // which affects the generated Go package paths. @@ -152,9 +155,25 @@ func newGenerator(res *wit.Resolve, opts ...Option) (*generator, error) { g.opts.cmPackage = cmPackage } g.res = res + for _, g.world = range res.Worlds { + if g.world.Match(g.opts.world) { + break + } + // otherwise chose the last world + } return g, nil } +// TODO: factor this out +func findWorld(r *wit.Resolve, pattern string) *wit.World { + for _, w := range r.Worlds { + if w.Match(pattern) { + return w + } + } + return nil +} + func (g *generator) generate() ([]*gen.Package, error) { g.detectVersionedPackages() err := g.defineWorlds() @@ -205,8 +224,8 @@ func (g *generator) define(dir wit.Direction, v wit.Node) (defined bool) { // WIT interfaces and/or worlds into a single Go package. func (g *generator) defineWorlds() error { g.opts.logger.Infof("Generating Go for %d world(s)\n", len(g.res.Worlds)) - for i, w := range g.res.Worlds { - if w.Match(g.opts.world) || (g.opts.world == "" && i == len(g.res.Worlds)-1) { + for _, w := range g.res.Worlds { + if w == g.world || g.world == nil { err := g.defineWorld(w) if err != nil { return err @@ -231,8 +250,8 @@ func (g *generator) defineWorld(w *wit.World) error { } // Write WIT file for this world - witFile := g.witFileFor(w) - witFile.WriteString(g.res.WIT(w, "")) + // witFile := g.witFileFor(w) + // witFile.WriteString(g.res.WIT(wit.Filter(w, nil), "")) // Write Go package docs file := g.fileFor(w) @@ -299,8 +318,13 @@ func (g *generator) defineInterface(w *wit.World, dir wit.Direction, i *wit.Inte if err != nil { return err } - file := g.fileFor(i) + // Write WIT file for this interface + // Disabled until we write metadata .wasm file + // witFile := g.witFileFor(i) + // witFile.WriteString(g.res.WIT(wit.Filter(g.world, i), "")) + + file := g.fileFor(i) { var b strings.Builder stringio.Write(&b, "Package ", pkg.Name, " represents the ", dir.String(), " ", i.WITKind(), " \"", g.moduleNames[i], "\".\n") @@ -2277,7 +2301,7 @@ func (g *generator) newPackage(w *wit.World, i *wit.Interface, name string) (*ge if name != id.Extension { segments = append(segments, name) // for anonymous interfaces nested under worlds } - path := strings.Join(segments, "/") + pkgPath := strings.Join(segments, "/") // TODO: write tests for this goName := GoPackageName(name) @@ -2291,29 +2315,89 @@ func (g *generator) newPackage(w *wit.World, i *wit.Interface, name string) (*ge } } - pkg = gen.NewPackage(path + "#" + goName) + pkg = gen.NewPackage(pkgPath + "#" + goName) g.packages[pkg.Path] = pkg g.witPackages[owner] = pkg g.exportScopes[owner] = gen.NewScope(nil) pkg.DeclareName("Exports") - // Write a Cgo file that adds a library to the linker arguments. - // The library is a WebAssembly file that includes a custom section - // with a name prefixed with "component-type:". The contents are the + // Write a WebAssembly file that includes a custom section + // with a name prefixed with "component-type". The contents are the // Component Model definition for a world that encapsulates the // Component Model types and functions imported into and/or exported // from this Go package. - if false { - cgoFile := g.cgoFileFor(owner) - lib := id.String() - if name != id.Extension { - lib += "-" + name + { + // Synthesize a unique-ish name + worldID := w.Package.Name + worldID.Extension = "WORLD-" + w.Name + if i != nil { + worldID.Extension += "-INTERFACE-" + name + } + worldName := worldID.String() + worldName = replacer.Replace(worldName) + // libFile := pkg.File("lib" + worldName + ".a") + + // Generate wasm file + res, world := synthesizeWorld(g.res, w, worldName) + witText := res.WIT(wit.Filter(world, i), "") + if g.opts.generateWIT { + witFile := g.witFileFor(owner) + witFile.WriteString(witText) + } + content, err := g.componentEmbed(witText) + if err != nil { + // return nil, err + } + componentType := &wasm.CustomSection{ + Name: "component-type:" + worldName, + Contents: content, } - lib = strings.ReplaceAll(lib, "/", "-") - lib = strings.ReplaceAll(lib, ":", "-") - stringio.Write(cgoFile, "// #cgo LDFLAGS: -L. -l", lib, "\n") - stringio.Write(cgoFile, "import \"C\"\n") + if false { + sysoFile := pkg.File(path.Base(pkg.Path) + ".wasm.syso") + wasm.Write(sysoFile, []wasm.Section{&wasm.LinkingSection{}, componentType}) + } + + // Write Cgo file + // cgoFile := g.cgoFileFor(owner) + // stringio.Write(cgoFile, "// #cgo LDFLAGS: -L. -l", lib, "\n") + // stringio.Write(cgoFile, "import \"C\"\n") } return pkg, nil } + +var replacer = strings.NewReplacer("/", "-", ":", "-", "@", "-v", ".", "") + +// componentEmbed runs generated WIT through wasm-tools to generate a wasm file with a component-type custom section. +func (g *generator) componentEmbed(witData string) ([]byte, error) { + // TODO: --all-features? + cmd := exec.Command("wasm-tools", "component", "embed", "--only-custom", "/dev/stdin") + cmd.Stdin = strings.NewReader(witData) + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + cmd.Stdout = stdout + cmd.Stderr = stderr + err := cmd.Run() + if err != nil { + g.opts.logger.Errorf("wasm-tools: %s", stderr.String()) + } + return stdout.Bytes(), err +} + +func synthesizeWorld(r *wit.Resolve, w *wit.World, name string) (*wit.Resolve, *wit.World) { + p := &wit.Package{} + p.Name.Namespace = "go" + p.Name.Package = "bindgen" + + w = w.Clone() + w.Name = name + w.Docs = wit.Docs{} + w.Package = p + p.Worlds.Set(name, w) + + r = r.Clone() + r.Worlds = append(r.Worlds, w) + r.Packages = append(r.Packages, p) + + return r, w +} diff --git a/wit/bindgen/options.go b/wit/bindgen/options.go index c8c1956c..56e21f24 100644 --- a/wit/bindgen/options.go +++ b/wit/bindgen/options.go @@ -22,7 +22,7 @@ type options struct { generatedBy string // world is the name of the WIT world to generate, e.g. "command" or "wasi:cli/command". - // Default: all worlds in the Resolve will be generated. + // Default: the first world in the Resolve will be generated. world string // packageRoot is the root Go package or module path used in generated code. @@ -34,6 +34,9 @@ type options struct { // versioned determines if Go packages are generated with version numbers. versioned bool + + // generateWIT determines if WIT files will be generated for each world and interface. + generateWIT bool } func (opts *options) apply(o ...Option) error { @@ -64,6 +67,7 @@ func GeneratedBy(name string) Option { } // World returns an [Option] that specifies the WIT world to generate. +// By default, the first world will be generated. func World(world string) Option { return optionFunc(func(opts *options) error { opts.world = world @@ -96,3 +100,12 @@ func Versioned(versioned bool) Option { return nil }) } + +// WIT returns an [Option] that specifies that a WIT file will be generated +// for each Go package corresponding to each WIT world and interface. +func WIT(generateWIT bool) Option { + return optionFunc(func(opts *options) error { + opts.generateWIT = generateWIT + return nil + }) +} diff --git a/wit/docs.go b/wit/docs.go index d98ff882..f523d619 100644 --- a/wit/docs.go +++ b/wit/docs.go @@ -41,3 +41,8 @@ // [WebAssembly Interface Type]: https://component-model.bytecodealliance.org/design/wit.html // [Component]: https://component-model.bytecodealliance.org/introduction.html package wit + +// Docs represent WIT documentation text extracted from comments. +type Docs struct { + Contents string // may be empty +} diff --git a/wit/enum.go b/wit/enum.go new file mode 100644 index 00000000..a37b0980 --- /dev/null +++ b/wit/enum.go @@ -0,0 +1,59 @@ +package wit + +// Enum represents a WIT [enum type], which is a [Variant] without associated data. +// The equivalent in Go is a set of const identifiers declared with iota. +// It implements the [Node], [ABI], and [TypeDefKind] interfaces. +// +// [enum type]: https://component-model.bytecodealliance.org/design/wit.html#enums +type Enum struct { + _typeDefKind + Cases []EnumCase +} + +// Despecialize despecializes [Enum] e into a [Variant] with no associated types. +// See the [canonical ABI documentation] for more information. +// +// [canonical ABI documentation]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization +func (e *Enum) Despecialize() TypeDefKind { + v := &Variant{ + Cases: make([]Case, len(e.Cases)), + } + for i := range e.Cases { + v.Cases[i].Name = e.Cases[i].Name + v.Cases[i].Docs = e.Cases[i].Docs + } + return v +} + +// Size returns the [ABI byte size] for [Enum] e, the smallest integer +// type that can represent 0...len(e.Cases). +// It is first [despecialized] into a [Variant] with no associated types, then sized. +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization +func (e *Enum) Size() uintptr { + return e.Despecialize().Size() +} + +// Align returns the [ABI byte alignment] for [Enum] e. +// It is first [despecialized] into a [Variant] with no associated types, then aligned. +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization +func (e *Enum) Align() uintptr { + return e.Despecialize().Align() +} + +// Flat returns the [flattened] ABI representation of [Enum] e. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (v *Enum) Flat() []Type { + return Discriminant(len(v.Cases)).Flat() +} + +// EnumCase represents a single case in an [Enum]. +// It implements the [Node] interface. +type EnumCase struct { + Name string + Docs Docs +} diff --git a/wit/flags.go b/wit/flags.go new file mode 100644 index 00000000..9ed8d058 --- /dev/null +++ b/wit/flags.go @@ -0,0 +1,56 @@ +package wit + +// Flags represents a WIT [flags type], stored as a bitfield. +// It implements the [Node], [ABI], and [TypeDefKind] interfaces. +// +// [flags type]: https://component-model.bytecodealliance.org/design/wit.html#flags +type Flags struct { + _typeDefKind + Flags []Flag +} + +// Size returns the [ABI byte size] of [Flags] f. +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +func (f *Flags) Size() uintptr { + n := len(f.Flags) + switch { + case n <= 8: + return 1 + case n <= 16: + return 2 + } + return 4 * uintptr((n+31)>>5) +} + +// Align returns the [ABI byte alignment] of [Flags] f. +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +func (f *Flags) Align() uintptr { + n := len(f.Flags) + switch { + case n <= 8: + return 1 + case n <= 16: + return 2 + } + return 4 +} + +// Flat returns the [flattened] ABI representation of [Flags] f. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (f *Flags) Flat() []Type { + flat := make([]Type, (len(f.Flags)+31)>>5) + for i := range flat { + flat[i] = U32{} + } + return flat +} + +// Flag represents a single flag value in a [Flags] type. +// It implements the [Node] interface. +type Flag struct { + Name string + Docs Docs +} diff --git a/wit/function.go b/wit/function.go new file mode 100644 index 00000000..6da32747 --- /dev/null +++ b/wit/function.go @@ -0,0 +1,184 @@ +package wit + +import ( + "strings" +) + +// Function represents a WIT [function]. +// Functions can be freestanding, methods, constructors or static. +// It implements the [Node] and [WorldItem] interfaces. +// +// [function]: https://component-model.bytecodealliance.org/design/wit.html#functions +type Function struct { + _worldItem + Name string + Kind FunctionKind + Params []Param // arguments to the function + Results []Param // a function can have a single anonymous result, or > 1 named results + Stability Stability // WIT @since or @unstable (nil if unknown) + Docs Docs +} + +// BaseName returns the base name of [Function] f. +// For static functions, this returns the function name unchanged. +// For constructors, this removes the [constructor] and type prefix. +// For static functions, this removes the [static] and type prefix. +// For methods, this removes the [method] and type prefix. +// For special functions like [resource-drop], it will return a well-known value. +func (f *Function) BaseName() string { + switch { + case strings.HasPrefix(f.Name, "[constructor]"): + return "constructor" + case strings.HasPrefix(f.Name, "[resource-new]"): + return "resource-new" + case strings.HasPrefix(f.Name, "[resource-rep]"): + return "resource-rep" + case strings.HasPrefix(f.Name, "[resource-drop]"): + return "resource-drop" + case strings.HasPrefix(f.Name, "[dtor]"): + return "destructor" + } + name, after, found := strings.Cut(f.Name, ".") + if found { + name = after + } + after, found = strings.CutPrefix(f.Name, "cabi_post_") + if found { + name = after + "-post-return" + } + return name +} + +// Type returns the associated (self) [Type] for [Function] f, if f is a constructor, method, or static function. +// If f is a freestanding function, this returns nil. +func (f *Function) Type() Type { + switch kind := f.Kind.(type) { + case *Constructor: + return kind.Type + case *Static: + return kind.Type + case *Method: + return kind.Type + default: + return nil + } +} + +// IsAdmin returns true if [Function] f is an administrative function in the Canonical ABI. +func (f *Function) IsAdmin() bool { + switch { + // Imported + case f.IsStatic() && strings.HasPrefix(f.Name, "[resource-new]"): + return true + case f.IsMethod() && strings.HasPrefix(f.Name, "[resource-rep]"): + return true + case f.IsMethod() && strings.HasPrefix(f.Name, "[resource-drop]"): + return true + + // Exported + case f.IsMethod() && strings.HasPrefix(f.Name, "[dtor]"): + return true + case strings.HasPrefix(f.Name, "cabi_post_"): + return true + } + return false +} + +// IsFreestanding returns true if [Function] f is a freestanding function, +// and not a constructor, method, or static function. +func (f *Function) IsFreestanding() bool { + _, ok := f.Kind.(*Freestanding) + return ok +} + +// IsConstructor returns true if [Function] f is a constructor. +// To qualify, it must have a *[Constructor] Kind with a non-nil type. +func (f *Function) IsConstructor() bool { + kind, ok := f.Kind.(*Constructor) + return ok && kind.Type != nil +} + +// IsMethod returns true if [Function] f is a method. +// To qualify, it must have a *[Method] Kind with a non-nil [Type] which matches borrow of its first param. +func (f *Function) IsMethod() bool { + if len(f.Params) == 0 { + return false + } + kind, ok := f.Kind.(*Method) + if !ok { + return false + } + t := f.Params[0].Type + h := KindOf[*Borrow](t) + return t == kind.Type || (h != nil && h.Type == kind.Type) +} + +// IsStatic returns true if [Function] f is a static function. +// To qualify, it must have a *[Static] Kind with a non-nil type. +func (f *Function) IsStatic() bool { + kind, ok := f.Kind.(*Static) + return ok && kind.Type != nil +} + +func (f *Function) dependsOn(dep Node) bool { + if dep == f { + return true + } + for _, p := range f.Params { + if DependsOn(p.Type, dep) { + return true + } + } + for _, r := range f.Results { + if DependsOn(r.Type, dep) { + return true + } + } + return false +} + +func compareFunctions(a, b *Function) int { + return strings.Compare(a.Name, b.Name) +} + +// Param represents a parameter to or the result of a [Function]. +// A Param can be unnamed. +type Param struct { + Name string + Type Type +} + +// FunctionKind represents the kind of a WIT [function], which can be one of +// [Freestanding], [Method], [Static], or [Constructor]. +// +// [function]: https://component-model.bytecodealliance.org/design/wit.html#functions +type FunctionKind interface { + isFunctionKind() +} + +// _functionKind is an embeddable type that conforms to the [FunctionKind] interface. +type _functionKind struct{} + +func (_functionKind) isFunctionKind() {} + +// Freestanding represents a free-standing function that is not a method, static, or a constructor. +type Freestanding struct{ _functionKind } + +// Method represents a function that is a method on its associated [Type]. +// The first argument to the function is self, an instance of [Type]. +type Method struct { + _functionKind + Type Type +} + +// Static represents a function that is a static method of its associated [Type]. +type Static struct { + _functionKind + Type Type +} + +// Constructor represents a function that is a constructor for its associated [Type]. +type Constructor struct { + _functionKind + Type Type +} diff --git a/wit/future.go b/wit/future.go new file mode 100644 index 00000000..b5ec3380 --- /dev/null +++ b/wit/future.go @@ -0,0 +1,34 @@ +package wit + +// Future represents a WIT [future type], expected to be part of [WASI Preview 3]. +// It implements the [Node], [ABI], and [TypeDefKind] interfaces. +// +// [future type]: https://github.com/bytecodealliance/wit-bindgen/issues/270 +// [WASI Preview 3]: https://bytecodealliance.org/articles/webassembly-the-updated-roadmap-for-developers +type Future struct { + _typeDefKind + Type Type // optional associated Type (can be nil) +} + +// Size returns the [ABI byte size] for a [Future]. +// TODO: what is the ABI size of a future? +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +func (*Future) Size() uintptr { return 0 } + +// Align returns the [ABI byte alignment] a [Future]. +// TODO: what is the ABI alignment of a future? +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +func (*Future) Align() uintptr { return 0 } + +// Flat returns the [flattened] ABI representation of [Future]. +// TODO: what is the ABI representation of a stream? +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (*Future) Flat() []Type { return nil } + +func (f *Future) hasPointer() bool { return HasPointer(f.Type) } +func (f *Future) hasBorrow() bool { return HasBorrow(f.Type) } +func (f *Future) hasResource() bool { return HasResource(f.Type) } +func (f *Future) dependsOn(dep Node) bool { return dep == f || DependsOn(f.Type, dep) } diff --git a/wit/interface.go b/wit/interface.go new file mode 100644 index 00000000..af72386f --- /dev/null +++ b/wit/interface.go @@ -0,0 +1,95 @@ +package wit + +import ( + "go.bytecodealliance.org/wit/iterate" + "go.bytecodealliance.org/wit/ordered" +) + +// An Interface represents a [collection of types and functions], which are imported into +// or exported from a [WebAssembly component]. +// It implements the [Node], and [TypeOwner] interfaces. +// +// [collection of types and functions]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#wit-interfaces. +// [WebAssembly component]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#wit-worlds +type Interface struct { + _typeOwner + + Name *string + TypeDefs ordered.Map[string, *TypeDef] + Functions ordered.Map[string, *Function] + Package *Package // the Package this Interface belongs to + Stability Stability // WIT @since or @unstable (nil if unknown) + Docs Docs +} + +// WITPackage returns the [Package] this [Interface] belongs to. +func (i *Interface) WITPackage() *Package { + return i.Package +} + +// Match returns true if [Interface] i matches pattern, which can be one of: +// "name", "namespace:package/name" (qualified), or "namespace:package/name@1.0.0" (versioned). +func (i *Interface) Match(pattern string) bool { + if i.Name == nil { + return false + } + if pattern == *i.Name { + return true + } + id := i.Package.Name + id.Extension = *i.Name + if pattern == id.String() { + return true + } + id.Version = nil + return pattern == id.String() +} + +// AllFunctions returns a [sequence] that yields each [Function] in an [Interface]. +// The sequence stops if yield returns false. +// +// [sequence]: https://github.com/golang/go/issues/61897 +func (i *Interface) AllFunctions() iterate.Seq[*Function] { + return func(yield func(*Function) bool) { + i.Functions.All()(func(_ string, f *Function) bool { + return yield(f) + }) + } +} + +func (i *Interface) dependsOn(dep Node) bool { + if dep == i || dep == i.Package { + return true + } + // _, depIsInterface := dep.(*Interface) + var done bool + i.TypeDefs.All()(func(_ string, t *TypeDef) bool { + done = DependsOn(t, dep) + // A type alias transitively pulls in the dependencies of its owner + if root := t.Root(); !done && root != t && root.Owner != nil && root.Owner != i { + done = DependsOn(root.Owner, dep) + } + return !done + }) + if done { + return true + } + i.Functions.All()(func(_ string, f *Function) bool { + done = DependsOn(f, dep) + return !done + }) + return done +} + +// An InterfaceRef represents a reference to an [Interface] with a [Stability] attribute. +// It implements the [Node] and [WorldItem] interfaces. +type InterfaceRef struct { + _worldItem + + Interface *Interface + Stability Stability +} + +func (ref *InterfaceRef) dependsOn(dep Node) bool { + return dep == ref || DependsOn(ref.Interface, dep) +} diff --git a/wit/list.go b/wit/list.go new file mode 100644 index 00000000..b9cbdc74 --- /dev/null +++ b/wit/list.go @@ -0,0 +1,30 @@ +package wit + +// List represents a WIT [list type], which is an ordered vector of an arbitrary type. +// It implements the [Node], [ABI], and [TypeDefKind] interfaces. +// +// [list type]: https://component-model.bytecodealliance.org/design/wit.html#lists +type List struct { + _typeDefKind + Type Type +} + +// Size returns the [ABI byte size] for a [List]. +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +func (*List) Size() uintptr { return 8 } // [2]int32 + +// Align returns the [ABI byte alignment] a [List]. +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +func (*List) Align() uintptr { return 8 } // [2]int32 + +// Flat returns the [flattened] ABI representation of [List]. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (l *List) Flat() []Type { return []Type{PointerTo(l.Type), U32{}} } + +func (*List) hasPointer() bool { return true } +func (l *List) hasBorrow() bool { return HasBorrow(l.Type) } +func (l *List) hasResource() bool { return HasResource(l.Type) } +func (l *List) dependsOn(dep Node) bool { return dep == l || DependsOn(l.Type, dep) } diff --git a/wit/option.go b/wit/option.go new file mode 100644 index 00000000..0d5d2a28 --- /dev/null +++ b/wit/option.go @@ -0,0 +1,50 @@ +package wit + +// Option represents a WIT [option type], a special case of [Variant]. An Option can +// contain a value of a single type, either build-in or user defined, or no value. +// The equivalent in Go for an option could be represented as *string. +// It implements the [Node], [ABI], and [TypeDefKind] interfaces. +// +// [option type]: https://component-model.bytecodealliance.org/design/wit.html#options +type Option struct { + _typeDefKind + Type Type +} + +// Despecialize despecializes [Option] o into a [Variant] with two cases, "none" and "some". +// See the [canonical ABI documentation] for more information. +// +// [canonical ABI documentation]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization +func (o *Option) Despecialize() TypeDefKind { + return &Variant{ + Cases: []Case{ + {Name: "none"}, + {Name: "some", Type: o.Type}, + }, + } +} + +// Size returns the [ABI byte size] for [Option] o. +// It is first [despecialized] into a [Variant] with two cases, "none" and "some(T)", then sized. +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization +func (o *Option) Size() uintptr { + return o.Despecialize().Size() +} + +// Align returns the [ABI byte alignment] for [Option] o. +// It is first [despecialized] into a [Variant] with two cases, "none" and "some(T)", then aligned. +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization +func (o *Option) Align() uintptr { + return o.Despecialize().Align() +} + +// Flat returns the [flattened] ABI representation of [Option] o. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (o *Option) Flat() []Type { + return o.Despecialize().Flat() +} diff --git a/wit/ordered/list.go b/wit/ordered/list.go index 857366c6..e57afdda 100644 --- a/wit/ordered/list.go +++ b/wit/ordered/list.go @@ -1,6 +1,8 @@ package ordered -import "go.bytecodealliance.org/wit/iterate" +import ( + "go.bytecodealliance.org/wit/iterate" +) type list[K, V any] struct { root element[K, V] diff --git a/wit/ordered/map.go b/wit/ordered/map.go index 425d2450..c1aea627 100644 --- a/wit/ordered/map.go +++ b/wit/ordered/map.go @@ -14,6 +14,16 @@ type Map[K comparable, V any] struct { m map[K]*element[K, V] } +// Clone returns a shallow clone of m. +func (m *Map[K, V]) Clone() *Map[K, V] { + var c Map[K, V] + m.All()(func(k K, v V) bool { + c.Set(k, v) + return true + }) + return &c +} + // Get returns a value of type V if it exists in the map, otherwise the zero value. func (m *Map[K, V]) Get(k K) (v V) { if e, ok := m.m[k]; ok { diff --git a/wit/package.go b/wit/package.go new file mode 100644 index 00000000..6c088182 --- /dev/null +++ b/wit/package.go @@ -0,0 +1,65 @@ +package wit + +import ( + "strings" + + "go.bytecodealliance.org/wit/ordered" +) + +// Package represents a [WIT package] within a [Resolve]. +// It implements the [Node] interface. +// +// A Package is a collection of [Interface] and [World] values. Additionally, +// a Package contains a unique identifier that affects generated components and uniquely +// identifies this particular package. +// +// [WIT package]: https://component-model.bytecodealliance.org/design/wit.html#packages +type Package struct { + Name Ident + Interfaces ordered.Map[string, *Interface] + Worlds ordered.Map[string, *World] + Docs Docs +} + +// Clone returns a shallow clone of p. +func (p *Package) Clone() *Package { + c := *p + c.Interfaces = *p.Interfaces.Clone() + c.Worlds = *p.Worlds.Clone() + return &c +} + +func (p *Package) dependsOn(dep Node) bool { + if dep == p { + return true + } + var done bool + p.Interfaces.All()(func(_ string, i *Interface) bool { + done = DependsOn(i, dep) + return !done + }) + if done { + return true + } + p.Worlds.All()(func(_ string, w *World) bool { + done = DependsOn(w, dep) + return !done + }) + return done +} + +func comparePackages(a, b *Package) int { + // fmt.Fprintln(os.Stderr, "comparing "+b.Name.String()+" to "+a.Name.String()) + switch { + case a == b: + return 0 + case DependsOn(a, b): + // fmt.Fprintln(os.Stderr, a.Name.String()+" depends on "+b.Name.String()) + return 1 + case DependsOn(b, a): + // fmt.Fprintln(os.Stderr, b.Name.String()+" depends on "+a.Name.String()) + return -1 + } + // fmt.Fprintln(os.Stderr, a.Name.String()+" does not depend on "+b.Name.String()) + return -1 * strings.Compare(a.Name.String(), b.Name.String()) +} diff --git a/wit/pointer.go b/wit/pointer.go new file mode 100644 index 00000000..53828960 --- /dev/null +++ b/wit/pointer.go @@ -0,0 +1,33 @@ +package wit + +// PointerTo returns a [Pointer] to [Type] t. +func PointerTo(t Type) *TypeDef { + return &TypeDef{Kind: &Pointer{Type: t}} +} + +// Pointer represents a pointer to a WIT type. +// It is only used for ABI representation, e.g. pointers to function parameters or return values. +type Pointer struct { + _typeDefKind + Type Type +} + +// Size returns the [ABI byte size] for [Pointer]. +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +func (*Pointer) Size() uintptr { return 4 } + +// Align returns the [ABI byte alignment] for [Pointer]. +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +func (*Pointer) Align() uintptr { return 4 } + +// Flat returns the [flattened] ABI representation of [Pointer]. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (p *Pointer) Flat() []Type { return []Type{PointerTo(p.Type)} } + +func (*Pointer) hasPointer() bool { return true } +func (p *Pointer) hasBorrow() bool { return HasBorrow(p.Type) } +func (p *Pointer) hasResource() bool { return HasResource(p.Type) } +func (p *Pointer) dependsOn(dep Node) bool { return dep == p || DependsOn(p.Type, dep) } diff --git a/wit/record.go b/wit/record.go new file mode 100644 index 00000000..ebf9afcf --- /dev/null +++ b/wit/record.go @@ -0,0 +1,90 @@ +package wit + +// Record represents a WIT [record type], akin to a struct. +// It implements the [Node], [ABI], and [TypeDefKind] interfaces. +// +// [record type]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#item-record-bag-of-named-fields +type Record struct { + _typeDefKind + Fields []Field +} + +// Size returns the [ABI byte size] for [Record] r. +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +func (r *Record) Size() uintptr { + var s uintptr + for _, f := range r.Fields { + s = Align(s, f.Type.Align()) + s += f.Type.Size() + } + return s +} + +// Align returns the [ABI byte alignment] for [Record] r. +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +func (r *Record) Align() uintptr { + var a uintptr = 1 + for _, f := range r.Fields { + a = max(a, f.Type.Align()) + } + return a +} + +// Flat returns the [flattened] ABI representation of [Record] r. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (r *Record) Flat() []Type { + flat := make([]Type, 0, len(r.Fields)) + for _, f := range r.Fields { + flat = append(flat, f.Type.Flat()...) + } + return flat +} + +func (r *Record) hasPointer() bool { + for _, f := range r.Fields { + if HasPointer(f.Type) { + return true + } + } + return false +} + +func (r *Record) hasBorrow() bool { + for _, f := range r.Fields { + if HasBorrow(f.Type) { + return true + } + } + return false +} + +func (r *Record) hasResource() bool { + for _, f := range r.Fields { + if HasResource(f.Type) { + return true + } + } + return false +} + +func (r *Record) dependsOn(dep Node) bool { + if dep == r { + return true + } + for _, f := range r.Fields { + if DependsOn(f.Type, dep) { + return true + } + } + return false +} + +// Field represents a field in a [Record]. +type Field struct { + Name string + Type Type + Docs Docs +} diff --git a/wit/resolve.go b/wit/resolve.go index e3b83191..9b9a4477 100644 --- a/wit/resolve.go +++ b/wit/resolve.go @@ -1,15 +1,9 @@ package wit import ( - "fmt" "slices" - "strconv" - "strings" - "unsafe" - "github.com/coreos/go-semver/semver" "go.bytecodealliance.org/wit/iterate" - "go.bytecodealliance.org/wit/ordered" ) // Resolve represents a fully resolved set of WIT ([WebAssembly Interface Type]) @@ -29,6 +23,16 @@ type Resolve struct { Packages []*Package } +// Clone returns a shallow clone of r. +func (r *Resolve) Clone() *Resolve { + c := *r + c.Worlds = slices.Clone(r.Worlds) + c.Interfaces = slices.Clone(r.Interfaces) + c.TypeDefs = slices.Clone(r.TypeDefs) + c.Packages = slices.Clone(r.Packages) + return &c +} + // AllFunctions returns a [sequence] that yields each [Function] in a [Resolve]. // The sequence stops if yield returns false. // @@ -46,1625 +50,26 @@ func (r *Resolve) AllFunctions() iterate.Seq[*Function] { } } -// A World represents all of the imports and exports of a [WebAssembly component]. -// It implements the [Node] and [TypeOwner] interfaces. -// -// [WebAssembly component]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#wit-worlds -type World struct { - _typeOwner - - Name string - Imports ordered.Map[string, WorldItem] - Exports ordered.Map[string, WorldItem] - Package *Package // the Package this World belongs to (must be non-nil) - Stability Stability // WIT @since or @unstable (nil if unknown) - Docs Docs -} - -func (w *World) dependsOn(pkg *Package) bool { - if w.Package == pkg { - return true - } - var done bool - w.AllItems()(func(_ string, i WorldItem) bool { - done = DependsOn(i, pkg) - return !done - }) - return done -} - -// WITPackage returns the [Package] that [World] w belongs to. -func (w *World) WITPackage() *Package { - return w.Package -} - -// Match returns true if [World] w matches pattern, which can be one of: -// "name", "namespace:package/name" (qualified), or "namespace:package/name@1.0.0" (versioned). -func (w *World) Match(pattern string) bool { - if pattern == w.Name { - return true - } - id := w.Package.Name - id.Extension = w.Name - if pattern == id.String() { - return true - } - id.Version = nil - return pattern == id.String() -} - -// HasInterface returns true if [World] w references [Interface] i. -func (w *World) HasInterface(i *Interface) bool { - var found bool - w.AllInterfaces()(func(_ string, face *Interface) bool { - found = face == i - return !found - }) - return found -} - -// AllInterfaces returns a [sequence] that yields each [Interface] in a [World]. -// The sequence stops if yield returns false. -// -// [sequence]: https://github.com/golang/go/issues/61897 -func (w *World) AllInterfaces() iterate.Seq2[string, *Interface] { - return func(yield func(string, *Interface) bool) { - w.AllItems()(func(name string, i WorldItem) bool { - if ref, ok := i.(*InterfaceRef); ok { - return yield(name, ref.Interface) - } - return true - }) - } -} - -// AllTypeDefs returns a [sequence] that yields each [TypeDef] in a [World]. -// The sequence stops if yield returns false. -// -// [sequence]: https://github.com/golang/go/issues/61897 -func (w *World) AllTypeDefs() iterate.Seq2[string, *TypeDef] { - return func(yield func(string, *TypeDef) bool) { - w.AllItems()(func(name string, i WorldItem) bool { - if t, ok := i.(*TypeDef); ok { - return yield(name, t) - } - return true - }) - } -} - -// AllFunctions returns a [sequence] that yields each [Function] in a [World]. -// The sequence stops if yield returns false. -// -// [sequence]: https://github.com/golang/go/issues/61897 -func (w *World) AllFunctions() iterate.Seq[*Function] { - return func(yield func(*Function) bool) { - w.AllItems()(func(_ string, i WorldItem) bool { - if f, ok := i.(*Function); ok { - return yield(f) - } - return true - }) - } -} - -// AllItems returns a [sequence] that yields each [WorldItem] in a [World]. -// The sequence stops if yield returns false. -// -// [sequence]: https://github.com/golang/go/issues/61897 -func (w *World) AllItems() iterate.Seq2[string, WorldItem] { - return func(yield func(string, WorldItem) bool) { - var done bool - yield = iterate.Done2(iterate.Once2(yield), func() { done = true }) - f := func(name string, i WorldItem) bool { - return yield(name, i) - } - w.Imports.All()(f) - if done { - return - } - w.Exports.All()(f) - } -} - -// A WorldItem is any item that can be exported from or imported into a [World], -// currently either an [InterfaceRef], [TypeDef], or [Function]. -// Any WorldItem is also a [Node]. -type WorldItem interface { - Node - isWorldItem() -} - -// _worldItem is an embeddable type that conforms to the [WorldItem] interface. -type _worldItem struct{} - -func (_worldItem) isWorldItem() {} - -// An InterfaceRef represents a reference to an [Interface] with a [Stability] attribute. -// It implements the [Node] and [WorldItem] interfaces. -type InterfaceRef struct { - _worldItem - - Interface *Interface - Stability Stability -} - -func (ref *InterfaceRef) dependsOn(pkg *Package) bool { - return DependsOn(ref.Interface, pkg) -} - -// An Interface represents a [collection of types and functions], which are imported into -// or exported from a [WebAssembly component]. -// It implements the [Node], and [TypeOwner] interfaces. -// -// [collection of types and functions]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#wit-interfaces. -// [WebAssembly component]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#wit-worlds -type Interface struct { - _typeOwner - - Name *string - TypeDefs ordered.Map[string, *TypeDef] - Functions ordered.Map[string, *Function] - Package *Package // the Package this Interface belongs to - Stability Stability // WIT @since or @unstable (nil if unknown) - Docs Docs -} - -func (i *Interface) dependsOn(pkg *Package) bool { - if i.Package == pkg { - return true - } - var done bool - i.TypeDefs.All()(func(_ string, t *TypeDef) bool { - done = DependsOn(t, pkg) - return !done - }) - if done { - return true - } - i.Functions.All()(func(_ string, f *Function) bool { - done = DependsOn(f, pkg) - return !done - }) - return done -} - -// WITPackage returns the [Package] this [Interface] belongs to. -func (i *Interface) WITPackage() *Package { - return i.Package -} - -// AllFunctions returns a [sequence] that yields each [Function] in an [Interface]. -// The sequence stops if yield returns false. -// -// [sequence]: https://github.com/golang/go/issues/61897 -func (i *Interface) AllFunctions() iterate.Seq[*Function] { - return func(yield func(*Function) bool) { - i.Functions.All()(func(_ string, f *Function) bool { - return yield(f) - }) - } -} - -// TypeDef represents a WIT type definition. A TypeDef may be named or anonymous, -// and optionally belong to a [World] or [Interface]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -type TypeDef struct { - _type - _worldItem - Name *string - Kind TypeDefKind - Owner TypeOwner - Stability Stability // WIT @since or @unstable (nil if unknown) - Docs Docs -} - -// TypeName returns the [WIT] type name for t. -// Returns an empty string if t is anonymous. -// -// [WIT]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md -func (t *TypeDef) TypeName() string { - if t.Name != nil { - return *t.Name - } - return "" -} - -// TypeDef returns the parent [TypeDef] of [TypeDef] t. -// If t is not a [type alias], TypeDef returns t. -// -// [type alias]: https://component-model.bytecodealliance.org/design/wit.html#type-aliases -func (t *TypeDef) TypeDef() *TypeDef { - if t, ok := t.Kind.(*TypeDef); ok { - return t - } - return t -} - -// Root returns the root [TypeDef] of [TypeDef] t. -// If t is not a [type alias], Root returns t. -// -// [type alias]: https://component-model.bytecodealliance.org/design/wit.html#type-aliases -func (t *TypeDef) Root() *TypeDef { - for { - switch kind := t.Kind.(type) { - case *TypeDef: - t = kind - default: - return t - } - } -} - -// Constructor returns the constructor for [TypeDef] t, or nil if none. -// Currently t must be a [Resource] to have a constructor. -func (t *TypeDef) Constructor() *Function { - var constructor *Function - t.Owner.AllFunctions()(func(f *Function) bool { - if c, ok := f.Kind.(*Constructor); ok && c.Type == t { - constructor = f - return false - } - return true - }) - return constructor -} - -// StaticFunctions returns all static functions for [TypeDef] t. -// Currently t must be a [Resource] to have static functions. -func (t *TypeDef) StaticFunctions() []*Function { - var statics []*Function - t.Owner.AllFunctions()(func(f *Function) bool { - if s, ok := f.Kind.(*Static); ok && s.Type == t { - statics = append(statics, f) - } - return true - }) - slices.SortFunc(statics, compareFunctions) - return statics -} - -// Methods returns all methods for [TypeDef] t. -// Currently t must be a [Resource] to have methods. -func (t *TypeDef) Methods() []*Function { - var methods []*Function - t.Owner.AllFunctions()(func(f *Function) bool { - if m, ok := f.Kind.(*Method); ok && m.Type == t { - methods = append(methods, f) - } - return true - }) - slices.SortFunc(methods, compareFunctions) - return methods -} - -func compareFunctions(a, b *Function) int { - return strings.Compare(a.Name, b.Name) -} - -// Size returns the byte size for values of type t. -func (t *TypeDef) Size() uintptr { - return t.Kind.Size() -} - -// Align returns the byte alignment for values of type t. -func (t *TypeDef) Align() uintptr { - return t.Kind.Align() -} - -// Flat returns the [flattened] ABI representation of t. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (t *TypeDef) Flat() []Type { - return t.Kind.Flat() -} - -func (t *TypeDef) dependsOn(pkg *Package) bool { - if t.Owner != nil && t.Owner.WITPackage() == pkg { - return true - } - return DependsOn(t.Kind, pkg) -} - -func (t *TypeDef) hasPointer() bool { return HasPointer(t.Kind) } -func (t *TypeDef) hasBorrow() bool { return HasBorrow(t.Kind) } -func (t *TypeDef) hasResource() bool { return HasResource(t.Kind) } - -// TypeDefKind represents the underlying type in a [TypeDef], which can be one of -// [Record], [Resource], [Handle], [Flags], [Tuple], [Variant], [Enum], -// [Option], [Result], [List], [Future], [Stream], or [Type]. -// It implements the [Node] and [ABI] interfaces. -type TypeDefKind interface { - Node - ABI - isTypeDefKind() -} - -// _typeDefKind is an embeddable type that conforms to the [TypeDefKind] interface. -type _typeDefKind struct{} - -func (_typeDefKind) isTypeDefKind() {} - -// KindOf probes [Type] t to determine if it is a [TypeDef] with [TypeDefKind] K. -// It returns the underlying Kind if present. -func KindOf[K TypeDefKind](t Type) (kind K) { - if td, ok := t.(*TypeDef); ok { - if kind, ok = td.Kind.(K); ok { - return kind - } - } - var zero K - return zero -} - -// PointerTo returns a [Pointer] to [Type] t. -func PointerTo(t Type) *TypeDef { - return &TypeDef{Kind: &Pointer{Type: t}} -} - -// Pointer represents a pointer to a WIT type. -// It is only used for ABI representation, e.g. pointers to function parameters or return values. -type Pointer struct { - _typeDefKind - Type Type -} - -// Size returns the [ABI byte size] for [Pointer]. -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -func (*Pointer) Size() uintptr { return 4 } - -// Align returns the [ABI byte alignment] for [Pointer]. -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -func (*Pointer) Align() uintptr { return 4 } - -// Flat returns the [flattened] ABI representation of [Pointer]. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (p *Pointer) Flat() []Type { return []Type{PointerTo(p.Type)} } - -func (p *Pointer) dependsOn(pkg *Package) bool { return DependsOn(p.Type, pkg) } -func (*Pointer) hasPointer() bool { return true } -func (p *Pointer) hasBorrow() bool { return HasBorrow(p.Type) } -func (p *Pointer) hasResource() bool { return HasResource(p.Type) } - -// Record represents a WIT [record type], akin to a struct. -// It implements the [Node], [ABI], and [TypeDefKind] interfaces. -// -// [record type]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#item-record-bag-of-named-fields -type Record struct { - _typeDefKind - Fields []Field -} - -// Size returns the [ABI byte size] for [Record] r. -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -func (r *Record) Size() uintptr { - var s uintptr - for _, f := range r.Fields { - s = Align(s, f.Type.Align()) - s += f.Type.Size() - } - return s -} - -// Align returns the [ABI byte alignment] for [Record] r. -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -func (r *Record) Align() uintptr { - var a uintptr = 1 - for _, f := range r.Fields { - a = max(a, f.Type.Align()) - } - return a -} - -// Flat returns the [flattened] ABI representation of [Record] r. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (r *Record) Flat() []Type { - flat := make([]Type, 0, len(r.Fields)) - for _, f := range r.Fields { - flat = append(flat, f.Type.Flat()...) - } - return flat -} - -func (r *Record) dependsOn(pkg *Package) bool { - for _, f := range r.Fields { - if DependsOn(f.Type, pkg) { +func (r *Resolve) dependsOn(dep Node) bool { + for _, w := range r.Worlds { + if DependsOn(w, dep) { return true } } - return false -} - -func (r *Record) hasPointer() bool { - for _, f := range r.Fields { - if HasPointer(f.Type) { + for _, i := range r.Interfaces { + if DependsOn(i, dep) { return true } } - return false -} - -func (r *Record) hasBorrow() bool { - for _, f := range r.Fields { - if HasBorrow(f.Type) { + for _, t := range r.TypeDefs { + if DependsOn(t, dep) { return true } } - return false -} - -func (r *Record) hasResource() bool { - for _, f := range r.Fields { - if HasResource(f.Type) { + for _, p := range r.Packages { + if DependsOn(p, dep) { return true } } return false } - -// Field represents a field in a [Record]. -type Field struct { - Name string - Type Type - Docs Docs -} - -// Resource represents a WIT [resource type]. -// It implements the [Node], [ABI], and [TypeDefKind] interfaces. -// -// [resource type]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#item-resource -type Resource struct{ _typeDefKind } - -// Size returns the [ABI byte size] for [Resource]. -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -func (*Resource) Size() uintptr { return 4 } - -// Align returns the [ABI byte alignment] for [Resource]. -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -func (*Resource) Align() uintptr { return 4 } - -// Flat returns the [flattened] ABI representation of [Resource]. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (*Resource) Flat() []Type { return []Type{U32{}} } - -// hasResource always returns true. -func (*Resource) hasResource() bool { return true } - -// Handle represents a WIT [handle type]. -// It conforms to the [Node], [ABI], and [TypeDefKind] interfaces. -// Handles represent the passing of unique ownership of a resource between -// two components. When the owner of an owned handle drops that handle, -// the resource is destroyed. In contrast, a borrowed handle represents -// a temporary loan of a handle from the caller to the callee for the -// duration of the call. -// -// [handle type]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#handles -type Handle interface { - TypeDefKind - isHandle() -} - -// _handle is an embeddable type that conforms to the [Handle] interface. -type _handle struct{ _typeDefKind } - -func (_handle) isHandle() {} - -// Size returns the [ABI byte size] for this [Handle]. -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -func (_handle) Size() uintptr { return 4 } - -// Align returns the [ABI byte alignment] for this [Handle]. -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -func (_handle) Align() uintptr { return 4 } - -// Flat returns the [flattened] ABI representation of this type. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (_handle) Flat() []Type { return []Type{U32{}} } - -// Own represents an WIT [owned handle]. -// It implements the [Handle], [Node], [ABI], and [TypeDefKind] interfaces. -// -// [owned handle]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#handles -type Own struct { - _handle - Type *TypeDef -} - -func (o *Own) dependsOn(pkg *Package) bool { return DependsOn(o.Type, pkg) } -func (o *Own) hasResource() bool { return HasResource(o.Type) } - -// Borrow represents a WIT [borrowed handle]. -// It implements the [Handle], [Node], [ABI], and [TypeDefKind] interfaces. -// -// [borrowed handle]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#handles -type Borrow struct { - _handle - Type *TypeDef -} - -func (b *Borrow) dependsOn(pkg *Package) bool { return DependsOn(b.Type, pkg) } -func (b *Borrow) hasBorrow() bool { return true } -func (b *Borrow) hasResource() bool { return HasResource(b.Type) } - -// Flags represents a WIT [flags type], stored as a bitfield. -// It implements the [Node], [ABI], and [TypeDefKind] interfaces. -// -// [flags type]: https://component-model.bytecodealliance.org/design/wit.html#flags -type Flags struct { - _typeDefKind - Flags []Flag -} - -// Size returns the [ABI byte size] of [Flags] f. -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -func (f *Flags) Size() uintptr { - n := len(f.Flags) - switch { - case n <= 8: - return 1 - case n <= 16: - return 2 - } - return 4 * uintptr((n+31)>>5) -} - -// Align returns the [ABI byte alignment] of [Flags] f. -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -func (f *Flags) Align() uintptr { - n := len(f.Flags) - switch { - case n <= 8: - return 1 - case n <= 16: - return 2 - } - return 4 -} - -// Flat returns the [flattened] ABI representation of [Flags] f. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (f *Flags) Flat() []Type { - flat := make([]Type, (len(f.Flags)+31)>>5) - for i := range flat { - flat[i] = U32{} - } - return flat -} - -// Flag represents a single flag value in a [Flags] type. -// It implements the [Node] interface. -type Flag struct { - Name string - Docs Docs -} - -// Tuple represents a WIT [tuple type]. -// A tuple type is an ordered fixed length sequence of values of specified types. -// It is similar to a [Record], except that the fields are identified by their order instead of by names. -// It implements the [Node], [ABI], and [TypeDefKind] interfaces. -// -// [tuple type]: https://component-model.bytecodealliance.org/design/wit.html#tuples -type Tuple struct { - _typeDefKind - Types []Type -} - -// Type returns a non-nil [Type] if all types in t -// are the same. Returns nil if t contains more than one type. -func (t *Tuple) Type() Type { - if len(t.Types) == 0 { - return nil - } - typ := t.Types[0] - for i := 0; i < len(t.Types); i++ { - if t.Types[i] != typ { - return nil - } - } - return typ -} - -// Despecialize despecializes [Tuple] e into a [Record] with 0-based integer field names. -// See the [canonical ABI documentation] for more information. -// -// [canonical ABI documentation]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization -func (t *Tuple) Despecialize() TypeDefKind { - r := &Record{ - Fields: make([]Field, len(t.Types)), - } - for i := range t.Types { - r.Fields[i].Name = strconv.Itoa(i) - r.Fields[i].Type = t.Types[i] - } - return r -} - -// Size returns the [ABI byte size] for [Tuple] t. -// It is first [despecialized] into a [Record] with 0-based integer field names, then sized. -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization -func (t *Tuple) Size() uintptr { - return t.Despecialize().Size() -} - -// Align returns the [ABI byte alignment] for [Tuple] t. -// It is first [despecialized] into a [Record] with 0-based integer field names, then aligned. -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization -func (t *Tuple) Align() uintptr { - return t.Despecialize().Align() -} - -// Flat returns the [flattened] ABI representation of [Tuple] t. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (t *Tuple) Flat() []Type { - return t.Despecialize().Flat() -} - -// Variant represents a WIT [variant type], a tagged/discriminated union. -// A variant type declares one or more cases. Each case has a name and, optionally, -// a type of data associated with that case. -// It implements the [Node], [ABI], and [TypeDefKind] interfaces. -// -// [variant type]: https://component-model.bytecodealliance.org/design/wit.html#variants -type Variant struct { - _typeDefKind - Cases []Case -} - -// Enum attempts to represent [Variant] v as an [Enum]. -// This will only succeed if v has no associated types. If v has -// associated types, then it will return nil. -func (v *Variant) Enum() *Enum { - types := v.Types() - if len(types) > 0 { - return nil - } - e := &Enum{ - Cases: make([]EnumCase, len(v.Cases)), - } - for i := range v.Cases { - e.Cases[i].Name = v.Cases[i].Name - e.Cases[i].Docs = v.Cases[i].Docs - } - return e -} - -// Types returns the unique associated types in [Variant] v. -func (v *Variant) Types() []Type { - var types []Type - typeMap := make(map[Type]bool) - for i := range v.Cases { - t := v.Cases[i].Type - if t == nil || typeMap[t] { - continue - } - types = append(types, t) - typeMap[t] = true - } - return types -} - -// Size returns the [ABI byte size] for [Variant] v. -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -func (v *Variant) Size() uintptr { - s := Discriminant(len(v.Cases)).Size() - s = Align(s, v.maxCaseAlign()) - s += v.maxCaseSize() - return Align(s, v.Align()) -} - -// Align returns the [ABI byte alignment] for [Variant] v. -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -func (v *Variant) Align() uintptr { - return max(Discriminant(len(v.Cases)).Align(), v.maxCaseAlign()) -} - -// Flat returns the [flattened] ABI representation of [Variant] v. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (v *Variant) Flat() []Type { - var flat []Type - for _, t := range v.Types() { - for i, f := range t.Flat() { - if i >= len(flat) { - flat = append(flat, f) - } else { - flat[i] = flatJoin(flat[i], f) - } - } - } - return append(Discriminant(len(v.Cases)).Flat(), flat...) -} - -func flatJoin(a, b Type) Type { - if a == b { - return a - } - if a.Size() == 4 && b.Size() == 4 { - return U32{} - } - return U64{} -} - -func (v *Variant) maxCaseSize() uintptr { - var s uintptr - for _, c := range v.Cases { - if c.Type != nil { - s = max(s, c.Type.Size()) - } - } - return s -} - -func (v *Variant) maxCaseAlign() uintptr { - var a uintptr = 1 - for _, c := range v.Cases { - if c.Type != nil { - a = max(a, c.Type.Align()) - } - } - return a -} - -func (v *Variant) dependsOn(pkg *Package) bool { - for _, t := range v.Types() { - if DependsOn(t, pkg) { - return true - } - } - return false -} - -func (v *Variant) hasPointer() bool { - for _, t := range v.Types() { - if HasPointer(t) { - return true - } - } - return false -} - -func (v *Variant) hasBorrow() bool { - for _, t := range v.Types() { - if HasBorrow(t) { - return true - } - } - return false -} - -func (v *Variant) hasResource() bool { - for _, t := range v.Types() { - if HasResource(t) { - return true - } - } - return false -} - -// Case represents a single case in a [Variant]. -// It implements the [Node] interface. -type Case struct { - Name string - Type Type // optional associated [Type] (can be nil) - Docs Docs -} - -func (c *Case) dependsOn(pkg *Package) bool { - return DependsOn(c.Type, pkg) -} - -// Enum represents a WIT [enum type], which is a [Variant] without associated data. -// The equivalent in Go is a set of const identifiers declared with iota. -// It implements the [Node], [ABI], and [TypeDefKind] interfaces. -// -// [enum type]: https://component-model.bytecodealliance.org/design/wit.html#enums -type Enum struct { - _typeDefKind - Cases []EnumCase -} - -// Despecialize despecializes [Enum] e into a [Variant] with no associated types. -// See the [canonical ABI documentation] for more information. -// -// [canonical ABI documentation]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization -func (e *Enum) Despecialize() TypeDefKind { - v := &Variant{ - Cases: make([]Case, len(e.Cases)), - } - for i := range e.Cases { - v.Cases[i].Name = e.Cases[i].Name - v.Cases[i].Docs = e.Cases[i].Docs - } - return v -} - -// Size returns the [ABI byte size] for [Enum] e, the smallest integer -// type that can represent 0...len(e.Cases). -// It is first [despecialized] into a [Variant] with no associated types, then sized. -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization -func (e *Enum) Size() uintptr { - return e.Despecialize().Size() -} - -// Align returns the [ABI byte alignment] for [Enum] e. -// It is first [despecialized] into a [Variant] with no associated types, then aligned. -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization -func (e *Enum) Align() uintptr { - return e.Despecialize().Align() -} - -// Flat returns the [flattened] ABI representation of [Enum] e. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (v *Enum) Flat() []Type { - return Discriminant(len(v.Cases)).Flat() -} - -// EnumCase represents a single case in an [Enum]. -// It implements the [Node] interface. -type EnumCase struct { - Name string - Docs Docs -} - -// Option represents a WIT [option type], a special case of [Variant]. An Option can -// contain a value of a single type, either build-in or user defined, or no value. -// The equivalent in Go for an option could be represented as *string. -// It implements the [Node], [ABI], and [TypeDefKind] interfaces. -// -// [option type]: https://component-model.bytecodealliance.org/design/wit.html#options -type Option struct { - _typeDefKind - Type Type -} - -// Despecialize despecializes [Option] o into a [Variant] with two cases, "none" and "some". -// See the [canonical ABI documentation] for more information. -// -// [canonical ABI documentation]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization -func (o *Option) Despecialize() TypeDefKind { - return &Variant{ - Cases: []Case{ - {Name: "none"}, - {Name: "some", Type: o.Type}, - }, - } -} - -// Size returns the [ABI byte size] for [Option] o. -// It is first [despecialized] into a [Variant] with two cases, "none" and "some(T)", then sized. -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization -func (o *Option) Size() uintptr { - return o.Despecialize().Size() -} - -// Align returns the [ABI byte alignment] for [Option] o. -// It is first [despecialized] into a [Variant] with two cases, "none" and "some(T)", then aligned. -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization -func (o *Option) Align() uintptr { - return o.Despecialize().Align() -} - -// Flat returns the [flattened] ABI representation of [Option] o. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (o *Option) Flat() []Type { - return o.Despecialize().Flat() -} - -// Result represents a WIT [result type], which is the result of a function call, -// returning an optional value and/or an optional error. It is roughly equivalent to -// the Go pattern of returning (T, error). -// It implements the [Node], [ABI], and [TypeDefKind] interfaces. -// -// [result type]: https://component-model.bytecodealliance.org/design/wit.html#results -type Result struct { - _typeDefKind - OK Type // optional associated [Type] (can be nil) - Err Type // optional associated [Type] (can be nil) -} - -// Despecialize despecializes [Result] o into a [Variant] with two cases, "ok" and "error". -// See the [canonical ABI documentation] for more information. -// -// [canonical ABI documentation]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization -func (r *Result) Despecialize() TypeDefKind { - return &Variant{ - Cases: []Case{ - {Name: "ok", Type: r.OK}, - {Name: "error", Type: r.Err}, - }, - } -} - -// Types returns the unique associated types in [Result] r. -func (r *Result) Types() []Type { - var types []Type - if r.OK != nil { - types = append(types, r.OK) - } - if r.Err != nil && r.Err != r.OK { - types = append(types, r.Err) - } - return types -} - -// Size returns the [ABI byte size] for [Result] r. -// It is first [despecialized] into a [Variant] with two cases "ok" and "error", then sized. -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization -func (r *Result) Size() uintptr { - return r.Despecialize().Size() -} - -// Align returns the [ABI byte alignment] for [Result] r. -// It is first [despecialized] into a [Variant] with two cases "ok" and "error", then aligned. -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization -func (r *Result) Align() uintptr { - return r.Despecialize().Align() -} - -// Flat returns the [flattened] ABI representation of [Result] r. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (r *Result) Flat() []Type { - return r.Despecialize().Flat() -} - -// List represents a WIT [list type], which is an ordered vector of an arbitrary type. -// It implements the [Node], [ABI], and [TypeDefKind] interfaces. -// -// [list type]: https://component-model.bytecodealliance.org/design/wit.html#lists -type List struct { - _typeDefKind - Type Type -} - -// Size returns the [ABI byte size] for a [List]. -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -func (*List) Size() uintptr { return 8 } // [2]int32 - -// Align returns the [ABI byte alignment] a [List]. -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -func (*List) Align() uintptr { return 8 } // [2]int32 - -// Flat returns the [flattened] ABI representation of [List]. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (l *List) Flat() []Type { return []Type{PointerTo(l.Type), U32{}} } - -func (l *List) dependsOn(p *Package) bool { return DependsOn(l.Type, p) } -func (*List) hasPointer() bool { return true } -func (l *List) hasBorrow() bool { return HasBorrow(l.Type) } -func (l *List) hasResource() bool { return HasResource(l.Type) } - -// Future represents a WIT [future type], expected to be part of [WASI Preview 3]. -// It implements the [Node], [ABI], and [TypeDefKind] interfaces. -// -// [future type]: https://github.com/bytecodealliance/wit-bindgen/issues/270 -// [WASI Preview 3]: https://bytecodealliance.org/articles/webassembly-the-updated-roadmap-for-developers -type Future struct { - _typeDefKind - Type Type // optional associated Type (can be nil) -} - -// Size returns the [ABI byte size] for a [Future]. -// TODO: what is the ABI size of a future? -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -func (*Future) Size() uintptr { return 0 } - -// Align returns the [ABI byte alignment] a [Future]. -// TODO: what is the ABI alignment of a future? -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -func (*Future) Align() uintptr { return 0 } - -// Flat returns the [flattened] ABI representation of [Future]. -// TODO: what is the ABI representation of a stream? -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (*Future) Flat() []Type { return nil } - -func (f *Future) dependsOn(p *Package) bool { return DependsOn(f.Type, p) } -func (f *Future) hasPointer() bool { return HasPointer(f.Type) } -func (f *Future) hasBorrow() bool { return HasBorrow(f.Type) } -func (f *Future) hasResource() bool { return HasResource(f.Type) } - -// Stream represents a WIT [stream type], expected to be part of [WASI Preview 3]. -// It implements the [Node], [ABI], and [TypeDefKind] interfaces. -// -// [stream type]: https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#streams -// [WASI Preview 3]: https://bytecodealliance.org/articles/webassembly-the-updated-roadmap-for-developers -type Stream struct { - _typeDefKind - Element Type // optional associated Type (can be nil) - End Type // optional associated Type (can be nil) -} - -// Size returns the [ABI byte size] for a [Stream]. -// TODO: what is the ABI size of a stream? -// -// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size -func (*Stream) Size() uintptr { return 0 } - -// Align returns the [ABI byte alignment] a [Stream]. -// TODO: what is the ABI alignment of a stream? -// -// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment -func (*Stream) Align() uintptr { return 0 } - -// Flat returns the [flattened] ABI representation of [Stream]. -// TODO: what is the ABI representation of a stream? -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (*Stream) Flat() []Type { return nil } - -func (s *Stream) dependsOn(p *Package) bool { return DependsOn(s.Element, p) || DependsOn(s.End, p) } -func (s *Stream) hasPointer() bool { return HasPointer(s.Element) || HasPointer(s.End) } -func (s *Stream) hasBorrow() bool { return HasBorrow(s.Element) || HasBorrow(s.End) } -func (s *Stream) hasResource() bool { return HasResource(s.Element) || HasResource(s.End) } - -// TypeOwner is the interface implemented by any type that can own a TypeDef, -// currently [World] and [Interface]. -type TypeOwner interface { - Node - AllFunctions() iterate.Seq[*Function] - WITPackage() *Package - isTypeOwner() -} - -type _typeOwner struct{} - -func (_typeOwner) isTypeOwner() {} - -// Type is the interface implemented by any type definition. This can be a -// [primitive type] or a user-defined type in a [TypeDef]. -// It also conforms to the [Node], [ABI], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -type Type interface { - TypeDefKind - TypeName() string - isType() -} - -// _type is an embeddable struct that conforms to the [Type] interface. -// It also implements the [Node], [ABI], and [TypeDefKind] interfaces. -type _type struct{ _typeDefKind } - -func (_type) TypeName() string { return "" } -func (_type) isType() {} - -// ParseType parses a WIT [primitive type] string into -// the associated Type implementation from this package. -// It returns an error if the type string is not recognized. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -func ParseType(s string) (Type, error) { - switch s { - case "bool": - return Bool{}, nil - case "s8": - return S8{}, nil - case "u8": - return U8{}, nil - case "s16": - return S16{}, nil - case "u16": - return U16{}, nil - case "s32": - return S32{}, nil - case "u32": - return U32{}, nil - case "s64": - return S64{}, nil - case "u64": - return U64{}, nil - case "f32", "float32": // TODO: remove float32 at some point - return F32{}, nil - case "f64", "float64": // TODO: remove float64 at some point - return F64{}, nil - case "char": - return Char{}, nil - case "string": - return String{}, nil - } - return nil, fmt.Errorf("unknown primitive type %q", s) -} - -// primitive is a type constraint of the Go equivalents of WIT [primitive types]. -// -// [primitive types]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -type primitive interface { - bool | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64 | float32 | float64 | char | string -} - -// char is defined because [rune] is an alias of [int32] -type char rune - -// Primitive is the interface implemented by WIT [primitive types]. -// It also conforms to the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive types]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -type Primitive interface { - Type - isPrimitive() -} - -// _primitive is an embeddable struct that conforms to the [PrimitiveType] interface. -// It represents a WebAssembly Component Model [primitive type] mapped to its equivalent Go type. -// It also conforms to the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -type _primitive[T primitive] struct{ _type } - -// isPrimitive conforms to the [Primitive] interface. -func (_primitive[T]) isPrimitive() {} - -// Size returns the byte size for values of this type. -func (_primitive[T]) Size() uintptr { - var v T - switch any(v).(type) { - case string: - return 8 // [2]int32 - default: - return unsafe.Sizeof(v) - } -} - -// Align returns the byte alignment for values of this type. -func (_primitive[T]) Align() uintptr { - var v T - switch any(v).(type) { - case string: - return 4 // int32 - default: - return unsafe.Alignof(v) - } -} - -// hasPointer returns whether the ABI representation of this type contains a pointer. -// This will only return true for [String]. -func (_primitive[T]) hasPointer() bool { - var v T - switch any(v).(type) { - case string: - return true - default: - return false - } -} - -// Flat returns the [flattened] ABI representation of this type. -// -// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening -func (_primitive[T]) Flat() []Type { - var v T - switch any(v).(type) { - case bool, int8, uint8, int16, uint16, int32, uint32, char: - return []Type{U32{}} - case int64, uint64: - return []Type{U64{}} - case float32: - return []Type{F32{}} - case float64: - return []Type{F64{}} - case string: - return []Type{PointerTo(U8{}), U32{}} - default: - panic(fmt.Sprintf("BUG: unknown primitive type %T", v)) // should never reach here - } -} - -// Bool represents the WIT [primitive type] bool, a boolean value either true or false. -// It is equivalent to the Go type [bool]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [bool]: https://pkg.go.dev/builtin#bool -type Bool struct{ _primitive[bool] } - -// S8 represents the WIT [primitive type] s8, a signed 8-bit integer. -// It is equivalent to the Go type [int8]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [int8]: https://pkg.go.dev/builtin#int8 -type S8 struct{ _primitive[int8] } - -// U8 represents the WIT [primitive type] u8, an unsigned 8-bit integer. -// It is equivalent to the Go type [uint8]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [uint8]: https://pkg.go.dev/builtin#uint8 -type U8 struct{ _primitive[uint8] } - -// S16 represents the WIT [primitive type] s16, a signed 16-bit integer. -// It is equivalent to the Go type [int16]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [int16]: https://pkg.go.dev/builtin#int16 -type S16 struct{ _primitive[int16] } - -// U16 represents the WIT [primitive type] u16, an unsigned 16-bit integer. -// It is equivalent to the Go type [uint16]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [uint16]: https://pkg.go.dev/builtin#uint16 -type U16 struct{ _primitive[uint16] } - -// S32 represents the WIT [primitive type] s32, a signed 32-bit integer. -// It is equivalent to the Go type [int32]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [int32]: https://pkg.go.dev/builtin#int32 -type S32 struct{ _primitive[int32] } - -// U32 represents the WIT [primitive type] u32, an unsigned 32-bit integer. -// It is equivalent to the Go type [uint32]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [uint32]: https://pkg.go.dev/builtin#uint32 -type U32 struct{ _primitive[uint32] } - -// S64 represents the WIT [primitive type] s64, a signed 64-bit integer. -// It is equivalent to the Go type [int64]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [int64]: https://pkg.go.dev/builtin#int64 -type S64 struct{ _primitive[int64] } - -// U64 represents the WIT [primitive type] u64, an unsigned 64-bit integer. -// It is equivalent to the Go type [uint64]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [uint64]: https://pkg.go.dev/builtin#uint64 -type U64 struct{ _primitive[uint64] } - -// F32 represents the WIT [primitive type] f32, a 32-bit floating point value. -// It is equivalent to the Go type [float32]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [float32]: https://pkg.go.dev/builtin#float32 -type F32 struct{ _primitive[float32] } - -// F64 represents the WIT [primitive type] f64, a 64-bit floating point value. -// It is equivalent to the Go type [float64]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [float64]: https://pkg.go.dev/builtin#float64 -type F64 struct{ _primitive[float64] } - -// Char represents the WIT [primitive type] char, a single Unicode character, -// specifically a [Unicode scalar value]. It is equivalent to the Go type [rune]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [Unicode scalar value]: https://unicode.org/glossary/#unicode_scalar_value -// [rune]: https://pkg.go.dev/builtin#rune -type Char struct{ _primitive[char] } - -// String represents the WIT [primitive type] string, a finite string of Unicode characters. -// It is equivalent to the Go type [string]. -// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. -// -// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types -// [string]: https://pkg.go.dev/builtin#string -type String struct{ _primitive[string] } - -// Function represents a WIT [function]. -// Functions can be freestanding, methods, constructors or static. -// It implements the [Node] and [WorldItem] interfaces. -// -// [function]: https://component-model.bytecodealliance.org/design/wit.html#functions -type Function struct { - _worldItem - Name string - Kind FunctionKind - Params []Param // arguments to the function - Results []Param // a function can have a single anonymous result, or > 1 named results - Stability Stability // WIT @since or @unstable (nil if unknown) - Docs Docs -} - -func (f *Function) dependsOn(pkg *Package) bool { - for _, p := range f.Params { - if DependsOn(p.Type, pkg) { - return true - } - } - for _, r := range f.Results { - if DependsOn(r.Type, pkg) { - return true - } - } - return false -} - -// BaseName returns the base name of [Function] f. -// For static functions, this returns the function name unchanged. -// For constructors, this removes the [constructor] and type prefix. -// For static functions, this removes the [static] and type prefix. -// For methods, this removes the [method] and type prefix. -// For special functions like [resource-drop], it will return a well-known value. -func (f *Function) BaseName() string { - switch { - case strings.HasPrefix(f.Name, "[constructor]"): - return "constructor" - case strings.HasPrefix(f.Name, "[resource-new]"): - return "resource-new" - case strings.HasPrefix(f.Name, "[resource-rep]"): - return "resource-rep" - case strings.HasPrefix(f.Name, "[resource-drop]"): - return "resource-drop" - case strings.HasPrefix(f.Name, "[dtor]"): - return "destructor" - } - name, after, found := strings.Cut(f.Name, ".") - if found { - name = after - } - after, found = strings.CutPrefix(f.Name, "cabi_post_") - if found { - name = after + "-post-return" - } - return name -} - -// Type returns the associated (self) [Type] for [Function] f, if f is a constructor, method, or static function. -// If f is a freestanding function, this returns nil. -func (f *Function) Type() Type { - switch kind := f.Kind.(type) { - case *Constructor: - return kind.Type - case *Static: - return kind.Type - case *Method: - return kind.Type - default: - return nil - } -} - -// IsAdmin returns true if [Function] f is an administrative function in the Canonical ABI. -func (f *Function) IsAdmin() bool { - switch { - // Imported - case f.IsStatic() && strings.HasPrefix(f.Name, "[resource-new]"): - return true - case f.IsMethod() && strings.HasPrefix(f.Name, "[resource-rep]"): - return true - case f.IsMethod() && strings.HasPrefix(f.Name, "[resource-drop]"): - return true - - // Exported - case f.IsMethod() && strings.HasPrefix(f.Name, "[dtor]"): - return true - case strings.HasPrefix(f.Name, "cabi_post_"): - return true - } - return false -} - -// IsFreestanding returns true if [Function] f is a freestanding function, -// and not a constructor, method, or static function. -func (f *Function) IsFreestanding() bool { - _, ok := f.Kind.(*Freestanding) - return ok -} - -// IsConstructor returns true if [Function] f is a constructor. -// To qualify, it must have a *[Constructor] Kind with a non-nil type. -func (f *Function) IsConstructor() bool { - kind, ok := f.Kind.(*Constructor) - return ok && kind.Type != nil -} - -// IsMethod returns true if [Function] f is a method. -// To qualify, it must have a *[Method] Kind with a non-nil [Type] which matches borrow of its first param. -func (f *Function) IsMethod() bool { - if len(f.Params) == 0 { - return false - } - kind, ok := f.Kind.(*Method) - if !ok { - return false - } - t := f.Params[0].Type - h := KindOf[*Borrow](t) - return t == kind.Type || (h != nil && h.Type == kind.Type) -} - -// IsStatic returns true if [Function] f is a static function. -// To qualify, it must have a *[Static] Kind with a non-nil type. -func (f *Function) IsStatic() bool { - kind, ok := f.Kind.(*Static) - return ok && kind.Type != nil -} - -// FunctionKind represents the kind of a WIT [function], which can be one of -// [Freestanding], [Method], [Static], or [Constructor]. -// -// [function]: https://component-model.bytecodealliance.org/design/wit.html#functions -type FunctionKind interface { - isFunctionKind() -} - -// _functionKind is an embeddable type that conforms to the [FunctionKind] interface. -type _functionKind struct{} - -func (_functionKind) isFunctionKind() {} - -// Freestanding represents a free-standing function that is not a method, static, or a constructor. -type Freestanding struct{ _functionKind } - -// Method represents a function that is a method on its associated [Type]. -// The first argument to the function is self, an instance of [Type]. -type Method struct { - _functionKind - Type Type -} - -// Static represents a function that is a static method of its associated [Type]. -type Static struct { - _functionKind - Type Type -} - -// Constructor represents a function that is a constructor for its associated [Type]. -type Constructor struct { - _functionKind - Type Type -} - -// Param represents a parameter to or the result of a [Function]. -// A Param can be unnamed. -type Param struct { - Name string - Type Type -} - -// Package represents a [WIT package] within a [Resolve]. -// It implements the [Node] interface. -// -// A Package is a collection of [Interface] and [World] values. Additionally, -// a Package contains a unique identifier that affects generated components and uniquely -// identifies this particular package. -// -// [WIT package]: https://component-model.bytecodealliance.org/design/wit.html#packages -type Package struct { - Name Ident - Interfaces ordered.Map[string, *Interface] - Worlds ordered.Map[string, *World] - Docs Docs -} - -func (p *Package) dependsOn(pkg *Package) bool { - if pkg == p { - return true - } - var done bool - p.Interfaces.All()(func(_ string, i *Interface) bool { - done = DependsOn(i, pkg) - return !done - }) - if done { - return true - } - p.Worlds.All()(func(_ string, w *World) bool { - done = DependsOn(w, pkg) - return !done - }) - return done -} - -// DependsOn returns true if [Node] node depends on [Package] p. -// Because a package implicitly depends on itself, this returns true if node == p. -func DependsOn(node Node, p *Package) bool { - if node == nil { - return false - } - if node == p { - return true - } - if k, ok := node.(TypeDefKind); ok { - node = Despecialize(k) - } - if d, ok := node.(interface{ dependsOn(*Package) bool }); ok { - return d.dependsOn(p) - } - return false -} - -func comparePackages(a, b *Package) int { - // fmt.Fprintln(os.Stderr, "comparing "+b.Name.String()+" to "+a.Name.String()) - switch { - case a == b: - return 0 - case DependsOn(a, b): - // fmt.Fprintln(os.Stderr, a.Name.String()+" depends on "+b.Name.String()) - return 1 - case DependsOn(b, a): - // fmt.Fprintln(os.Stderr, b.Name.String()+" depends on "+a.Name.String()) - return -1 - } - // fmt.Fprintln(os.Stderr, a.Name.String()+" does not depend on "+b.Name.String()) - return -1 * strings.Compare(a.Name.String(), b.Name.String()) -} - -// Stability represents the version or feature-gated stability of a given feature. -type Stability interface { - Node - isStability() -} - -// _stability is an embeddable type that conforms to the [Stability] interface. -type _stability struct{} - -func (_stability) isStability() {} - -// Stable represents a stable WIT feature, for example: @since(version = 1.2.3) -// -// Stable features have an explicit since version and an optional feature name. -type Stable struct { - _stability - Since semver.Version - Deprecated *semver.Version -} - -// Unstable represents an unstable WIT feature defined by name. -type Unstable struct { - _stability - Feature string - Deprecated *semver.Version -} - -// Docs represent WIT documentation text extracted from comments. -type Docs struct { - Contents string // may be empty -} diff --git a/wit/resource.go b/wit/resource.go new file mode 100644 index 00000000..546c7e1f --- /dev/null +++ b/wit/resource.go @@ -0,0 +1,84 @@ +package wit + +// Resource represents a WIT [resource type]. +// It implements the [Node], [ABI], and [TypeDefKind] interfaces. +// +// [resource type]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#item-resource +type Resource struct{ _typeDefKind } + +// Size returns the [ABI byte size] for [Resource]. +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +func (*Resource) Size() uintptr { return 4 } + +// Align returns the [ABI byte alignment] for [Resource]. +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +func (*Resource) Align() uintptr { return 4 } + +// Flat returns the [flattened] ABI representation of [Resource]. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (*Resource) Flat() []Type { return []Type{U32{}} } + +// hasResource always returns true. +func (*Resource) hasResource() bool { return true } + +// Handle represents a WIT [handle type]. +// It conforms to the [Node], [ABI], and [TypeDefKind] interfaces. +// Handles represent the passing of unique ownership of a resource between +// two components. When the owner of an owned handle drops that handle, +// the resource is destroyed. In contrast, a borrowed handle represents +// a temporary loan of a handle from the caller to the callee for the +// duration of the call. +// +// [handle type]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#handles +type Handle interface { + TypeDefKind + isHandle() +} + +// _handle is an embeddable type that conforms to the [Handle] interface. +type _handle struct{ _typeDefKind } + +func (_handle) isHandle() {} + +// Size returns the [ABI byte size] for this [Handle]. +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +func (_handle) Size() uintptr { return 4 } + +// Align returns the [ABI byte alignment] for this [Handle]. +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +func (_handle) Align() uintptr { return 4 } + +// Flat returns the [flattened] ABI representation of this type. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (_handle) Flat() []Type { return []Type{U32{}} } + +// Own represents an WIT [owned handle]. +// It implements the [Handle], [Node], [ABI], and [TypeDefKind] interfaces. +// +// [owned handle]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#handles +type Own struct { + _handle + Type *TypeDef +} + +func (o *Own) hasResource() bool { return HasResource(o.Type) } +func (o *Own) dependsOn(dep Node) bool { return dep == o || DependsOn(o.Type, dep) } + +// Borrow represents a WIT [borrowed handle]. +// It implements the [Handle], [Node], [ABI], and [TypeDefKind] interfaces. +// +// [borrowed handle]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#handles +type Borrow struct { + _handle + Type *TypeDef +} + +func (*Borrow) hasBorrow() bool { return true } +func (b *Borrow) hasResource() bool { return HasResource(b.Type) } +func (b *Borrow) dependsOn(dep Node) bool { return dep == b || DependsOn(b.Type, dep) } diff --git a/wit/result.go b/wit/result.go new file mode 100644 index 00000000..f4787fa1 --- /dev/null +++ b/wit/result.go @@ -0,0 +1,63 @@ +package wit + +// Result represents a WIT [result type], which is the result of a function call, +// returning an optional value and/or an optional error. It is roughly equivalent to +// the Go pattern of returning (T, error). +// It implements the [Node], [ABI], and [TypeDefKind] interfaces. +// +// [result type]: https://component-model.bytecodealliance.org/design/wit.html#results +type Result struct { + _typeDefKind + OK Type // optional associated [Type] (can be nil) + Err Type // optional associated [Type] (can be nil) +} + +// Despecialize despecializes [Result] o into a [Variant] with two cases, "ok" and "error". +// See the [canonical ABI documentation] for more information. +// +// [canonical ABI documentation]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization +func (r *Result) Despecialize() TypeDefKind { + return &Variant{ + Cases: []Case{ + {Name: "ok", Type: r.OK}, + {Name: "error", Type: r.Err}, + }, + } +} + +// Types returns the unique associated types in [Result] r. +func (r *Result) Types() []Type { + var types []Type + if r.OK != nil { + types = append(types, r.OK) + } + if r.Err != nil && r.Err != r.OK { + types = append(types, r.Err) + } + return types +} + +// Size returns the [ABI byte size] for [Result] r. +// It is first [despecialized] into a [Variant] with two cases "ok" and "error", then sized. +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization +func (r *Result) Size() uintptr { + return r.Despecialize().Size() +} + +// Align returns the [ABI byte alignment] for [Result] r. +// It is first [despecialized] into a [Variant] with two cases "ok" and "error", then aligned. +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization +func (r *Result) Align() uintptr { + return r.Despecialize().Align() +} + +// Flat returns the [flattened] ABI representation of [Result] r. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (r *Result) Flat() []Type { + return r.Despecialize().Flat() +} diff --git a/wit/stability.go b/wit/stability.go new file mode 100644 index 00000000..583282f0 --- /dev/null +++ b/wit/stability.go @@ -0,0 +1,32 @@ +package wit + +import ( + "github.com/coreos/go-semver/semver" +) + +// Stability represents the version or feature-gated stability of a given feature. +type Stability interface { + Node + isStability() +} + +// _stability is an embeddable type that conforms to the [Stability] interface. +type _stability struct{} + +func (_stability) isStability() {} + +// Stable represents a stable WIT feature, for example: @since(version = 1.2.3) +// +// Stable features have an explicit since version and an optional feature name. +type Stable struct { + _stability + Since semver.Version + Deprecated *semver.Version +} + +// Unstable represents an unstable WIT feature defined by name. +type Unstable struct { + _stability + Feature string + Deprecated *semver.Version +} diff --git a/wit/stream.go b/wit/stream.go new file mode 100644 index 00000000..36532dc3 --- /dev/null +++ b/wit/stream.go @@ -0,0 +1,37 @@ +package wit + +// Stream represents a WIT [stream type], expected to be part of [WASI Preview 3]. +// It implements the [Node], [ABI], and [TypeDefKind] interfaces. +// +// [stream type]: https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#streams +// [WASI Preview 3]: https://bytecodealliance.org/articles/webassembly-the-updated-roadmap-for-developers +type Stream struct { + _typeDefKind + Element Type // optional associated Type (can be nil) + End Type // optional associated Type (can be nil) +} + +// Size returns the [ABI byte size] for a [Stream]. +// TODO: what is the ABI size of a stream? +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +func (*Stream) Size() uintptr { return 0 } + +// Align returns the [ABI byte alignment] a [Stream]. +// TODO: what is the ABI alignment of a stream? +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +func (*Stream) Align() uintptr { return 0 } + +// Flat returns the [flattened] ABI representation of [Stream]. +// TODO: what is the ABI representation of a stream? +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (*Stream) Flat() []Type { return nil } + +func (s *Stream) hasPointer() bool { return HasPointer(s.Element) || HasPointer(s.End) } +func (s *Stream) hasBorrow() bool { return HasBorrow(s.Element) || HasBorrow(s.End) } +func (s *Stream) hasResource() bool { return HasResource(s.Element) || HasResource(s.End) } +func (s *Stream) dependsOn(dep Node) bool { + return dep == s || DependsOn(s.Element, dep) || DependsOn(s.End, dep) +} diff --git a/wit/testdata_test.go b/wit/testdata_test.go index 87cb89ac..5946e8c4 100644 --- a/wit/testdata_test.go +++ b/wit/testdata_test.go @@ -67,7 +67,7 @@ func TestGoldenWITFiles(t *testing.T) { } var canWasmTools = sync.OnceValue[bool](func() bool { - // This is explicitly NOT using exec.LookPath so itfails to run on WebAssembly. + // This is explicitly NOT using exec.LookPath so it fails to run on WebAssembly. // This disables tests that require wasm-tools. err := exec.Command("wasm-tools", "--version").Run() return err == nil diff --git a/wit/tuple.go b/wit/tuple.go new file mode 100644 index 00000000..30d51403 --- /dev/null +++ b/wit/tuple.go @@ -0,0 +1,71 @@ +package wit + +import ( + "strconv" +) + +// Tuple represents a WIT [tuple type]. +// A tuple type is an ordered fixed length sequence of values of specified types. +// It is similar to a [Record], except that the fields are identified by their order instead of by names. +// It implements the [Node], [ABI], and [TypeDefKind] interfaces. +// +// [tuple type]: https://component-model.bytecodealliance.org/design/wit.html#tuples +type Tuple struct { + _typeDefKind + Types []Type +} + +// Type returns a non-nil [Type] if all types in t +// are the same. Returns nil if t contains more than one type. +func (t *Tuple) Type() Type { + if len(t.Types) == 0 { + return nil + } + typ := t.Types[0] + for i := 0; i < len(t.Types); i++ { + if t.Types[i] != typ { + return nil + } + } + return typ +} + +// Despecialize despecializes [Tuple] e into a [Record] with 0-based integer field names. +// See the [canonical ABI documentation] for more information. +// +// [canonical ABI documentation]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization +func (t *Tuple) Despecialize() TypeDefKind { + r := &Record{ + Fields: make([]Field, len(t.Types)), + } + for i := range t.Types { + r.Fields[i].Name = strconv.Itoa(i) + r.Fields[i].Type = t.Types[i] + } + return r +} + +// Size returns the [ABI byte size] for [Tuple] t. +// It is first [despecialized] into a [Record] with 0-based integer field names, then sized. +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization +func (t *Tuple) Size() uintptr { + return t.Despecialize().Size() +} + +// Align returns the [ABI byte alignment] for [Tuple] t. +// It is first [despecialized] into a [Record] with 0-based integer field names, then aligned. +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +// [despecialized]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#despecialization +func (t *Tuple) Align() uintptr { + return t.Despecialize().Align() +} + +// Flat returns the [flattened] ABI representation of [Tuple] t. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (t *Tuple) Flat() []Type { + return t.Despecialize().Flat() +} diff --git a/wit/type.go b/wit/type.go new file mode 100644 index 00000000..d730048d --- /dev/null +++ b/wit/type.go @@ -0,0 +1,265 @@ +package wit + +import ( + "fmt" + "unsafe" + + "go.bytecodealliance.org/wit/iterate" +) + +// TypeOwner is the interface implemented by any type that can own a TypeDef, +// currently [World] and [Interface]. +type TypeOwner interface { + Node + AllFunctions() iterate.Seq[*Function] + WITPackage() *Package + isTypeOwner() +} + +type _typeOwner struct{} + +func (_typeOwner) isTypeOwner() {} + +// Type is the interface implemented by any type definition. This can be a +// [primitive type] or a user-defined type in a [TypeDef]. +// It also conforms to the [Node], [ABI], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +type Type interface { + TypeDefKind + TypeName() string + isType() +} + +// _type is an embeddable struct that conforms to the [Type] interface. +// It also implements the [Node], [ABI], and [TypeDefKind] interfaces. +type _type struct{ _typeDefKind } + +func (_type) TypeName() string { return "" } +func (_type) isType() {} + +// ParseType parses a WIT [primitive type] string into +// the associated Type implementation from this package. +// It returns an error if the type string is not recognized. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +func ParseType(s string) (Type, error) { + switch s { + case "bool": + return Bool{}, nil + case "s8": + return S8{}, nil + case "u8": + return U8{}, nil + case "s16": + return S16{}, nil + case "u16": + return U16{}, nil + case "s32": + return S32{}, nil + case "u32": + return U32{}, nil + case "s64": + return S64{}, nil + case "u64": + return U64{}, nil + case "f32", "float32": // TODO: remove float32 at some point + return F32{}, nil + case "f64", "float64": // TODO: remove float64 at some point + return F64{}, nil + case "char": + return Char{}, nil + case "string": + return String{}, nil + } + return nil, fmt.Errorf("unknown primitive type %q", s) +} + +// primitive is a type constraint of the Go equivalents of WIT [primitive types]. +// +// [primitive types]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +type primitive interface { + bool | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64 | float32 | float64 | char | string +} + +// char is defined because [rune] is an alias of [int32] +type char rune + +// Primitive is the interface implemented by WIT [primitive types]. +// It also conforms to the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive types]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +type Primitive interface { + Type + isPrimitive() +} + +// _primitive is an embeddable struct that conforms to the [PrimitiveType] interface. +// It represents a WebAssembly Component Model [primitive type] mapped to its equivalent Go type. +// It also conforms to the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +type _primitive[T primitive] struct{ _type } + +// isPrimitive conforms to the [Primitive] interface. +func (_primitive[T]) isPrimitive() {} + +// Size returns the byte size for values of this type. +func (_primitive[T]) Size() uintptr { + var v T + switch any(v).(type) { + case string: + return 8 // [2]int32 + default: + return unsafe.Sizeof(v) + } +} + +// Align returns the byte alignment for values of this type. +func (_primitive[T]) Align() uintptr { + var v T + switch any(v).(type) { + case string: + return 4 // int32 + default: + return unsafe.Alignof(v) + } +} + +// hasPointer returns whether the ABI representation of this type contains a pointer. +// This will only return true for [String]. +func (_primitive[T]) hasPointer() bool { + var v T + switch any(v).(type) { + case string: + return true + default: + return false + } +} + +// Flat returns the [flattened] ABI representation of this type. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (_primitive[T]) Flat() []Type { + var v T + switch any(v).(type) { + case bool, int8, uint8, int16, uint16, int32, uint32, char: + return []Type{U32{}} + case int64, uint64: + return []Type{U64{}} + case float32: + return []Type{F32{}} + case float64: + return []Type{F64{}} + case string: + return []Type{PointerTo(U8{}), U32{}} + default: + panic(fmt.Sprintf("BUG: unknown primitive type %T", v)) // should never reach here + } +} + +// Bool represents the WIT [primitive type] bool, a boolean value either true or false. +// It is equivalent to the Go type [bool]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [bool]: https://pkg.go.dev/builtin#bool +type Bool struct{ _primitive[bool] } + +// S8 represents the WIT [primitive type] s8, a signed 8-bit integer. +// It is equivalent to the Go type [int8]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [int8]: https://pkg.go.dev/builtin#int8 +type S8 struct{ _primitive[int8] } + +// U8 represents the WIT [primitive type] u8, an unsigned 8-bit integer. +// It is equivalent to the Go type [uint8]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [uint8]: https://pkg.go.dev/builtin#uint8 +type U8 struct{ _primitive[uint8] } + +// S16 represents the WIT [primitive type] s16, a signed 16-bit integer. +// It is equivalent to the Go type [int16]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [int16]: https://pkg.go.dev/builtin#int16 +type S16 struct{ _primitive[int16] } + +// U16 represents the WIT [primitive type] u16, an unsigned 16-bit integer. +// It is equivalent to the Go type [uint16]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [uint16]: https://pkg.go.dev/builtin#uint16 +type U16 struct{ _primitive[uint16] } + +// S32 represents the WIT [primitive type] s32, a signed 32-bit integer. +// It is equivalent to the Go type [int32]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [int32]: https://pkg.go.dev/builtin#int32 +type S32 struct{ _primitive[int32] } + +// U32 represents the WIT [primitive type] u32, an unsigned 32-bit integer. +// It is equivalent to the Go type [uint32]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [uint32]: https://pkg.go.dev/builtin#uint32 +type U32 struct{ _primitive[uint32] } + +// S64 represents the WIT [primitive type] s64, a signed 64-bit integer. +// It is equivalent to the Go type [int64]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [int64]: https://pkg.go.dev/builtin#int64 +type S64 struct{ _primitive[int64] } + +// U64 represents the WIT [primitive type] u64, an unsigned 64-bit integer. +// It is equivalent to the Go type [uint64]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [uint64]: https://pkg.go.dev/builtin#uint64 +type U64 struct{ _primitive[uint64] } + +// F32 represents the WIT [primitive type] f32, a 32-bit floating point value. +// It is equivalent to the Go type [float32]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [float32]: https://pkg.go.dev/builtin#float32 +type F32 struct{ _primitive[float32] } + +// F64 represents the WIT [primitive type] f64, a 64-bit floating point value. +// It is equivalent to the Go type [float64]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [float64]: https://pkg.go.dev/builtin#float64 +type F64 struct{ _primitive[float64] } + +// Char represents the WIT [primitive type] char, a single Unicode character, +// specifically a [Unicode scalar value]. It is equivalent to the Go type [rune]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [Unicode scalar value]: https://unicode.org/glossary/#unicode_scalar_value +// [rune]: https://pkg.go.dev/builtin#rune +type Char struct{ _primitive[char] } + +// String represents the WIT [primitive type] string, a finite string of Unicode characters. +// It is equivalent to the Go type [string]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +// +// [primitive type]: https://component-model.bytecodealliance.org/design/wit.html#primitive-types +// [string]: https://pkg.go.dev/builtin#string +type String struct{ _primitive[string] } diff --git a/wit/typedef.go b/wit/typedef.go new file mode 100644 index 00000000..c9426113 --- /dev/null +++ b/wit/typedef.go @@ -0,0 +1,150 @@ +package wit + +import ( + "slices" +) + +// TypeDef represents a WIT type definition. A TypeDef may be named or anonymous, +// and optionally belong to a [World] or [Interface]. +// It implements the [Node], [ABI], [Type], and [TypeDefKind] interfaces. +type TypeDef struct { + _type + _worldItem + Name *string + Kind TypeDefKind + Owner TypeOwner + Stability Stability // WIT @since or @unstable (nil if unknown) + Docs Docs +} + +// TypeName returns the [WIT] type name for t. +// Returns an empty string if t is anonymous. +// +// [WIT]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md +func (t *TypeDef) TypeName() string { + if t.Name != nil { + return *t.Name + } + return "" +} + +// TypeDef returns the parent [TypeDef] of [TypeDef] t. +// If t is not a [type alias], TypeDef returns t. +// +// [type alias]: https://component-model.bytecodealliance.org/design/wit.html#type-aliases +func (t *TypeDef) TypeDef() *TypeDef { + if t, ok := t.Kind.(*TypeDef); ok { + return t + } + return t +} + +// Root returns the root [TypeDef] of [TypeDef] t. +// If t is not a [type alias], Root returns t. +// +// [type alias]: https://component-model.bytecodealliance.org/design/wit.html#type-aliases +func (t *TypeDef) Root() *TypeDef { + for { + switch kind := t.Kind.(type) { + case *TypeDef: + t = kind + default: + return t + } + } +} + +// Constructor returns the constructor for [TypeDef] t, or nil if none. +// Currently t must be a [Resource] to have a constructor. +func (t *TypeDef) Constructor() *Function { + var constructor *Function + t.Owner.AllFunctions()(func(f *Function) bool { + if c, ok := f.Kind.(*Constructor); ok && c.Type == t { + constructor = f + return false + } + return true + }) + return constructor +} + +// StaticFunctions returns all static functions for [TypeDef] t. +// Currently t must be a [Resource] to have static functions. +func (t *TypeDef) StaticFunctions() []*Function { + var statics []*Function + t.Owner.AllFunctions()(func(f *Function) bool { + if s, ok := f.Kind.(*Static); ok && s.Type == t { + statics = append(statics, f) + } + return true + }) + slices.SortFunc(statics, compareFunctions) + return statics +} + +// Methods returns all methods for [TypeDef] t. +// Currently t must be a [Resource] to have methods. +func (t *TypeDef) Methods() []*Function { + var methods []*Function + t.Owner.AllFunctions()(func(f *Function) bool { + if m, ok := f.Kind.(*Method); ok && m.Type == t { + methods = append(methods, f) + } + return true + }) + slices.SortFunc(methods, compareFunctions) + return methods +} + +// Size returns the byte size for values of type t. +func (t *TypeDef) Size() uintptr { + return t.Kind.Size() +} + +// Align returns the byte alignment for values of type t. +func (t *TypeDef) Align() uintptr { + return t.Kind.Align() +} + +// Flat returns the [flattened] ABI representation of t. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (t *TypeDef) Flat() []Type { + return t.Kind.Flat() +} + +func (t *TypeDef) hasPointer() bool { return HasPointer(t.Kind) } +func (t *TypeDef) hasBorrow() bool { return HasBorrow(t.Kind) } +func (t *TypeDef) hasResource() bool { return HasResource(t.Kind) } +func (t *TypeDef) dependsOn(dep Node) bool { + return dep == t || dep == t.Owner || + (t.Owner != nil && dep == t.Owner.WITPackage()) || + DependsOn(t.Kind, dep) +} + +// TypeDefKind represents the underlying type in a [TypeDef], which can be one of +// [Record], [Resource], [Handle], [Flags], [Tuple], [Variant], [Enum], +// [Option], [Result], [List], [Future], [Stream], or [Type]. +// It implements the [Node] and [ABI] interfaces. +type TypeDefKind interface { + Node + ABI + isTypeDefKind() +} + +// _typeDefKind is an embeddable type that conforms to the [TypeDefKind] interface. +type _typeDefKind struct{} + +func (_typeDefKind) isTypeDefKind() {} + +// KindOf probes [Type] t to determine if it is a [TypeDef] with [TypeDefKind] K. +// It returns the underlying Kind if present. +func KindOf[K TypeDefKind](t Type) (kind K) { + if td, ok := t.(*TypeDef); ok { + if kind, ok = td.Kind.(K); ok { + return kind + } + } + var zero K + return zero +} diff --git a/wit/variant.go b/wit/variant.go new file mode 100644 index 00000000..efdcbee6 --- /dev/null +++ b/wit/variant.go @@ -0,0 +1,158 @@ +package wit + +// Variant represents a WIT [variant type], a tagged/discriminated union. +// A variant type declares one or more cases. Each case has a name and, optionally, +// a type of data associated with that case. +// It implements the [Node], [ABI], and [TypeDefKind] interfaces. +// +// [variant type]: https://component-model.bytecodealliance.org/design/wit.html#variants +type Variant struct { + _typeDefKind + Cases []Case +} + +// Enum attempts to represent [Variant] v as an [Enum]. +// This will only succeed if v has no associated types. If v has +// associated types, then it will return nil. +func (v *Variant) Enum() *Enum { + types := v.Types() + if len(types) > 0 { + return nil + } + e := &Enum{ + Cases: make([]EnumCase, len(v.Cases)), + } + for i := range v.Cases { + e.Cases[i].Name = v.Cases[i].Name + e.Cases[i].Docs = v.Cases[i].Docs + } + return e +} + +// Types returns the unique associated types in [Variant] v. +func (v *Variant) Types() []Type { + var types []Type + typeMap := make(map[Type]bool) + for i := range v.Cases { + t := v.Cases[i].Type + if t == nil || typeMap[t] { + continue + } + types = append(types, t) + typeMap[t] = true + } + return types +} + +// Size returns the [ABI byte size] for [Variant] v. +// +// [ABI byte size]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#size +func (v *Variant) Size() uintptr { + s := Discriminant(len(v.Cases)).Size() + s = Align(s, v.maxCaseAlign()) + s += v.maxCaseSize() + return Align(s, v.Align()) +} + +// Align returns the [ABI byte alignment] for [Variant] v. +// +// [ABI byte alignment]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment +func (v *Variant) Align() uintptr { + return max(Discriminant(len(v.Cases)).Align(), v.maxCaseAlign()) +} + +// Flat returns the [flattened] ABI representation of [Variant] v. +// +// [flattened]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening +func (v *Variant) Flat() []Type { + var flat []Type + for _, t := range v.Types() { + for i, f := range t.Flat() { + if i >= len(flat) { + flat = append(flat, f) + } else { + flat[i] = flatJoin(flat[i], f) + } + } + } + return append(Discriminant(len(v.Cases)).Flat(), flat...) +} + +func flatJoin(a, b Type) Type { + if a == b { + return a + } + if a.Size() == 4 && b.Size() == 4 { + return U32{} + } + return U64{} +} + +func (v *Variant) maxCaseSize() uintptr { + var s uintptr + for _, c := range v.Cases { + if c.Type != nil { + s = max(s, c.Type.Size()) + } + } + return s +} + +func (v *Variant) maxCaseAlign() uintptr { + var a uintptr = 1 + for _, c := range v.Cases { + if c.Type != nil { + a = max(a, c.Type.Align()) + } + } + return a +} + +func (v *Variant) hasPointer() bool { + for _, t := range v.Types() { + if HasPointer(t) { + return true + } + } + return false +} + +func (v *Variant) hasBorrow() bool { + for _, t := range v.Types() { + if HasBorrow(t) { + return true + } + } + return false +} + +func (v *Variant) hasResource() bool { + for _, t := range v.Types() { + if HasResource(t) { + return true + } + } + return false +} + +func (v *Variant) dependsOn(dep Node) bool { + if dep == v { + return true + } + for _, t := range v.Types() { + if DependsOn(t, dep) { + return true + } + } + return false +} + +// Case represents a single case in a [Variant]. +// It implements the [Node] interface. +type Case struct { + Name string + Type Type // optional associated [Type] (can be nil) + Docs Docs +} + +func (c *Case) dependsOn(dep Node) bool { return dep == c || DependsOn(c.Type, dep) } diff --git a/wit/webassembly.wit b/wit/webassembly.wit deleted file mode 100755 index f3618092..00000000 --- a/wit/webassembly.wit +++ /dev/null @@ -1,707 +0,0 @@ -package wasi:cli@0.2.0; - -interface stdout { - use wasi:io/streams@0.2.0.{output-stream}; - get-stdout: func() -> output-stream; -} - -interface stderr { - use wasi:io/streams@0.2.0.{output-stream}; - get-stderr: func() -> output-stream; -} - -interface stdin { - use wasi:io/streams@0.2.0.{input-stream}; - get-stdin: func() -> input-stream; -} - - -package wasi:clocks@0.2.0 { - interface monotonic-clock { - use wasi:io/poll@0.2.0.{pollable}; - type instant = u64; - type duration = u64; - now: func() -> instant; - resolution: func() -> duration; - subscribe-instant: func(when: instant) -> pollable; - subscribe-duration: func(when: duration) -> pollable; - } - - interface wall-clock { - record datetime { - seconds: u64, - nanoseconds: u32, - } - now: func() -> datetime; - resolution: func() -> datetime; - } -} - -package wasi:http@0.2.0 { - /// This interface defines all of the types and methods for implementing - /// HTTP Requests and Responses, both incoming and outgoing, as well as - /// their headers, trailers, and bodies. - interface types { - use wasi:clocks/monotonic-clock@0.2.0.{duration}; - use wasi:io/streams@0.2.0.{input-stream}; - use wasi:io/streams@0.2.0.{output-stream}; - use wasi:io/error@0.2.0.{error as io-error}; - use wasi:io/poll@0.2.0.{pollable}; - - /// This type corresponds to HTTP standard Methods. - variant method { - get, - head, - post, - put, - delete, - connect, - options, - trace, - patch, - other(string), - } - - /// This type corresponds to HTTP standard Related Schemes. - variant scheme { HTTP, HTTPS, other(string) } - - /// Defines the case payload type for `DNS-error` above: - record DNS-error-payload { - rcode: option, - info-code: option, - } - - /// Defines the case payload type for `TLS-alert-received` above: - record TLS-alert-received-payload { - alert-id: option, - alert-message: option, - } - - /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: - record field-size-payload { - field-name: option, - field-size: option, - } - - /// These cases are inspired by the IANA HTTP Proxy Error Types: - /// https://www.iana.org/assignments/http-proxy-status/http-proxy-status.xhtml#table-http-proxy-error-types - variant error-code { - DNS-timeout, - DNS-error(DNS-error-payload), - destination-not-found, - destination-unavailable, - destination-IP-prohibited, - destination-IP-unroutable, - connection-refused, - connection-terminated, - connection-timeout, - connection-read-timeout, - connection-write-timeout, - connection-limit-reached, - TLS-protocol-error, - TLS-certificate-error, - TLS-alert-received(TLS-alert-received-payload), - HTTP-request-denied, - HTTP-request-length-required, - HTTP-request-body-size(option), - HTTP-request-method-invalid, - HTTP-request-URI-invalid, - HTTP-request-URI-too-long, - HTTP-request-header-section-size(option), - HTTP-request-header-size(option), - HTTP-request-trailer-section-size(option), - HTTP-request-trailer-size(field-size-payload), - HTTP-response-incomplete, - HTTP-response-header-section-size(option), - HTTP-response-header-size(field-size-payload), - HTTP-response-body-size(option), - HTTP-response-trailer-section-size(option), - HTTP-response-trailer-size(field-size-payload), - HTTP-response-transfer-coding(option), - HTTP-response-content-coding(option), - HTTP-response-timeout, - HTTP-upgrade-failed, - HTTP-protocol-error, - loop-detected, - configuration-error, - /// This is a catch-all error for anything that doesn't fit cleanly into a - /// more specific case. It also includes an optional string for an - /// unstructured description of the error. Users should not depend on the - /// string for diagnosing errors, as it's not required to be consistent - /// between implementations. - internal-error(option), - } - - /// This type enumerates the different kinds of errors that may occur when - /// setting or appending to a `fields` resource. - variant header-error { - /// This error indicates that a `field-key` or `field-value` was - /// syntactically invalid when used with an operation that sets headers in a - /// `fields`. - invalid-syntax, - /// This error indicates that a forbidden `field-key` was used when trying - /// to set a header in a `fields`. - forbidden, - /// This error indicates that the operation on the `fields` was not - /// permitted because the fields are immutable. - immutable, - } - - /// Field keys are always strings. - type field-key = string; - - /// Field values should always be ASCII strings. However, in - /// reality, HTTP implementations often have to interpret malformed values, - /// so they are provided as a list of bytes. - type field-value = list; - - /// This following block defines the `fields` resource which corresponds to - /// HTTP standard Fields. Fields are a common representation used for both - /// Headers and Trailers. - /// - /// A `fields` may be mutable or immutable. A `fields` created using the - /// constructor, `from-list`, or `clone` will be mutable, but a `fields` - /// resource given by other means (including, but not limited to, - /// `incoming-request.headers`, `outgoing-request.headers`) might be be - /// immutable. In an immutable fields, the `set`, `append`, and `delete` - /// operations will fail with `header-error.immutable`. - resource fields { - /// Construct an empty HTTP Fields. - /// - /// The resulting `fields` is mutable. - constructor(); - - /// Append a value for a key. Does not change or delete any existing - /// values for that key. - /// - /// Fails with `header-error.immutable` if the `fields` are immutable. - append: func(name: field-key, value: field-value) -> result<_, header-error>; - - /// Make a deep copy of the Fields. Equivelant in behavior to calling the - /// `fields` constructor on the return value of `entries`. The resulting - /// `fields` is mutable. - clone: func() -> fields; - - /// Delete all values for a key. Does nothing if no values for the key - /// exist. - /// - /// Fails with `header-error.immutable` if the `fields` are immutable. - delete: func(name: field-key) -> result<_, header-error>; - - /// Retrieve the full set of keys and values in the Fields. Like the - /// constructor, the list represents each key-value pair. - /// - /// The outer list represents each key-value pair in the Fields. Keys - /// which have multiple values are represented by multiple entries in this - /// list with the same key. - entries: func() -> list>; - - /// Get all of the values corresponding to a key. If the key is not present - /// in this `fields`, an empty list is returned. However, if the key is - /// present but empty, this is represented by a list with one or more - /// empty field-values present. - get: func(name: field-key) -> list; - - /// Returns `true` when the key is present in this `fields`. If the key is - /// syntactically invalid, `false` is returned. - has: func(name: field-key) -> bool; - - /// Set all of the values for a key. Clears any existing values for that - /// key, if they have been set. - /// - /// Fails with `header-error.immutable` if the `fields` are immutable. - set: func(name: field-key, value: list) -> result<_, header-error>; - - /// Construct an HTTP Fields. - /// - /// The resulting `fields` is mutable. - /// - /// The list represents each key-value pair in the Fields. Keys - /// which have multiple values are represented by multiple entries in this - /// list with the same key. - /// - /// The tuple is a pair of the field key, represented as a string, and - /// Value, represented as a list of bytes. In a valid Fields, all keys - /// and values are valid UTF-8 strings. However, values are not always - /// well-formed, so they are represented as a raw list of bytes. - /// - /// An error result will be returned if any header or value was - /// syntactically invalid, or if a header was forbidden. - from-list: static func(entries: list>) -> result; - } - - /// Headers is an alias for Fields. - type headers = fields; - - /// Trailers is an alias for Fields. - type trailers = fields; - - /// Represents an incoming HTTP Request. - resource incoming-request { - - /// Returns the authority from the request, if it was present. - authority: func() -> option; - - /// Gives the `incoming-body` associated with this request. Will only - /// return success at most once, and subsequent calls will return error. - consume: func() -> result; - - /// Get the `headers` associated with the request. - /// - /// The returned `headers` resource is immutable: `set`, `append`, and - /// `delete` operations will fail with `header-error.immutable`. - /// - /// The `headers` returned are a child resource: it must be dropped before - /// the parent `incoming-request` is dropped. Dropping this - /// `incoming-request` before all children are dropped will trap. - headers: func() -> headers; - - /// Returns the method of the incoming request. - method: func() -> method; - - /// Returns the path with query parameters from the request, as a string. - path-with-query: func() -> option; - - /// Returns the protocol scheme from the request. - scheme: func() -> option; - } - - /// Represents an outgoing HTTP Request. - resource outgoing-request { - /// Construct a new `outgoing-request` with a default `method` of `GET`, and - /// `none` values for `path-with-query`, `scheme`, and `authority`. - /// - /// * `headers` is the HTTP Headers for the Request. - /// - /// It is possible to construct, or manipulate with the accessor functions - /// below, an `outgoing-request` with an invalid combination of `scheme` - /// and `authority`, or `headers` which are not permitted to be sent. - /// It is the obligation of the `outgoing-handler.handle` implementation - /// to reject invalid constructions of `outgoing-request`. - constructor(headers: headers); - - /// Get the HTTP Authority for the Request. A value of `none` may be used - /// with Related Schemes which do not require an Authority. The HTTP and - /// HTTPS schemes always require an authority. - authority: func() -> option; - - /// Returns the resource corresponding to the outgoing Body for this - /// Request. - /// - /// Returns success on the first call: the `outgoing-body` resource for - /// this `outgoing-request` can be retrieved at most once. Subsequent - /// calls will return error. - body: func() -> result; - - /// Get the headers associated with the Request. - /// - /// The returned `headers` resource is immutable: `set`, `append`, and - /// `delete` operations will fail with `header-error.immutable`. - /// - /// This headers resource is a child: it must be dropped before the parent - /// `outgoing-request` is dropped, or its ownership is transfered to - /// another component by e.g. `outgoing-handler.handle`. - headers: func() -> headers; - - /// Get the Method for the Request. - method: func() -> method; - - /// Get the combination of the HTTP Path and Query for the Request. - /// When `none`, this represents an empty Path and empty Query. - path-with-query: func() -> option; - - /// Get the HTTP Related Scheme for the Request. When `none`, the - /// implementation may choose an appropriate default scheme. - scheme: func() -> option; - - /// Set the HTTP Authority for the Request. A value of `none` may be used - /// with Related Schemes which do not require an Authority. The HTTP and - /// HTTPS schemes always require an authority. Fails if the string given is - /// not a syntactically valid uri authority. - set-authority: func(authority: option) -> result; - - /// Set the Method for the Request. Fails if the string present in a - /// `method.other` argument is not a syntactically valid method. - set-method: func(method: method) -> result; - - /// Set the combination of the HTTP Path and Query for the Request. - /// When `none`, this represents an empty Path and empty Query. Fails is the - /// string given is not a syntactically valid path and query uri component. - set-path-with-query: func(path-with-query: option) -> result; - - /// Set the HTTP Related Scheme for the Request. When `none`, the - /// implementation may choose an appropriate default scheme. Fails if the - /// string given is not a syntactically valid uri scheme. - set-scheme: func(scheme: option) -> result; - } - - /// Parameters for making an HTTP Request. Each of these parameters is - /// currently an optional timeout applicable to the transport layer of the - /// HTTP protocol. - /// - /// These timeouts are separate from any the user may use to bound a - /// blocking call to `wasi:io/poll.poll`. - resource request-options { - /// Construct a default `request-options` value. - constructor(); - - /// The timeout for receiving subsequent chunks of bytes in the Response - /// body stream. - between-bytes-timeout: func() -> option; - - /// The timeout for the initial connect to the HTTP Server. - connect-timeout: func() -> option; - - /// The timeout for receiving the first byte of the Response body. - first-byte-timeout: func() -> option; - - /// Set the timeout for receiving subsequent chunks of bytes in the Response - /// body stream. An error return value indicates that this timeout is not - /// supported. - set-between-bytes-timeout: func(duration: option) -> result; - - /// Set the timeout for the initial connect to the HTTP Server. An error - /// return value indicates that this timeout is not supported. - set-connect-timeout: func(duration: option) -> result; - - /// Set the timeout for receiving the first byte of the Response body. An - /// error return value indicates that this timeout is not supported. - set-first-byte-timeout: func(duration: option) -> result; - } - - /// Represents the ability to send an HTTP Response. - /// - /// This resource is used by the `wasi:http/incoming-handler` interface to - /// allow a Response to be sent corresponding to the Request provided as the - /// other argument to `incoming-handler.handle`. - resource response-outparam { - - /// Set the value of the `response-outparam` to either send a response, - /// or indicate an error. - /// - /// This method consumes the `response-outparam` to ensure that it is - /// called at most once. If it is never called, the implementation - /// will respond with an error. - /// - /// The user may provide an `error` to `response` to allow the - /// implementation determine how to respond with an HTTP error response. - set: static func(param: response-outparam, response: result); - } - - /// This type corresponds to the HTTP standard Status Code. - type status-code = u16; - - /// Represents an incoming HTTP Response. - resource incoming-response { - - /// Returns the incoming body. May be called at most once. Returns error - /// if called additional times. - consume: func() -> result; - - /// Returns the headers from the incoming response. - /// - /// The returned `headers` resource is immutable: `set`, `append`, and - /// `delete` operations will fail with `header-error.immutable`. - /// - /// This headers resource is a child: it must be dropped before the parent - /// `incoming-response` is dropped. - headers: func() -> headers; - - /// Returns the status code from the incoming response. - status: func() -> status-code; - } - - /// Represents an incoming HTTP Request or Response's Body. - /// - /// A body has both its contents - a stream of bytes - and a (possibly - /// empty) set of trailers, indicating that the full contents of the - /// body have been received. This resource represents the contents as - /// an `input-stream` and the delivery of trailers as a `future-trailers`, - /// and ensures that the user of this interface may only be consuming either - /// the body contents or waiting on trailers at any given time. - resource incoming-body { - - /// Returns the contents of the body, as a stream of bytes. - /// - /// Returns success on first call: the stream representing the contents - /// can be retrieved at most once. Subsequent calls will return error. - /// - /// The returned `input-stream` resource is a child: it must be dropped - /// before the parent `incoming-body` is dropped, or consumed by - /// `incoming-body.finish`. - /// - /// This invariant ensures that the implementation can determine whether - /// the user is consuming the contents of the body, waiting on the - /// `future-trailers` to be ready, or neither. This allows for network - /// backpressure is to be applied when the user is consuming the body, - /// and for that backpressure to not inhibit delivery of the trailers if - /// the user does not read the entire body. - %stream: func() -> result; - - /// Takes ownership of `incoming-body`, and returns a `future-trailers`. - /// This function will trap if the `input-stream` child is still alive. - finish: static func(this: incoming-body) -> future-trailers; - } - - /// Represents a future which may eventaully return trailers, or an error. - /// - /// In the case that the incoming HTTP Request or Response did not have any - /// trailers, this future will resolve to the empty set of trailers once the - /// complete Request or Response body has been received. - resource future-trailers { - - /// Returns the contents of the trailers, or an error which occured, - /// once the future is ready. - /// - /// The outer `option` represents future readiness. Users can wait on this - /// `option` to become `some` using the `subscribe` method. - /// - /// The outer `result` is used to retrieve the trailers or error at most - /// once. It will be success on the first call in which the outer option - /// is `some`, and error on subsequent calls. - /// - /// The inner `result` represents that either the HTTP Request or Response - /// body, as well as any trailers, were received successfully, or that an - /// error occured receiving them. The optional `trailers` indicates whether - /// or not trailers were present in the body. - /// - /// When some `trailers` are returned by this method, the `trailers` - /// resource is immutable, and a child. Use of the `set`, `append`, or - /// `delete` methods will return an error, and the resource must be - /// dropped before the parent `future-trailers` is dropped. - get: func() -> option, error-code>>>; - - /// Returns a pollable which becomes ready when either the trailers have - /// been received, or an error has occured. When this pollable is ready, - /// the `get` method will return `some`. - subscribe: func() -> pollable; - } - - /// Represents an outgoing HTTP Response. - resource outgoing-response { - /// Construct an `outgoing-response`, with a default `status-code` of `200`. - /// If a different `status-code` is needed, it must be set via the - /// `set-status-code` method. - /// - /// * `headers` is the HTTP Headers for the Response. - constructor(headers: headers); - - /// Returns the resource corresponding to the outgoing Body for this Response. - /// - /// Returns success on the first call: the `outgoing-body` resource for - /// this `outgoing-response` can be retrieved at most once. Subsequent - /// calls will return error. - body: func() -> result; - - /// Get the headers associated with the Request. - /// - /// The returned `headers` resource is immutable: `set`, `append`, and - /// `delete` operations will fail with `header-error.immutable`. - /// - /// This headers resource is a child: it must be dropped before the parent - /// `outgoing-request` is dropped, or its ownership is transfered to - /// another component by e.g. `outgoing-handler.handle`. - headers: func() -> headers; - - /// Set the HTTP Status Code for the Response. Fails if the status-code - /// given is not a valid http status code. - set-status-code: func(status-code: status-code) -> result; - - /// Get the HTTP Status Code for the Response. - status-code: func() -> status-code; - } - - /// Represents an outgoing HTTP Request or Response's Body. - /// - /// A body has both its contents - a stream of bytes - and a (possibly - /// empty) set of trailers, inducating the full contents of the body - /// have been sent. This resource represents the contents as an - /// `output-stream` child resource, and the completion of the body (with - /// optional trailers) with a static function that consumes the - /// `outgoing-body` resource, and ensures that the user of this interface - /// may not write to the body contents after the body has been finished. - /// - /// If the user code drops this resource, as opposed to calling the static - /// method `finish`, the implementation should treat the body as incomplete, - /// and that an error has occured. The implementation should propogate this - /// error to the HTTP protocol by whatever means it has available, - /// including: corrupting the body on the wire, aborting the associated - /// Request, or sending a late status code for the Response. - resource outgoing-body { - - /// Returns a stream for writing the body contents. - /// - /// The returned `output-stream` is a child resource: it must be dropped - /// before the parent `outgoing-body` resource is dropped (or finished), - /// otherwise the `outgoing-body` drop or `finish` will trap. - /// - /// Returns success on the first call: the `output-stream` resource for - /// this `outgoing-body` may be retrieved at most once. Subsequent calls - /// will return error. - write: func() -> result; - - /// Finalize an outgoing body, optionally providing trailers. This must be - /// called to signal that the response is complete. If the `outgoing-body` - /// is dropped without calling `outgoing-body.finalize`, the implementation - /// should treat the body as corrupted. - /// - /// Fails if the body's `outgoing-request` or `outgoing-response` was - /// constructed with a Content-Length header, and the contents written - /// to the body (via `write`) does not match the value given in the - /// Content-Length. - finish: static func(this: outgoing-body, trailers: option) -> result<_, error-code>; - } - - /// Represents a future which may eventaully return an incoming HTTP - /// Response, or an error. - /// - /// This resource is returned by the `wasi:http/outgoing-handler` interface to - /// provide the HTTP Response corresponding to the sent Request. - resource future-incoming-response { - - /// Returns the incoming HTTP Response, or an error, once one is ready. - /// - /// The outer `option` represents future readiness. Users can wait on this - /// `option` to become `some` using the `subscribe` method. - /// - /// The outer `result` is used to retrieve the response or error at most - /// once. It will be success on the first call in which the outer option - /// is `some`, and error on subsequent calls. - /// - /// The inner `result` represents that either the incoming HTTP Response - /// status and headers have recieved successfully, or that an error - /// occured. Errors may also occur while consuming the response body, - /// but those will be reported by the `incoming-body` and its - /// `output-stream` child. - get: func() -> option>>; - - /// Returns a pollable which becomes ready when either the Response has - /// been received, or an error has occured. When this pollable is ready, - /// the `get` method will return `some`. - subscribe: func() -> pollable; - } - - /// Attempts to extract a http-related `error` from the wasi:io `error` - /// provided. - /// - /// Stream operations which return - /// `wasi:io/stream/stream-error::last-operation-failed` have a payload of - /// type `wasi:io/error/error` with more information about the operation - /// that failed. This payload can be passed through to this function to see - /// if there's http-related information about the error to return. - /// - /// Note that this function is fallible because not all io-errors are - /// http-related errors. - http-error-code: func(err: borrow) -> option; - } - - /// This interface defines a handler of incoming HTTP Requests. It should - /// be exported by components which can respond to HTTP Requests. - interface incoming-handler { - use types.{incoming-request}; - use types.{response-outparam}; - - /// This function is invoked with an incoming HTTP Request, and a resource - /// `response-outparam` which provides the capability to reply with an HTTP - /// Response. The response is sent by calling the `response-outparam.set` - /// method, which allows execution to continue after the response has been - /// sent. This enables both streaming to the response body, and performing other - /// work. - /// - /// The implementor of this function must write a response to the - /// `response-outparam` before returning, or else the caller will respond - /// with an error on its behalf. - handle: func(request: incoming-request, response-out: response-outparam); - } - - /// This interface defines a handler of outgoing HTTP Requests. It should be - /// imported by components which wish to make HTTP Requests. - interface outgoing-handler { - use types.{outgoing-request}; - use types.{request-options}; - use types.{future-incoming-response}; - use types.{error-code}; - - /// This function is invoked with an outgoing HTTP Request, and it returns - /// a resource `future-incoming-response` which represents an HTTP Response - /// which may arrive in the future. - /// - /// The `options` argument accepts optional parameters for the HTTP - /// protocol's transport layer. - /// - /// This function may return an error if the `outgoing-request` is invalid - /// or not allowed to be made. Otherwise, protocol errors are reported - /// through the `future-incoming-response`. - handle: func(request: outgoing-request, options: option) -> result; - } - - /// The `wasi:http/proxy` world captures a widely-implementable intersection of - /// hosts that includes HTTP forward and reverse proxies. Components targeting - /// this world may concurrently stream in and out any number of incoming and - /// outgoing HTTP requests. - world proxy { - import wasi:random/random@0.2.0; - import wasi:io/error@0.2.0; - import wasi:io/poll@0.2.0; - import wasi:io/streams@0.2.0; - import wasi:cli/stdout@0.2.0; - import wasi:cli/stderr@0.2.0; - import wasi:cli/stdin@0.2.0; - import wasi:clocks/monotonic-clock@0.2.0; - import types; - import outgoing-handler; - import wasi:clocks/wall-clock@0.2.0; - export incoming-handler; - } -} - -package wasi:io@0.2.0 { - interface poll { - resource pollable { - block: func(); - ready: func() -> bool; - } - poll: func(in: list>) -> list; - } - - interface error { - resource error { - to-debug-string: func() -> string; - } - } - - interface streams { - use error.{error}; - use poll.{pollable}; - variant stream-error { - last-operation-failed(error), - closed, - } - resource input-stream { - blocking-read: func(len: u64) -> result, stream-error>; - blocking-skip: func(len: u64) -> result; - read: func(len: u64) -> result, stream-error>; - skip: func(len: u64) -> result; - subscribe: func() -> pollable; - } - resource output-stream { - blocking-flush: func() -> result<_, stream-error>; - blocking-splice: func(src: borrow, len: u64) -> result; - blocking-write-and-flush: func(contents: list) -> result<_, stream-error>; - blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>; - check-write: func() -> result; - flush: func() -> result<_, stream-error>; - splice: func(src: borrow, len: u64) -> result; - subscribe: func() -> pollable; - write: func(contents: list) -> result<_, stream-error>; - write-zeroes: func(len: u64) -> result<_, stream-error>; - } - } -} - -package wasi:random@0.2.0 { - interface random { - get-random-bytes: func(len: u64) -> list; - get-random-u64: func() -> u64; - } -} \ No newline at end of file diff --git a/wit/wit.go b/wit/wit.go index 9b1e83f1..9a74350f 100644 --- a/wit/wit.go +++ b/wit/wit.go @@ -18,6 +18,75 @@ type Node interface { WIT(ctx Node, name string) string } +// Returns true if node == dep. +// Returns false if node or dep are nil. +func DependsOn(node, dep Node) bool { + if node == nil || dep == nil { + return false + } + if node == dep { + return true + } + + // Dereference InterfaceRefs + if ref, ok := dep.(*InterfaceRef); ok { + dep = ref.Interface + } + + // TODO: is it harmful to despecialize before doing dependency check? + // e.g. it breaks the node == dep check? + if k, ok := node.(TypeDefKind); ok { + node = Despecialize(k) + } + if d, ok := node.(dependent); ok { + return d.dependsOn(dep) + } + + return false +} + +type dependent interface { + dependsOn(dep Node) bool +} + +// Filter returns a [Node] suitable to passing to (Resolve).WIT to filter +// the emitted WIT to a specific [World] and/or [Interface]. +func Filter(w *World, i *Interface) Node { + if w == nil && i == nil { + return nil + } + return &witFilter{ + w: w, + i: i, + } +} + +type witFilter struct { + w *World + i *Interface +} + +func (*witFilter) WITKind() string { panic("BUG: WITKind called on filter") } +func (*witFilter) WIT(_ Node, _ string) string { panic("BUG: WIT called on filter") } + +// filter returns true if the supplied [Node] should be filtered out. +func (f *witFilter) filter(node Node) bool { + if f == nil { + return false // allow for nil receiver + } + return (f.w != nil && filterNode(f.w, node)) || (f.i != nil && filterNode(f.i, node)) +} + +func filterNode(target, node Node) bool { + switch node := node.(type) { + case *Package: + return !DependsOn(node, target) && !DependsOn(target, node) + case *World: + return !DependsOn(node, target) + } + return !DependsOn(target, node) +} + func indent(s string) string { const ws = "\t" return strings.ReplaceAll(strings.TrimSuffix(ws+strings.ReplaceAll(s, "\n", "\n"+ws), ws), ws+"\n", "\n") @@ -66,7 +135,7 @@ func (r *Resolve) WIT(ctx Node, _ string) string { var name string if i != 0 { // Write subsequent packages with explicit name, which renders the package WIT with nested braces. - name = p.Name.WIT(p, "") + name = p.Name.WIT(ctx, "") } wit := p.WIT(ctx, name) if len(packages) == 1 || strings.Count(wit, "\n") > 1 { @@ -180,13 +249,14 @@ func (*World) WITKind() string { return "world" } // // [WIT]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md func (w *World) WIT(ctx Node, name string) string { + filter, _ := ctx.(*witFilter) if name == "" { name = w.Name } var b strings.Builder - b.WriteString(w.Docs.WIT(ctx, "")) + b.WriteString(w.Docs.WIT(nil, "")) if w.Stability != nil { - b.WriteString(w.Stability.WIT(ctx, "")) + b.WriteString(w.Stability.WIT(nil, "")) b.WriteRune('\n') } b.WriteString("world ") @@ -194,6 +264,9 @@ func (w *World) WIT(ctx Node, name string) string { b.WriteString(" {") n := 0 w.Imports.All()(func(name string, i WorldItem) bool { + if filter.filter(i) { + return true + } if f, ok := i.(*Function); ok { if !f.IsFreestanding() { return true @@ -202,17 +275,18 @@ func (w *World) WIT(ctx Node, name string) string { if n == 0 { b.WriteRune('\n') } - // b.WriteString(indent(w.itemWIT("import", name, i))) b.WriteString(indent(i.WIT(worldImport{w}, name))) b.WriteRune('\n') n++ return true }) w.Exports.All()(func(name string, i WorldItem) bool { + if filter.filter(i) { + return true + } if n == 0 { b.WriteRune('\n') } - // b.WriteString(indent(w.itemWIT("export", name, i))) b.WriteString(indent(i.WIT(worldExport{w}, name))) b.WriteRune('\n') n++ @@ -227,16 +301,6 @@ type ( worldExport struct{ *World } ) -func (w *World) itemWIT(motion, name string, v WorldItem) string { - switch v := v.(type) { - case *InterfaceRef, *Function: - return motion + " " + v.WIT(w, name) // TODO: handle resource methods? - case *TypeDef: - return v.WIT(w, name) // no motion, in Imports only - } - panic("BUG: unknown WorldItem") -} - // WITKind returns the WIT kind. func (*InterfaceRef) WITKind() string { return "interface ref" } @@ -257,6 +321,8 @@ func (*Interface) WITKind() string { return "interface" } // // [WIT]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md func (i *Interface) WIT(ctx Node, name string) string { + // filter, _ := ctx.(*witFilter) + if i.Name != nil && name == "" { name = *i.Name } @@ -264,16 +330,6 @@ func (i *Interface) WIT(ctx Node, name string) string { var b strings.Builder switch ctx := ctx.(type) { - case *Package: - b.WriteString(i.Docs.WIT(ctx, "")) - if i.Stability != nil { - b.WriteString(i.Stability.WIT(ctx, "")) - b.WriteRune('\n') - } - b.WriteString("interface ") - b.WriteString(escape(name)) - b.WriteRune(' ') - case worldImport: rname := relativeName(i, ctx.Package) if rname != "" { @@ -297,6 +353,16 @@ func (i *Interface) WIT(ctx Node, name string) string { b.WriteString("export ") b.WriteString(escape(name)) b.WriteString(": interface ") + + default: // e.g. *Package + b.WriteString(i.Docs.WIT(ctx, "")) + if i.Stability != nil { + b.WriteString(i.Stability.WIT(ctx, "")) + b.WriteRune('\n') + } + b.WriteString("interface ") + b.WriteString(escape(name)) + b.WriteRune(' ') } b.WriteRune('{') @@ -304,6 +370,9 @@ func (i *Interface) WIT(ctx Node, name string) string { // Emit use statements first i.TypeDefs.All()(func(name string, td *TypeDef) bool { + // if filter.filter(td) { + // return true + // } if td.Root().Owner == td.Owner { return true // Skip declarations } @@ -318,6 +387,9 @@ func (i *Interface) WIT(ctx Node, name string) string { // Declarations i.TypeDefs.All()(func(name string, td *TypeDef) bool { + // if filter.filter(td) { + // return true + // } if td.Root().Owner != td.Owner { return true // Skip use statements } @@ -332,6 +404,9 @@ func (i *Interface) WIT(ctx Node, name string) string { // Functions i.Functions.All()(func(name string, f *Function) bool { + // if filter.filter(f) { + // return true + // } if !f.IsFreestanding() { return true } @@ -1044,10 +1119,7 @@ func (*Package) WITKind() string { return "package" } // // [WIT]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md func (p *Package) WIT(ctx Node, name string) string { - var filter *World - if w, ok := ctx.(*World); ok { - filter = w - } + filter, _ := ctx.(*witFilter) multi := name != "" var b strings.Builder b.WriteString(p.Docs.WIT(ctx, "")) @@ -1060,28 +1132,28 @@ func (p *Package) WIT(ctx Node, name string) string { } i := 0 p.Interfaces.All()(func(name string, face *Interface) bool { - if filter != nil && !filter.HasInterface(face) { + if filter.filter(face) { return true } b.WriteRune('\n') if multi { - b.WriteString(indent(face.WIT(p, name))) + b.WriteString(indent(face.WIT(ctx, name))) } else { - b.WriteString(face.WIT(p, name)) + b.WriteString(face.WIT(ctx, name)) } b.WriteRune('\n') i++ return true }) p.Worlds.All()(func(name string, w *World) bool { - if filter != nil && w != filter { + if filter.filter(w) { return true } b.WriteRune('\n') if multi { - b.WriteString(indent(w.WIT(p, name))) + b.WriteString(indent(w.WIT(ctx, name))) } else { - b.WriteString(w.WIT(p, name)) + b.WriteString(w.WIT(ctx, name)) } b.WriteRune('\n') i++ @@ -1103,7 +1175,7 @@ func (id *Ident) WITKind() string { return "ident" } // WIT returns the [WIT] text format of [Ident] id. // // [WIT]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md -func (id *Ident) WIT(ctx Node, _ string) string { +func (id *Ident) WIT(_ Node, _ string) string { ide := Ident{ Namespace: escape(id.Namespace), Package: escape(id.Package), diff --git a/wit/world.go b/wit/world.go new file mode 100644 index 00000000..aa4dd3e5 --- /dev/null +++ b/wit/world.go @@ -0,0 +1,148 @@ +package wit + +import ( + "go.bytecodealliance.org/wit/iterate" + "go.bytecodealliance.org/wit/ordered" +) + +// A World represents all of the imports and exports of a [WebAssembly component]. +// It implements the [Node] and [TypeOwner] interfaces. +// +// [WebAssembly component]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#wit-worlds +type World struct { + _typeOwner + + Name string + Imports ordered.Map[string, WorldItem] + Exports ordered.Map[string, WorldItem] + Package *Package // the Package this World belongs to (must be non-nil) + Stability Stability // WIT @since or @unstable (nil if unknown) + Docs Docs +} + +// Clone returns a shallow clone of w. +func (w *World) Clone() *World { + c := *w + c.Imports = *w.Imports.Clone() + c.Exports = *w.Exports.Clone() + return &c +} + +// WITPackage returns the [Package] that [World] w belongs to. +func (w *World) WITPackage() *Package { + return w.Package +} + +// Match returns true if [World] w matches pattern, which can be one of: +// "name", "namespace:package/name" (qualified), or "namespace:package/name@1.0.0" (versioned). +func (w *World) Match(pattern string) bool { + if pattern == w.Name { + return true + } + id := w.Package.Name + id.Extension = w.Name + if pattern == id.String() { + return true + } + id.Version = nil + return pattern == id.String() +} + +// HasInterface returns true if [World] w references [Interface] i. +func (w *World) HasInterface(i *Interface) bool { + var found bool + w.AllInterfaces()(func(_ string, face *Interface) bool { + found = face == i + return !found + }) + return found +} + +// AllInterfaces returns a [sequence] that yields each [Interface] in a [World]. +// The sequence stops if yield returns false. +// +// [sequence]: https://github.com/golang/go/issues/61897 +func (w *World) AllInterfaces() iterate.Seq2[string, *Interface] { + return func(yield func(string, *Interface) bool) { + w.AllItems()(func(name string, i WorldItem) bool { + if ref, ok := i.(*InterfaceRef); ok { + return yield(name, ref.Interface) + } + return true + }) + } +} + +// AllTypeDefs returns a [sequence] that yields each [TypeDef] in a [World]. +// The sequence stops if yield returns false. +// +// [sequence]: https://github.com/golang/go/issues/61897 +func (w *World) AllTypeDefs() iterate.Seq2[string, *TypeDef] { + return func(yield func(string, *TypeDef) bool) { + w.AllItems()(func(name string, i WorldItem) bool { + if t, ok := i.(*TypeDef); ok { + return yield(name, t) + } + return true + }) + } +} + +// AllFunctions returns a [sequence] that yields each [Function] in a [World]. +// The sequence stops if yield returns false. +// +// [sequence]: https://github.com/golang/go/issues/61897 +func (w *World) AllFunctions() iterate.Seq[*Function] { + return func(yield func(*Function) bool) { + w.AllItems()(func(_ string, i WorldItem) bool { + if f, ok := i.(*Function); ok { + return yield(f) + } + return true + }) + } +} + +// AllItems returns a [sequence] that yields each [WorldItem] in a [World]. +// The sequence stops if yield returns false. +// +// [sequence]: https://github.com/golang/go/issues/61897 +func (w *World) AllItems() iterate.Seq2[string, WorldItem] { + return func(yield func(string, WorldItem) bool) { + var done bool + yield = iterate.Done2(iterate.Once2(yield), func() { done = true }) + f := func(name string, i WorldItem) bool { + return yield(name, i) + } + w.Imports.All()(f) + if done { + return + } + w.Exports.All()(f) + } +} + +func (w *World) dependsOn(dep Node) bool { + if dep == w || dep == w.Package { + return true + } + var done bool + w.AllItems()(func(_ string, i WorldItem) bool { + done = DependsOn(i, dep) + return !done + }) + return done +} + +// A WorldItem is any item that can be exported from or imported into a [World], +// currently either an [InterfaceRef], [TypeDef], or [Function]. +// Any WorldItem is also a [Node]. +type WorldItem interface { + Node + isWorldItem() +} + +// _worldItem is an embeddable type that conforms to the [WorldItem] interface. +type _worldItem struct{} + +func (_worldItem) isWorldItem() {}