From 5c1b4fc39c9fede666398ad72c55aaa41be75301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B0=B8=E9=87=8E=E5=B3=BB=E8=BC=94?= Date: Mon, 18 May 2026 01:41:40 +0900 Subject: [PATCH 1/6] Add Component Model composite ValType wrappers: list/record/tuple/enum/flags Continues the type-hierarchy work from #282 by exposing payload accessors for the product/enum composite kinds: list, record, tuple, enum, flags. Each ships as a Go wrapper type with the C API's count/nth-style accessors (plus list/record's element/field types via independent ComponentValType clones, matching ComponentItem.TypeAlias()): - ComponentListType (Element) - ComponentRecordType (FieldCount / FieldNth) - ComponentTupleType (TypesCount / TypesNth) - ComponentEnumType (NamesCount / NamesNth) - ComponentFlagsType (NamesCount / NamesNth) ComponentValType gains a downcast method per kind (List, Record, Tuple, Enum, Flags) returning nil for any other kind. The corresponding five ComponentValTypeKind* constants -- commented out in #282 -- are now uncommented. Out of scope for this slice (per the roadmap split): variant / option / result (sum types, deferred to share a focused PR with their value marshaling), resource own/borrow, future/stream/error-context/map, and value-side marshaling for the kinds added here. Verified: go build ./..., go test ./..., go test -tags debug ./..., go vet ./..., gofmt -l . -- all clean. 14 new test functions. --- component_valtype_feat_component_model.go | 348 ++++++++++++++++-- ...onent_valtype_feat_component_model_test.go | 345 +++++++++++++++++ 2 files changed, 672 insertions(+), 21 deletions(-) create mode 100644 component_valtype_feat_component_model_test.go diff --git a/component_valtype_feat_component_model.go b/component_valtype_feat_component_model.go index 72907615..db0fd778 100644 --- a/component_valtype_feat_component_model.go +++ b/component_valtype_feat_component_model.go @@ -5,17 +5,31 @@ package wasmtime // static inline uint8_t go_component_valtype_kind(const wasmtime_component_valtype_t *vt) { // return vt->kind; // } +// static inline wasmtime_component_list_type_t *go_component_valtype_list(const wasmtime_component_valtype_t *vt) { +// return vt->of.list; +// } +// static inline wasmtime_component_record_type_t *go_component_valtype_record(const wasmtime_component_valtype_t *vt) { +// return vt->of.record; +// } +// static inline wasmtime_component_tuple_type_t *go_component_valtype_tuple(const wasmtime_component_valtype_t *vt) { +// return vt->of.tuple; +// } +// static inline wasmtime_component_enum_type_t *go_component_valtype_enum(const wasmtime_component_valtype_t *vt) { +// return vt->of.enum_; +// } +// static inline wasmtime_component_flags_type_t *go_component_valtype_flags(const wasmtime_component_valtype_t *vt) { +// return vt->of.flags; +// } import "C" import "runtime" // ComponentValTypeKind discriminates the WIT type that a [ComponentValType] -// represents. Only constants for the 13 primitive kinds (bool through -// string) are exposed in this release; constants for the composite kinds -// (list / record / tuple / variant / enum / option / result / flags / own / -// borrow / future / stream / error-context / map) are intentionally -// commented out below — they will be uncommented when each composite kind -// gets a dedicated payload accessor and a test path that exercises it. +// represents. This release exposes the 13 primitive kinds plus the +// product/enum composite kinds (list / record / tuple / enum / flags). +// Constants for the remaining composite kinds (variant / option / result / +// own / borrow / future / stream / error-context / map) are commented out +// and arrive in follow-up work along with their payload accessors. type ComponentValTypeKind uint8 const ( @@ -32,19 +46,19 @@ const ( ComponentValTypeKindF64 ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_F64 ComponentValTypeKindChar ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_CHAR ComponentValTypeKindString ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_STRING + ComponentValTypeKindList ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_LIST + ComponentValTypeKindRecord ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_RECORD + ComponentValTypeKindTuple ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_TUPLE + ComponentValTypeKindEnum ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_ENUM + ComponentValTypeKindFlags ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_FLAGS - // Composite-kind constants are deferred until each gets a payload - // accessor that returns the corresponding sub-type wrapper, with a - // test path. Uncomment as each one lands. + // Remaining composite-kind constants are deferred until each gets a + // payload accessor that returns the corresponding sub-type wrapper, + // with a test path. Uncomment as each one lands. // - // ComponentValTypeKindList ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_LIST - // ComponentValTypeKindRecord ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_RECORD - // ComponentValTypeKindTuple ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_TUPLE // ComponentValTypeKindVariant ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_VARIANT - // ComponentValTypeKindEnum ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_ENUM // ComponentValTypeKindOption ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_OPTION // ComponentValTypeKindResult ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_RESULT - // ComponentValTypeKindFlags ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_FLAGS // ComponentValTypeKindOwn ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_OWN // ComponentValTypeKindBorrow ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_BORROW // ComponentValTypeKindFuture ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_FUTURE @@ -54,13 +68,9 @@ const ( ) // ComponentValType describes the WIT type of a value in the component -// model. -// -// In this release [ComponentValType.Kind] returns one of the 13 primitive -// [ComponentValTypeKind] constants. If the underlying value type is a -// composite kind, the returned uint8 will not match any exposed constant -// — those, along with payload accessors that return their sub-type -// wrappers, arrive in follow-up work. +// model. Use [ComponentValType.Kind] to discriminate, then call the +// corresponding downcast method ([ComponentValType.List], +// [ComponentValType.Record], ...) for composite kinds. type ComponentValType struct { val C.wasmtime_component_valtype_t closed bool @@ -83,6 +93,62 @@ func (vt *ComponentValType) Kind() ComponentValTypeKind { return ComponentValTypeKind(C.go_component_valtype_kind(&vt.val)) } +// List returns the [ComponentListType] wrapper when this value type's kind +// is [ComponentValTypeKindList], or nil otherwise. The returned wrapper has +// an independent lifecycle from the parent. +func (vt *ComponentValType) List() *ComponentListType { + if vt.Kind() != ComponentValTypeKindList { + return nil + } + cloned := C.wasmtime_component_list_type_clone(C.go_component_valtype_list(&vt.val)) + runtime.KeepAlive(vt) + return mkComponentListType(cloned) +} + +// Record returns the [ComponentRecordType] wrapper when this value type's +// kind is [ComponentValTypeKindRecord], or nil otherwise. +func (vt *ComponentValType) Record() *ComponentRecordType { + if vt.Kind() != ComponentValTypeKindRecord { + return nil + } + cloned := C.wasmtime_component_record_type_clone(C.go_component_valtype_record(&vt.val)) + runtime.KeepAlive(vt) + return mkComponentRecordType(cloned) +} + +// Tuple returns the [ComponentTupleType] wrapper when this value type's +// kind is [ComponentValTypeKindTuple], or nil otherwise. +func (vt *ComponentValType) Tuple() *ComponentTupleType { + if vt.Kind() != ComponentValTypeKindTuple { + return nil + } + cloned := C.wasmtime_component_tuple_type_clone(C.go_component_valtype_tuple(&vt.val)) + runtime.KeepAlive(vt) + return mkComponentTupleType(cloned) +} + +// Enum returns the [ComponentEnumType] wrapper when this value type's kind +// is [ComponentValTypeKindEnum], or nil otherwise. +func (vt *ComponentValType) Enum() *ComponentEnumType { + if vt.Kind() != ComponentValTypeKindEnum { + return nil + } + cloned := C.wasmtime_component_enum_type_clone(C.go_component_valtype_enum(&vt.val)) + runtime.KeepAlive(vt) + return mkComponentEnumType(cloned) +} + +// Flags returns the [ComponentFlagsType] wrapper when this value type's +// kind is [ComponentValTypeKindFlags], or nil otherwise. +func (vt *ComponentValType) Flags() *ComponentFlagsType { + if vt.Kind() != ComponentValTypeKindFlags { + return nil + } + cloned := C.wasmtime_component_flags_type_clone(C.go_component_valtype_flags(&vt.val)) + runtime.KeepAlive(vt) + return mkComponentFlagsType(cloned) +} + // Close deallocates this value type explicitly. func (vt *ComponentValType) Close() { if vt.closed { @@ -92,3 +158,243 @@ func (vt *ComponentValType) Close() { C.wasmtime_component_valtype_delete(&vt.val) vt.closed = true } + +// ComponentListType is the payload of a `list` value type. +type ComponentListType struct { + _ptr *C.wasmtime_component_list_type_t +} + +func mkComponentListType(p *C.wasmtime_component_list_type_t) *ComponentListType { + lt := &ComponentListType{_ptr: p} + runtime.SetFinalizer(lt, func(lt *ComponentListType) { + lt.Close() + }) + return lt +} + +func (lt *ComponentListType) ptr() *C.wasmtime_component_list_type_t { + if lt._ptr == nil { + panic("object has been closed already") + } + maybeGC() + return lt._ptr +} + +// Element returns the element type of this list. +func (lt *ComponentListType) Element() *ComponentValType { + var out C.wasmtime_component_valtype_t + C.wasmtime_component_list_type_element(lt.ptr(), &out) + runtime.KeepAlive(lt) + return mkComponentValType(out) +} + +// Close deallocates this list type explicitly. +func (lt *ComponentListType) Close() { + if lt._ptr == nil { + return + } + runtime.SetFinalizer(lt, nil) + C.wasmtime_component_list_type_delete(lt._ptr) + lt._ptr = nil +} + +// ComponentRecordType is the payload of a `record { ... }` value type. +type ComponentRecordType struct { + _ptr *C.wasmtime_component_record_type_t +} + +func mkComponentRecordType(p *C.wasmtime_component_record_type_t) *ComponentRecordType { + rt := &ComponentRecordType{_ptr: p} + runtime.SetFinalizer(rt, func(rt *ComponentRecordType) { + rt.Close() + }) + return rt +} + +func (rt *ComponentRecordType) ptr() *C.wasmtime_component_record_type_t { + if rt._ptr == nil { + panic("object has been closed already") + } + maybeGC() + return rt._ptr +} + +// FieldCount returns the number of fields in this record. +func (rt *ComponentRecordType) FieldCount() int { + n := C.wasmtime_component_record_type_field_count(rt.ptr()) + runtime.KeepAlive(rt) + return int(n) +} + +// FieldNth returns the name and type of the `i`-th field, or `("", nil)` +// if `i` is out of range. +func (rt *ComponentRecordType) FieldNth(i int) (string, *ComponentValType) { + var nameP *C.char + var nameLen C.size_t + var out C.wasmtime_component_valtype_t + found := C.wasmtime_component_record_type_field_nth(rt.ptr(), C.size_t(i), &nameP, &nameLen, &out) + runtime.KeepAlive(rt) + if !bool(found) { + return "", nil + } + return C.GoStringN(nameP, C.int(nameLen)), mkComponentValType(out) +} + +// Close deallocates this record type explicitly. +func (rt *ComponentRecordType) Close() { + if rt._ptr == nil { + return + } + runtime.SetFinalizer(rt, nil) + C.wasmtime_component_record_type_delete(rt._ptr) + rt._ptr = nil +} + +// ComponentTupleType is the payload of a `tuple` value type. +type ComponentTupleType struct { + _ptr *C.wasmtime_component_tuple_type_t +} + +func mkComponentTupleType(p *C.wasmtime_component_tuple_type_t) *ComponentTupleType { + tt := &ComponentTupleType{_ptr: p} + runtime.SetFinalizer(tt, func(tt *ComponentTupleType) { + tt.Close() + }) + return tt +} + +func (tt *ComponentTupleType) ptr() *C.wasmtime_component_tuple_type_t { + if tt._ptr == nil { + panic("object has been closed already") + } + maybeGC() + return tt._ptr +} + +// TypesCount returns the number of types in this tuple. +func (tt *ComponentTupleType) TypesCount() int { + n := C.wasmtime_component_tuple_type_types_count(tt.ptr()) + runtime.KeepAlive(tt) + return int(n) +} + +// TypesNth returns the type at position `i`, or nil if `i` is out of range. +func (tt *ComponentTupleType) TypesNth(i int) *ComponentValType { + var out C.wasmtime_component_valtype_t + found := C.wasmtime_component_tuple_type_types_nth(tt.ptr(), C.size_t(i), &out) + runtime.KeepAlive(tt) + if !bool(found) { + return nil + } + return mkComponentValType(out) +} + +// Close deallocates this tuple type explicitly. +func (tt *ComponentTupleType) Close() { + if tt._ptr == nil { + return + } + runtime.SetFinalizer(tt, nil) + C.wasmtime_component_tuple_type_delete(tt._ptr) + tt._ptr = nil +} + +// ComponentEnumType is the payload of an `enum "a" "b" ...` value type. +type ComponentEnumType struct { + _ptr *C.wasmtime_component_enum_type_t +} + +func mkComponentEnumType(p *C.wasmtime_component_enum_type_t) *ComponentEnumType { + et := &ComponentEnumType{_ptr: p} + runtime.SetFinalizer(et, func(et *ComponentEnumType) { + et.Close() + }) + return et +} + +func (et *ComponentEnumType) ptr() *C.wasmtime_component_enum_type_t { + if et._ptr == nil { + panic("object has been closed already") + } + maybeGC() + return et._ptr +} + +// NamesCount returns the number of cases in this enum. +func (et *ComponentEnumType) NamesCount() int { + n := C.wasmtime_component_enum_type_names_count(et.ptr()) + runtime.KeepAlive(et) + return int(n) +} + +// NamesNth returns the name of the `i`-th case, or "" if `i` is out of range. +func (et *ComponentEnumType) NamesNth(i int) string { + var nameP *C.char + var nameLen C.size_t + found := C.wasmtime_component_enum_type_names_nth(et.ptr(), C.size_t(i), &nameP, &nameLen) + runtime.KeepAlive(et) + if !bool(found) { + return "" + } + return C.GoStringN(nameP, C.int(nameLen)) +} + +// Close deallocates this enum type explicitly. +func (et *ComponentEnumType) Close() { + if et._ptr == nil { + return + } + runtime.SetFinalizer(et, nil) + C.wasmtime_component_enum_type_delete(et._ptr) + et._ptr = nil +} + +// ComponentFlagsType is the payload of a `flags "a" "b" ...` value type. +type ComponentFlagsType struct { + _ptr *C.wasmtime_component_flags_type_t +} + +func mkComponentFlagsType(p *C.wasmtime_component_flags_type_t) *ComponentFlagsType { + ft := &ComponentFlagsType{_ptr: p} + runtime.SetFinalizer(ft, func(ft *ComponentFlagsType) { + ft.Close() + }) + return ft +} + +func (ft *ComponentFlagsType) ptr() *C.wasmtime_component_flags_type_t { + if ft._ptr == nil { + panic("object has been closed already") + } + maybeGC() + return ft._ptr +} + +// NamesCount returns the number of flag names in this flags type. +func (ft *ComponentFlagsType) NamesCount() int { + n := C.wasmtime_component_flags_type_names_count(ft.ptr()) + runtime.KeepAlive(ft) + return int(n) +} + +// NamesNth returns the name of the `i`-th flag, or "" if `i` is out of range. +func (ft *ComponentFlagsType) NamesNth(i int) string { + var nameP *C.char + var nameLen C.size_t + found := C.wasmtime_component_flags_type_names_nth(ft.ptr(), C.size_t(i), &nameP, &nameLen) + runtime.KeepAlive(ft) + if !bool(found) { + return "" + } + return C.GoStringN(nameP, C.int(nameLen)) +} + +// Close deallocates this flags type explicitly. +func (ft *ComponentFlagsType) Close() { + if ft._ptr == nil { + return + } + runtime.SetFinalizer(ft, nil) + C.wasmtime_component_flags_type_delete(ft._ptr) + ft._ptr = nil +} diff --git a/component_valtype_feat_component_model_test.go b/component_valtype_feat_component_model_test.go new file mode 100644 index 00000000..36e0075f --- /dev/null +++ b/component_valtype_feat_component_model_test.go @@ -0,0 +1,345 @@ +package wasmtime + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestComponentValTypeList(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component + (type $l (list u32)) + (export "l" (type $l)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + require.NotNil(t, item) + defer item.Close() + + vt := item.TypeAlias() + require.NotNil(t, vt) + defer vt.Close() + require.Equal(t, ComponentValTypeKindList, vt.Kind()) + + lt := vt.List() + require.NotNil(t, lt) + defer lt.Close() + + elem := lt.Element() + require.NotNil(t, elem) + defer elem.Close() + require.Equal(t, ComponentValTypeKindU32, elem.Kind()) +} + +func TestComponentValTypeListOnNonListReturnsNil(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + require.Nil(t, vt.List()) +} + +func TestComponentValTypeRecord(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component + (type $r (record (field "age" u32) (field "name" string))) + (export "r" (type $r)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + require.NotNil(t, item) + defer item.Close() + + vt := item.TypeAlias() + require.NotNil(t, vt) + defer vt.Close() + require.Equal(t, ComponentValTypeKindRecord, vt.Kind()) + + rt := vt.Record() + require.NotNil(t, rt) + defer rt.Close() + require.Equal(t, 2, rt.FieldCount()) + + name0, ty0 := rt.FieldNth(0) + require.Equal(t, "age", name0) + require.NotNil(t, ty0) + defer ty0.Close() + require.Equal(t, ComponentValTypeKindU32, ty0.Kind()) + + name1, ty1 := rt.FieldNth(1) + require.Equal(t, "name", name1) + require.NotNil(t, ty1) + defer ty1.Close() + require.Equal(t, ComponentValTypeKindString, ty1.Kind()) +} + +func TestComponentValTypeRecordFieldNthOutOfRange(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component + (type $r (record (field "x" u32))) + (export "r" (type $r)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + rt := vt.Record() + defer rt.Close() + + name, ty := rt.FieldNth(1) + require.Equal(t, "", name) + require.Nil(t, ty) +} + +func TestComponentValTypeRecordOnNonRecordReturnsNil(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + require.Nil(t, vt.Record()) +} + +func TestComponentValTypeTuple(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component + (type $t (tuple bool s32 string)) + (export "t" (type $t)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + require.Equal(t, ComponentValTypeKindTuple, vt.Kind()) + + tt := vt.Tuple() + require.NotNil(t, tt) + defer tt.Close() + require.Equal(t, 3, tt.TypesCount()) + + ty0 := tt.TypesNth(0) + require.NotNil(t, ty0) + defer ty0.Close() + require.Equal(t, ComponentValTypeKindBool, ty0.Kind()) + + ty1 := tt.TypesNth(1) + require.NotNil(t, ty1) + defer ty1.Close() + require.Equal(t, ComponentValTypeKindS32, ty1.Kind()) + + ty2 := tt.TypesNth(2) + require.NotNil(t, ty2) + defer ty2.Close() + require.Equal(t, ComponentValTypeKindString, ty2.Kind()) +} + +func TestComponentValTypeTupleTypesNthOutOfRange(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component + (type $t (tuple bool)) + (export "t" (type $t)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + tt := vt.Tuple() + defer tt.Close() + + require.Nil(t, tt.TypesNth(1)) +} + +func TestComponentValTypeTupleOnNonTupleReturnsNil(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + require.Nil(t, vt.Tuple()) +} + +func TestComponentValTypeEnum(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component + (type $e (enum "red" "green" "blue")) + (export "e" (type $e)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + require.Equal(t, ComponentValTypeKindEnum, vt.Kind()) + + et := vt.Enum() + require.NotNil(t, et) + defer et.Close() + require.Equal(t, 3, et.NamesCount()) + require.Equal(t, "red", et.NamesNth(0)) + require.Equal(t, "green", et.NamesNth(1)) + require.Equal(t, "blue", et.NamesNth(2)) +} + +func TestComponentValTypeEnumNamesNthOutOfRange(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component + (type $e (enum "only")) + (export "e" (type $e)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + et := vt.Enum() + defer et.Close() + + require.Equal(t, "", et.NamesNth(1)) +} + +func TestComponentValTypeEnumOnNonEnumReturnsNil(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + require.Nil(t, vt.Enum()) +} + +func TestComponentValTypeFlags(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component + (type $f (flags "read" "write" "exec")) + (export "f" (type $f)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + require.Equal(t, ComponentValTypeKindFlags, vt.Kind()) + + ft := vt.Flags() + require.NotNil(t, ft) + defer ft.Close() + require.Equal(t, 3, ft.NamesCount()) + require.Equal(t, "read", ft.NamesNth(0)) + require.Equal(t, "write", ft.NamesNth(1)) + require.Equal(t, "exec", ft.NamesNth(2)) +} + +func TestComponentValTypeFlagsNamesNthOutOfRange(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component + (type $f (flags "only")) + (export "f" (type $f)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + ft := vt.Flags() + defer ft.Close() + + require.Equal(t, "", ft.NamesNth(1)) +} + +func TestComponentValTypeFlagsOnNonFlagsReturnsNil(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) + require.NoError(t, err) + component, err := NewComponent(engine, wasm) + require.NoError(t, err) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + require.Nil(t, vt.Flags()) +} From 0984a217dd1cd321be0a7ee8c5cb9fed9c1bc646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B0=B8=E9=87=8E=E5=B3=BB=E8=BC=94?= Date: Mon, 18 May 2026 02:31:02 +0900 Subject: [PATCH 2/6] Rename composite-type accessors to singular form for consistency Aligns the Tuple/Enum/Flags accessors with the singular noun + Count/Nth pattern already established by ComponentType.{Import,Export}{Count,Nth} and the record-side FieldCount/FieldNth added in this slice: - ComponentTupleType: TypesCount/TypesNth -> TypeCount/TypeNth - ComponentEnumType: NamesCount/NamesNth -> CaseCount/CaseNth - ComponentFlagsType: NamesCount/NamesNth -> FlagCount/FlagNth CaseNth/FlagNth also pick the WIT-domain term (enum "cases", flags "flags") over the C API's generic "_names_nth", matching how FieldNth uses the WIT term "field". --- component_valtype_feat_component_model.go | 24 +++++++-------- ...onent_valtype_feat_component_model_test.go | 30 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/component_valtype_feat_component_model.go b/component_valtype_feat_component_model.go index db0fd778..cf195654 100644 --- a/component_valtype_feat_component_model.go +++ b/component_valtype_feat_component_model.go @@ -271,15 +271,15 @@ func (tt *ComponentTupleType) ptr() *C.wasmtime_component_tuple_type_t { return tt._ptr } -// TypesCount returns the number of types in this tuple. -func (tt *ComponentTupleType) TypesCount() int { +// TypeCount returns the number of types in this tuple. +func (tt *ComponentTupleType) TypeCount() int { n := C.wasmtime_component_tuple_type_types_count(tt.ptr()) runtime.KeepAlive(tt) return int(n) } -// TypesNth returns the type at position `i`, or nil if `i` is out of range. -func (tt *ComponentTupleType) TypesNth(i int) *ComponentValType { +// TypeNth returns the type at position `i`, or nil if `i` is out of range. +func (tt *ComponentTupleType) TypeNth(i int) *ComponentValType { var out C.wasmtime_component_valtype_t found := C.wasmtime_component_tuple_type_types_nth(tt.ptr(), C.size_t(i), &out) runtime.KeepAlive(tt) @@ -320,15 +320,15 @@ func (et *ComponentEnumType) ptr() *C.wasmtime_component_enum_type_t { return et._ptr } -// NamesCount returns the number of cases in this enum. -func (et *ComponentEnumType) NamesCount() int { +// CaseCount returns the number of cases in this enum. +func (et *ComponentEnumType) CaseCount() int { n := C.wasmtime_component_enum_type_names_count(et.ptr()) runtime.KeepAlive(et) return int(n) } -// NamesNth returns the name of the `i`-th case, or "" if `i` is out of range. -func (et *ComponentEnumType) NamesNth(i int) string { +// CaseNth returns the name of the `i`-th case, or "" if `i` is out of range. +func (et *ComponentEnumType) CaseNth(i int) string { var nameP *C.char var nameLen C.size_t found := C.wasmtime_component_enum_type_names_nth(et.ptr(), C.size_t(i), &nameP, &nameLen) @@ -370,15 +370,15 @@ func (ft *ComponentFlagsType) ptr() *C.wasmtime_component_flags_type_t { return ft._ptr } -// NamesCount returns the number of flag names in this flags type. -func (ft *ComponentFlagsType) NamesCount() int { +// FlagCount returns the number of flags in this flags type. +func (ft *ComponentFlagsType) FlagCount() int { n := C.wasmtime_component_flags_type_names_count(ft.ptr()) runtime.KeepAlive(ft) return int(n) } -// NamesNth returns the name of the `i`-th flag, or "" if `i` is out of range. -func (ft *ComponentFlagsType) NamesNth(i int) string { +// FlagNth returns the name of the `i`-th flag, or "" if `i` is out of range. +func (ft *ComponentFlagsType) FlagNth(i int) string { var nameP *C.char var nameLen C.size_t found := C.wasmtime_component_flags_type_names_nth(ft.ptr(), C.size_t(i), &nameP, &nameLen) diff --git a/component_valtype_feat_component_model_test.go b/component_valtype_feat_component_model_test.go index 36e0075f..c0d4f5ed 100644 --- a/component_valtype_feat_component_model_test.go +++ b/component_valtype_feat_component_model_test.go @@ -155,19 +155,19 @@ func TestComponentValTypeTuple(t *testing.T) { tt := vt.Tuple() require.NotNil(t, tt) defer tt.Close() - require.Equal(t, 3, tt.TypesCount()) + require.Equal(t, 3, tt.TypeCount()) - ty0 := tt.TypesNth(0) + ty0 := tt.TypeNth(0) require.NotNil(t, ty0) defer ty0.Close() require.Equal(t, ComponentValTypeKindBool, ty0.Kind()) - ty1 := tt.TypesNth(1) + ty1 := tt.TypeNth(1) require.NotNil(t, ty1) defer ty1.Close() require.Equal(t, ComponentValTypeKindS32, ty1.Kind()) - ty2 := tt.TypesNth(2) + ty2 := tt.TypeNth(2) require.NotNil(t, ty2) defer ty2.Close() require.Equal(t, ComponentValTypeKindString, ty2.Kind()) @@ -192,7 +192,7 @@ func TestComponentValTypeTupleTypesNthOutOfRange(t *testing.T) { tt := vt.Tuple() defer tt.Close() - require.Nil(t, tt.TypesNth(1)) + require.Nil(t, tt.TypeNth(1)) } func TestComponentValTypeTupleOnNonTupleReturnsNil(t *testing.T) { @@ -233,10 +233,10 @@ func TestComponentValTypeEnum(t *testing.T) { et := vt.Enum() require.NotNil(t, et) defer et.Close() - require.Equal(t, 3, et.NamesCount()) - require.Equal(t, "red", et.NamesNth(0)) - require.Equal(t, "green", et.NamesNth(1)) - require.Equal(t, "blue", et.NamesNth(2)) + require.Equal(t, 3, et.CaseCount()) + require.Equal(t, "red", et.CaseNth(0)) + require.Equal(t, "green", et.CaseNth(1)) + require.Equal(t, "blue", et.CaseNth(2)) } func TestComponentValTypeEnumNamesNthOutOfRange(t *testing.T) { @@ -258,7 +258,7 @@ func TestComponentValTypeEnumNamesNthOutOfRange(t *testing.T) { et := vt.Enum() defer et.Close() - require.Equal(t, "", et.NamesNth(1)) + require.Equal(t, "", et.CaseNth(1)) } func TestComponentValTypeEnumOnNonEnumReturnsNil(t *testing.T) { @@ -299,10 +299,10 @@ func TestComponentValTypeFlags(t *testing.T) { ft := vt.Flags() require.NotNil(t, ft) defer ft.Close() - require.Equal(t, 3, ft.NamesCount()) - require.Equal(t, "read", ft.NamesNth(0)) - require.Equal(t, "write", ft.NamesNth(1)) - require.Equal(t, "exec", ft.NamesNth(2)) + require.Equal(t, 3, ft.FlagCount()) + require.Equal(t, "read", ft.FlagNth(0)) + require.Equal(t, "write", ft.FlagNth(1)) + require.Equal(t, "exec", ft.FlagNth(2)) } func TestComponentValTypeFlagsNamesNthOutOfRange(t *testing.T) { @@ -324,7 +324,7 @@ func TestComponentValTypeFlagsNamesNthOutOfRange(t *testing.T) { ft := vt.Flags() defer ft.Close() - require.Equal(t, "", ft.NamesNth(1)) + require.Equal(t, "", ft.FlagNth(1)) } func TestComponentValTypeFlagsOnNonFlagsReturnsNil(t *testing.T) { From 0ddf7c6c5c0517e5fe6382d645d1a6cf0cc45f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B0=B8=E9=87=8E=E5=B3=BB=E8=BC=94?= Date: Mon, 18 May 2026 18:52:44 +0900 Subject: [PATCH 3/6] Rename composite-type tests to match existing variation naming Two adjustments to align with the test names established in component_type_feat_component_model_test.go: 1. Fix stale method names left from the previous rename commit (TupleType.TypesNth -> TypeNth, EnumType.NamesNth -> CaseNth, FlagsType.NamesNth -> FlagNth): - TupleTypesNthOutOfRange -> TupleTypeNthOutOfRange - EnumNamesNthOutOfRange -> EnumCaseNthOutOfRange - FlagsNamesNthOutOfRange -> FlagsFlagNthOutOfRange 2. Switch the wrong-kind tests from "OnNonReturnsNil" to "ReturnsNilForNonKind", matching the only existing precedent (TestComponentItemTypeAliasReturnsNilForNonTypeKind): - ListOnNonListReturnsNil -> ListReturnsNilForNonListKind - RecordOnNonRecordReturnsNil -> RecordReturnsNilForNonRecordKind - TupleOnNonTupleReturnsNil -> TupleReturnsNilForNonTupleKind - EnumOnNonEnumReturnsNil -> EnumReturnsNilForNonEnumKind - FlagsOnNonFlagsReturnsNil -> FlagsReturnsNilForNonFlagsKind --- component_valtype_feat_component_model_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/component_valtype_feat_component_model_test.go b/component_valtype_feat_component_model_test.go index c0d4f5ed..fea374e6 100644 --- a/component_valtype_feat_component_model_test.go +++ b/component_valtype_feat_component_model_test.go @@ -37,7 +37,7 @@ func TestComponentValTypeList(t *testing.T) { require.Equal(t, ComponentValTypeKindU32, elem.Kind()) } -func TestComponentValTypeListOnNonListReturnsNil(t *testing.T) { +func TestComponentValTypeListReturnsNilForNonListKind(t *testing.T) { engine := newComponentEngine() wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) require.NoError(t, err) @@ -117,7 +117,7 @@ func TestComponentValTypeRecordFieldNthOutOfRange(t *testing.T) { require.Nil(t, ty) } -func TestComponentValTypeRecordOnNonRecordReturnsNil(t *testing.T) { +func TestComponentValTypeRecordReturnsNilForNonRecordKind(t *testing.T) { engine := newComponentEngine() wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) require.NoError(t, err) @@ -173,7 +173,7 @@ func TestComponentValTypeTuple(t *testing.T) { require.Equal(t, ComponentValTypeKindString, ty2.Kind()) } -func TestComponentValTypeTupleTypesNthOutOfRange(t *testing.T) { +func TestComponentValTypeTupleTypeNthOutOfRange(t *testing.T) { engine := newComponentEngine() wasm, err := Wat2Wasm(`(component (type $t (tuple bool)) @@ -195,7 +195,7 @@ func TestComponentValTypeTupleTypesNthOutOfRange(t *testing.T) { require.Nil(t, tt.TypeNth(1)) } -func TestComponentValTypeTupleOnNonTupleReturnsNil(t *testing.T) { +func TestComponentValTypeTupleReturnsNilForNonTupleKind(t *testing.T) { engine := newComponentEngine() wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) require.NoError(t, err) @@ -239,7 +239,7 @@ func TestComponentValTypeEnum(t *testing.T) { require.Equal(t, "blue", et.CaseNth(2)) } -func TestComponentValTypeEnumNamesNthOutOfRange(t *testing.T) { +func TestComponentValTypeEnumCaseNthOutOfRange(t *testing.T) { engine := newComponentEngine() wasm, err := Wat2Wasm(`(component (type $e (enum "only")) @@ -261,7 +261,7 @@ func TestComponentValTypeEnumNamesNthOutOfRange(t *testing.T) { require.Equal(t, "", et.CaseNth(1)) } -func TestComponentValTypeEnumOnNonEnumReturnsNil(t *testing.T) { +func TestComponentValTypeEnumReturnsNilForNonEnumKind(t *testing.T) { engine := newComponentEngine() wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) require.NoError(t, err) @@ -305,7 +305,7 @@ func TestComponentValTypeFlags(t *testing.T) { require.Equal(t, "exec", ft.FlagNth(2)) } -func TestComponentValTypeFlagsNamesNthOutOfRange(t *testing.T) { +func TestComponentValTypeFlagsFlagNthOutOfRange(t *testing.T) { engine := newComponentEngine() wasm, err := Wat2Wasm(`(component (type $f (flags "only")) @@ -327,7 +327,7 @@ func TestComponentValTypeFlagsNamesNthOutOfRange(t *testing.T) { require.Equal(t, "", ft.FlagNth(1)) } -func TestComponentValTypeFlagsOnNonFlagsReturnsNil(t *testing.T) { +func TestComponentValTypeFlagsReturnsNilForNonFlagsKind(t *testing.T) { engine := newComponentEngine() wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) require.NoError(t, err) From f85bfa41b0f3542261e9d5b250ce8ab31a2342dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B0=B8=E9=87=8E=E5=B3=BB=E8=BC=94?= Date: Mon, 18 May 2026 19:24:56 +0900 Subject: [PATCH 4/6] Consolidate composite-type tests to match pre-existing wasmtime-go style Pre-existing wasmtime-go test files (e.g. functype_test.go, importtype_test.go, valtype_test.go) follow a "one test per type, multiple inline assertions" pattern. The previous V1 layout (14 tests split by kind x variation axis with OutOfRange / ReturnsNilForNonKind suffixes) was modeled on the recently-merged #282 component tests, which have diverged from that pre-existing canonical style. Collapse to 5 tests, one per composite kind. Each test covers happy path + index-out-of-range (where applicable) + wrong-kind-returns-nil inline. Net: 224 -> 65 lines. --- ...onent_valtype_feat_component_model_test.go | 224 +++++------------- 1 file changed, 65 insertions(+), 159 deletions(-) diff --git a/component_valtype_feat_component_model_test.go b/component_valtype_feat_component_model_test.go index fea374e6..e9809a27 100644 --- a/component_valtype_feat_component_model_test.go +++ b/component_valtype_feat_component_model_test.go @@ -35,23 +35,20 @@ func TestComponentValTypeList(t *testing.T) { require.NotNil(t, elem) defer elem.Close() require.Equal(t, ComponentValTypeKindU32, elem.Kind()) -} -func TestComponentValTypeListReturnsNilForNonListKind(t *testing.T) { - engine := newComponentEngine() - wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) + // List() on a non-list kind returns nil. + wasmU32, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) require.NoError(t, err) - component, err := NewComponent(engine, wasm) + comp2, err := NewComponent(engine, wasmU32) require.NoError(t, err) - defer component.Close() - - ct := component.Type() - defer ct.Close() - _, item := ct.ExportNth(0) - defer item.Close() - vt := item.TypeAlias() - defer vt.Close() - require.Nil(t, vt.List()) + defer comp2.Close() + ct2 := comp2.Type() + defer ct2.Close() + _, item2 := ct2.ExportNth(0) + defer item2.Close() + vt2 := item2.TypeAlias() + defer vt2.Close() + require.Nil(t, vt2.List()) } func TestComponentValTypeRecord(t *testing.T) { @@ -67,11 +64,8 @@ func TestComponentValTypeRecord(t *testing.T) { ct := component.Type() defer ct.Close() _, item := ct.ExportNth(0) - require.NotNil(t, item) defer item.Close() - vt := item.TypeAlias() - require.NotNil(t, vt) defer vt.Close() require.Equal(t, ComponentValTypeKindRecord, vt.Kind()) @@ -91,47 +85,25 @@ func TestComponentValTypeRecord(t *testing.T) { require.NotNil(t, ty1) defer ty1.Close() require.Equal(t, ComponentValTypeKindString, ty1.Kind()) -} -func TestComponentValTypeRecordFieldNthOutOfRange(t *testing.T) { - engine := newComponentEngine() - wasm, err := Wat2Wasm(`(component - (type $r (record (field "x" u32))) - (export "r" (type $r)))`) - require.NoError(t, err) - component, err := NewComponent(engine, wasm) - require.NoError(t, err) - defer component.Close() - - ct := component.Type() - defer ct.Close() - _, item := ct.ExportNth(0) - defer item.Close() - vt := item.TypeAlias() - defer vt.Close() - rt := vt.Record() - defer rt.Close() - - name, ty := rt.FieldNth(1) - require.Equal(t, "", name) - require.Nil(t, ty) -} + // FieldNth out of range returns ("", nil). + name2, ty2 := rt.FieldNth(2) + require.Equal(t, "", name2) + require.Nil(t, ty2) -func TestComponentValTypeRecordReturnsNilForNonRecordKind(t *testing.T) { - engine := newComponentEngine() - wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) + // Record() on a non-record kind returns nil. + wasmU32, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) require.NoError(t, err) - component, err := NewComponent(engine, wasm) + comp2, err := NewComponent(engine, wasmU32) require.NoError(t, err) - defer component.Close() - - ct := component.Type() - defer ct.Close() - _, item := ct.ExportNth(0) - defer item.Close() - vt := item.TypeAlias() - defer vt.Close() - require.Nil(t, vt.Record()) + defer comp2.Close() + ct2 := comp2.Type() + defer ct2.Close() + _, item2 := ct2.ExportNth(0) + defer item2.Close() + vt2 := item2.TypeAlias() + defer vt2.Close() + require.Nil(t, vt2.Record()) } func TestComponentValTypeTuple(t *testing.T) { @@ -171,45 +143,23 @@ func TestComponentValTypeTuple(t *testing.T) { require.NotNil(t, ty2) defer ty2.Close() require.Equal(t, ComponentValTypeKindString, ty2.Kind()) -} - -func TestComponentValTypeTupleTypeNthOutOfRange(t *testing.T) { - engine := newComponentEngine() - wasm, err := Wat2Wasm(`(component - (type $t (tuple bool)) - (export "t" (type $t)))`) - require.NoError(t, err) - component, err := NewComponent(engine, wasm) - require.NoError(t, err) - defer component.Close() - - ct := component.Type() - defer ct.Close() - _, item := ct.ExportNth(0) - defer item.Close() - vt := item.TypeAlias() - defer vt.Close() - tt := vt.Tuple() - defer tt.Close() - require.Nil(t, tt.TypeNth(1)) -} + // TypeNth out of range returns nil. + require.Nil(t, tt.TypeNth(3)) -func TestComponentValTypeTupleReturnsNilForNonTupleKind(t *testing.T) { - engine := newComponentEngine() - wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) + // Tuple() on a non-tuple kind returns nil. + wasmU32, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) require.NoError(t, err) - component, err := NewComponent(engine, wasm) + comp2, err := NewComponent(engine, wasmU32) require.NoError(t, err) - defer component.Close() - - ct := component.Type() - defer ct.Close() - _, item := ct.ExportNth(0) - defer item.Close() - vt := item.TypeAlias() - defer vt.Close() - require.Nil(t, vt.Tuple()) + defer comp2.Close() + ct2 := comp2.Type() + defer ct2.Close() + _, item2 := ct2.ExportNth(0) + defer item2.Close() + vt2 := item2.TypeAlias() + defer vt2.Close() + require.Nil(t, vt2.Tuple()) } func TestComponentValTypeEnum(t *testing.T) { @@ -237,45 +187,23 @@ func TestComponentValTypeEnum(t *testing.T) { require.Equal(t, "red", et.CaseNth(0)) require.Equal(t, "green", et.CaseNth(1)) require.Equal(t, "blue", et.CaseNth(2)) -} -func TestComponentValTypeEnumCaseNthOutOfRange(t *testing.T) { - engine := newComponentEngine() - wasm, err := Wat2Wasm(`(component - (type $e (enum "only")) - (export "e" (type $e)))`) - require.NoError(t, err) - component, err := NewComponent(engine, wasm) - require.NoError(t, err) - defer component.Close() + // CaseNth out of range returns "". + require.Equal(t, "", et.CaseNth(3)) - ct := component.Type() - defer ct.Close() - _, item := ct.ExportNth(0) - defer item.Close() - vt := item.TypeAlias() - defer vt.Close() - et := vt.Enum() - defer et.Close() - - require.Equal(t, "", et.CaseNth(1)) -} - -func TestComponentValTypeEnumReturnsNilForNonEnumKind(t *testing.T) { - engine := newComponentEngine() - wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) + // Enum() on a non-enum kind returns nil. + wasmU32, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) require.NoError(t, err) - component, err := NewComponent(engine, wasm) + comp2, err := NewComponent(engine, wasmU32) require.NoError(t, err) - defer component.Close() - - ct := component.Type() - defer ct.Close() - _, item := ct.ExportNth(0) - defer item.Close() - vt := item.TypeAlias() - defer vt.Close() - require.Nil(t, vt.Enum()) + defer comp2.Close() + ct2 := comp2.Type() + defer ct2.Close() + _, item2 := ct2.ExportNth(0) + defer item2.Close() + vt2 := item2.TypeAlias() + defer vt2.Close() + require.Nil(t, vt2.Enum()) } func TestComponentValTypeFlags(t *testing.T) { @@ -303,43 +231,21 @@ func TestComponentValTypeFlags(t *testing.T) { require.Equal(t, "read", ft.FlagNth(0)) require.Equal(t, "write", ft.FlagNth(1)) require.Equal(t, "exec", ft.FlagNth(2)) -} - -func TestComponentValTypeFlagsFlagNthOutOfRange(t *testing.T) { - engine := newComponentEngine() - wasm, err := Wat2Wasm(`(component - (type $f (flags "only")) - (export "f" (type $f)))`) - require.NoError(t, err) - component, err := NewComponent(engine, wasm) - require.NoError(t, err) - defer component.Close() - - ct := component.Type() - defer ct.Close() - _, item := ct.ExportNth(0) - defer item.Close() - vt := item.TypeAlias() - defer vt.Close() - ft := vt.Flags() - defer ft.Close() - require.Equal(t, "", ft.FlagNth(1)) -} + // FlagNth out of range returns "". + require.Equal(t, "", ft.FlagNth(3)) -func TestComponentValTypeFlagsReturnsNilForNonFlagsKind(t *testing.T) { - engine := newComponentEngine() - wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) + // Flags() on a non-flags kind returns nil. + wasmU32, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) require.NoError(t, err) - component, err := NewComponent(engine, wasm) + comp2, err := NewComponent(engine, wasmU32) require.NoError(t, err) - defer component.Close() - - ct := component.Type() - defer ct.Close() - _, item := ct.ExportNth(0) - defer item.Close() - vt := item.TypeAlias() - defer vt.Close() - require.Nil(t, vt.Flags()) + defer comp2.Close() + ct2 := comp2.Type() + defer ct2.Close() + _, item2 := ct2.ExportNth(0) + defer item2.Close() + vt2 := item2.TypeAlias() + defer vt2.Close() + require.Nil(t, vt2.Flags()) } From 12cd8cd53adafd783e58135610f1a1b1929fcda3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B0=B8=E9=87=8E=E5=B3=BB=E8=BC=94?= Date: Tue, 19 May 2026 09:31:27 +0900 Subject: [PATCH 5/6] Tighten composite ValType doc comments and dedupe downcast nil probes - ComponentValTypeKind doc: drop the slice-scope narrative; the kind set is already self-documenting from the constants below. - Commented-out-kinds note: collapse to one line; the rationale lives in the PR description, not in the source. - Downcast lifecycle note: move the "independently-owned wrapper" remark from ComponentValType.List's per-method comment up to the type-level doc, since it applies to every downcast (List/Record/Tuple/Enum/Flags). - Tests: replace the five copy-pasted "X() returns nil on a non-X kind" blocks with a single TestComponentValTypeDowncastNilForOtherKinds that probes all five downcasts against a u32 type alias. --- component_valtype_feat_component_model.go | 17 ++-- ...onent_valtype_feat_component_model_test.go | 93 ++++++------------- 2 files changed, 32 insertions(+), 78 deletions(-) diff --git a/component_valtype_feat_component_model.go b/component_valtype_feat_component_model.go index cf195654..b7b3f65b 100644 --- a/component_valtype_feat_component_model.go +++ b/component_valtype_feat_component_model.go @@ -25,11 +25,7 @@ import "C" import "runtime" // ComponentValTypeKind discriminates the WIT type that a [ComponentValType] -// represents. This release exposes the 13 primitive kinds plus the -// product/enum composite kinds (list / record / tuple / enum / flags). -// Constants for the remaining composite kinds (variant / option / result / -// own / borrow / future / stream / error-context / map) are commented out -// and arrive in follow-up work along with their payload accessors. +// represents. type ComponentValTypeKind uint8 const ( @@ -52,9 +48,7 @@ const ( ComponentValTypeKindEnum ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_ENUM ComponentValTypeKindFlags ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_FLAGS - // Remaining composite-kind constants are deferred until each gets a - // payload accessor that returns the corresponding sub-type wrapper, - // with a test path. Uncomment as each one lands. + // Remaining composite kinds: each lands with its payload accessor. // // ComponentValTypeKindVariant ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_VARIANT // ComponentValTypeKindOption ComponentValTypeKind = C.WASMTIME_COMPONENT_VALTYPE_OPTION @@ -70,7 +64,9 @@ const ( // ComponentValType describes the WIT type of a value in the component // model. Use [ComponentValType.Kind] to discriminate, then call the // corresponding downcast method ([ComponentValType.List], -// [ComponentValType.Record], ...) for composite kinds. +// [ComponentValType.Record], ...) for composite kinds. Downcast methods +// return independently-owned wrappers that must be closed (or left to the +// finalizer) separately from this value type. type ComponentValType struct { val C.wasmtime_component_valtype_t closed bool @@ -94,8 +90,7 @@ func (vt *ComponentValType) Kind() ComponentValTypeKind { } // List returns the [ComponentListType] wrapper when this value type's kind -// is [ComponentValTypeKindList], or nil otherwise. The returned wrapper has -// an independent lifecycle from the parent. +// is [ComponentValTypeKindList], or nil otherwise. func (vt *ComponentValType) List() *ComponentListType { if vt.Kind() != ComponentValTypeKindList { return nil diff --git a/component_valtype_feat_component_model_test.go b/component_valtype_feat_component_model_test.go index e9809a27..8983859a 100644 --- a/component_valtype_feat_component_model_test.go +++ b/component_valtype_feat_component_model_test.go @@ -35,20 +35,6 @@ func TestComponentValTypeList(t *testing.T) { require.NotNil(t, elem) defer elem.Close() require.Equal(t, ComponentValTypeKindU32, elem.Kind()) - - // List() on a non-list kind returns nil. - wasmU32, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) - require.NoError(t, err) - comp2, err := NewComponent(engine, wasmU32) - require.NoError(t, err) - defer comp2.Close() - ct2 := comp2.Type() - defer ct2.Close() - _, item2 := ct2.ExportNth(0) - defer item2.Close() - vt2 := item2.TypeAlias() - defer vt2.Close() - require.Nil(t, vt2.List()) } func TestComponentValTypeRecord(t *testing.T) { @@ -90,20 +76,6 @@ func TestComponentValTypeRecord(t *testing.T) { name2, ty2 := rt.FieldNth(2) require.Equal(t, "", name2) require.Nil(t, ty2) - - // Record() on a non-record kind returns nil. - wasmU32, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) - require.NoError(t, err) - comp2, err := NewComponent(engine, wasmU32) - require.NoError(t, err) - defer comp2.Close() - ct2 := comp2.Type() - defer ct2.Close() - _, item2 := ct2.ExportNth(0) - defer item2.Close() - vt2 := item2.TypeAlias() - defer vt2.Close() - require.Nil(t, vt2.Record()) } func TestComponentValTypeTuple(t *testing.T) { @@ -146,20 +118,6 @@ func TestComponentValTypeTuple(t *testing.T) { // TypeNth out of range returns nil. require.Nil(t, tt.TypeNth(3)) - - // Tuple() on a non-tuple kind returns nil. - wasmU32, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) - require.NoError(t, err) - comp2, err := NewComponent(engine, wasmU32) - require.NoError(t, err) - defer comp2.Close() - ct2 := comp2.Type() - defer ct2.Close() - _, item2 := ct2.ExportNth(0) - defer item2.Close() - vt2 := item2.TypeAlias() - defer vt2.Close() - require.Nil(t, vt2.Tuple()) } func TestComponentValTypeEnum(t *testing.T) { @@ -190,20 +148,6 @@ func TestComponentValTypeEnum(t *testing.T) { // CaseNth out of range returns "". require.Equal(t, "", et.CaseNth(3)) - - // Enum() on a non-enum kind returns nil. - wasmU32, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) - require.NoError(t, err) - comp2, err := NewComponent(engine, wasmU32) - require.NoError(t, err) - defer comp2.Close() - ct2 := comp2.Type() - defer ct2.Close() - _, item2 := ct2.ExportNth(0) - defer item2.Close() - vt2 := item2.TypeAlias() - defer vt2.Close() - require.Nil(t, vt2.Enum()) } func TestComponentValTypeFlags(t *testing.T) { @@ -234,18 +178,33 @@ func TestComponentValTypeFlags(t *testing.T) { // FlagNth out of range returns "". require.Equal(t, "", ft.FlagNth(3)) +} - // Flags() on a non-flags kind returns nil. - wasmU32, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) +// TestComponentValTypeDowncastNilForOtherKinds checks that each composite +// downcast method ([ComponentValType.List], [ComponentValType.Record], +// [ComponentValType.Tuple], [ComponentValType.Enum], +// [ComponentValType.Flags]) returns nil when invoked on a value type of an +// unrelated kind. A single `u32` type alias serves as the unrelated kind +// for all five probes. +func TestComponentValTypeDowncastNilForOtherKinds(t *testing.T) { + engine := newComponentEngine() + wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) require.NoError(t, err) - comp2, err := NewComponent(engine, wasmU32) + component, err := NewComponent(engine, wasm) require.NoError(t, err) - defer comp2.Close() - ct2 := comp2.Type() - defer ct2.Close() - _, item2 := ct2.ExportNth(0) - defer item2.Close() - vt2 := item2.TypeAlias() - defer vt2.Close() - require.Nil(t, vt2.Flags()) + defer component.Close() + + ct := component.Type() + defer ct.Close() + _, item := ct.ExportNth(0) + defer item.Close() + vt := item.TypeAlias() + defer vt.Close() + require.Equal(t, ComponentValTypeKindU32, vt.Kind()) + + require.Nil(t, vt.List()) + require.Nil(t, vt.Record()) + require.Nil(t, vt.Tuple()) + require.Nil(t, vt.Enum()) + require.Nil(t, vt.Flags()) } From 347f574b30647ac0f0ddfb42828d153a7fe52a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B0=B8=E9=87=8E=E5=B3=BB=E8=BC=94?= Date: Wed, 20 May 2026 04:12:28 +0900 Subject: [PATCH 6/6] Align composite ValType doc comments and tests with pre-existing wasmtime-go style externtype.go already exposes the same pattern (ExternType.FuncType / GlobalType / TableType / MemoryType: return the underlying type for the matching kind, nil otherwise) but uses different conventions than the ones this slice originally adopted: - Doc wording: existing methods say "returns the underlying X ... if it's a Y type. Otherwise returns nil." -- not "downcast method". - Test shape: TestFuncType asserts NotNil for the matching accessor and Nil for every other accessor inline, not via a separate cross-kind test function. Adjust this slice to match: - Drop the "downcast" framing from the ComponentValType type comment and from each composite accessor's doc; switch to the "returns the underlying X if this is a Y type. Otherwise returns nil." form, in one line. - Compress the ComponentValType type comment from 6 lines to 4. - Move the "other-kind accessors return nil" assertions from a dedicated TestComponentValTypeDowncastNilForOtherKinds into each of the five per-kind tests inline, mirroring TestFuncType. The dedicated test is removed. --- component_valtype_feat_component_model.go | 23 ++++------ ...onent_valtype_feat_component_model_test.go | 44 +++++++++---------- 2 files changed, 28 insertions(+), 39 deletions(-) diff --git a/component_valtype_feat_component_model.go b/component_valtype_feat_component_model.go index b7b3f65b..ccc1330e 100644 --- a/component_valtype_feat_component_model.go +++ b/component_valtype_feat_component_model.go @@ -62,11 +62,9 @@ const ( ) // ComponentValType describes the WIT type of a value in the component -// model. Use [ComponentValType.Kind] to discriminate, then call the -// corresponding downcast method ([ComponentValType.List], -// [ComponentValType.Record], ...) for composite kinds. Downcast methods -// return independently-owned wrappers that must be closed (or left to the -// finalizer) separately from this value type. +// model. For composite kinds, the matching accessor +// ([ComponentValType.List], [ComponentValType.Record], etc.) returns an +// independently-owned wrapper, or nil if the kind does not match. type ComponentValType struct { val C.wasmtime_component_valtype_t closed bool @@ -89,8 +87,7 @@ func (vt *ComponentValType) Kind() ComponentValTypeKind { return ComponentValTypeKind(C.go_component_valtype_kind(&vt.val)) } -// List returns the [ComponentListType] wrapper when this value type's kind -// is [ComponentValTypeKindList], or nil otherwise. +// List returns the underlying [ComponentListType] if this is a list type. Otherwise returns nil. func (vt *ComponentValType) List() *ComponentListType { if vt.Kind() != ComponentValTypeKindList { return nil @@ -100,8 +97,7 @@ func (vt *ComponentValType) List() *ComponentListType { return mkComponentListType(cloned) } -// Record returns the [ComponentRecordType] wrapper when this value type's -// kind is [ComponentValTypeKindRecord], or nil otherwise. +// Record returns the underlying [ComponentRecordType] if this is a record type. Otherwise returns nil. func (vt *ComponentValType) Record() *ComponentRecordType { if vt.Kind() != ComponentValTypeKindRecord { return nil @@ -111,8 +107,7 @@ func (vt *ComponentValType) Record() *ComponentRecordType { return mkComponentRecordType(cloned) } -// Tuple returns the [ComponentTupleType] wrapper when this value type's -// kind is [ComponentValTypeKindTuple], or nil otherwise. +// Tuple returns the underlying [ComponentTupleType] if this is a tuple type. Otherwise returns nil. func (vt *ComponentValType) Tuple() *ComponentTupleType { if vt.Kind() != ComponentValTypeKindTuple { return nil @@ -122,8 +117,7 @@ func (vt *ComponentValType) Tuple() *ComponentTupleType { return mkComponentTupleType(cloned) } -// Enum returns the [ComponentEnumType] wrapper when this value type's kind -// is [ComponentValTypeKindEnum], or nil otherwise. +// Enum returns the underlying [ComponentEnumType] if this is an enum type. Otherwise returns nil. func (vt *ComponentValType) Enum() *ComponentEnumType { if vt.Kind() != ComponentValTypeKindEnum { return nil @@ -133,8 +127,7 @@ func (vt *ComponentValType) Enum() *ComponentEnumType { return mkComponentEnumType(cloned) } -// Flags returns the [ComponentFlagsType] wrapper when this value type's -// kind is [ComponentValTypeKindFlags], or nil otherwise. +// Flags returns the underlying [ComponentFlagsType] if this is a flags type. Otherwise returns nil. func (vt *ComponentValType) Flags() *ComponentFlagsType { if vt.Kind() != ComponentValTypeKindFlags { return nil diff --git a/component_valtype_feat_component_model_test.go b/component_valtype_feat_component_model_test.go index 8983859a..b86aa7a5 100644 --- a/component_valtype_feat_component_model_test.go +++ b/component_valtype_feat_component_model_test.go @@ -35,6 +35,11 @@ func TestComponentValTypeList(t *testing.T) { require.NotNil(t, elem) defer elem.Close() require.Equal(t, ComponentValTypeKindU32, elem.Kind()) + + require.Nil(t, vt.Record()) + require.Nil(t, vt.Tuple()) + require.Nil(t, vt.Enum()) + require.Nil(t, vt.Flags()) } func TestComponentValTypeRecord(t *testing.T) { @@ -76,6 +81,11 @@ func TestComponentValTypeRecord(t *testing.T) { name2, ty2 := rt.FieldNth(2) require.Equal(t, "", name2) require.Nil(t, ty2) + + require.Nil(t, vt.List()) + require.Nil(t, vt.Tuple()) + require.Nil(t, vt.Enum()) + require.Nil(t, vt.Flags()) } func TestComponentValTypeTuple(t *testing.T) { @@ -118,6 +128,11 @@ func TestComponentValTypeTuple(t *testing.T) { // TypeNth out of range returns nil. require.Nil(t, tt.TypeNth(3)) + + require.Nil(t, vt.List()) + require.Nil(t, vt.Record()) + require.Nil(t, vt.Enum()) + require.Nil(t, vt.Flags()) } func TestComponentValTypeEnum(t *testing.T) { @@ -148,6 +163,11 @@ func TestComponentValTypeEnum(t *testing.T) { // CaseNth out of range returns "". require.Equal(t, "", et.CaseNth(3)) + + require.Nil(t, vt.List()) + require.Nil(t, vt.Record()) + require.Nil(t, vt.Tuple()) + require.Nil(t, vt.Flags()) } func TestComponentValTypeFlags(t *testing.T) { @@ -178,33 +198,9 @@ func TestComponentValTypeFlags(t *testing.T) { // FlagNth out of range returns "". require.Equal(t, "", ft.FlagNth(3)) -} - -// TestComponentValTypeDowncastNilForOtherKinds checks that each composite -// downcast method ([ComponentValType.List], [ComponentValType.Record], -// [ComponentValType.Tuple], [ComponentValType.Enum], -// [ComponentValType.Flags]) returns nil when invoked on a value type of an -// unrelated kind. A single `u32` type alias serves as the unrelated kind -// for all five probes. -func TestComponentValTypeDowncastNilForOtherKinds(t *testing.T) { - engine := newComponentEngine() - wasm, err := Wat2Wasm(`(component (type $a u32) (export "a" (type $a)))`) - require.NoError(t, err) - component, err := NewComponent(engine, wasm) - require.NoError(t, err) - defer component.Close() - - ct := component.Type() - defer ct.Close() - _, item := ct.ExportNth(0) - defer item.Close() - vt := item.TypeAlias() - defer vt.Close() - require.Equal(t, ComponentValTypeKindU32, vt.Kind()) require.Nil(t, vt.List()) require.Nil(t, vt.Record()) require.Nil(t, vt.Tuple()) require.Nil(t, vt.Enum()) - require.Nil(t, vt.Flags()) }