diff --git a/component_valtype_feat_component_model.go b/component_valtype_feat_component_model.go index 72907615..ccc1330e 100644 --- a/component_valtype_feat_component_model.go +++ b/component_valtype_feat_component_model.go @@ -5,17 +5,27 @@ 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. type ComponentValTypeKind uint8 const ( @@ -32,19 +42,17 @@ 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 kinds: each lands with its payload accessor. // - // 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 +62,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. 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 @@ -83,6 +87,56 @@ func (vt *ComponentValType) Kind() ComponentValTypeKind { return ComponentValTypeKind(C.go_component_valtype_kind(&vt.val)) } +// 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 + } + cloned := C.wasmtime_component_list_type_clone(C.go_component_valtype_list(&vt.val)) + runtime.KeepAlive(vt) + return mkComponentListType(cloned) +} + +// 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 + } + cloned := C.wasmtime_component_record_type_clone(C.go_component_valtype_record(&vt.val)) + runtime.KeepAlive(vt) + return mkComponentRecordType(cloned) +} + +// 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 + } + cloned := C.wasmtime_component_tuple_type_clone(C.go_component_valtype_tuple(&vt.val)) + runtime.KeepAlive(vt) + return mkComponentTupleType(cloned) +} + +// 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 + } + cloned := C.wasmtime_component_enum_type_clone(C.go_component_valtype_enum(&vt.val)) + runtime.KeepAlive(vt) + return mkComponentEnumType(cloned) +} + +// 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 + } + 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 +146,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 +} + +// 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) +} + +// 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) + 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 +} + +// 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) +} + +// 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) + 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 +} + +// 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) +} + +// 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) + 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..b86aa7a5 --- /dev/null +++ b/component_valtype_feat_component_model_test.go @@ -0,0 +1,206 @@ +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()) + + 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) { + 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) + defer item.Close() + vt := item.TypeAlias() + 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()) + + // FieldNth out of range returns ("", nil). + 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) { + 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.TypeCount()) + + ty0 := tt.TypeNth(0) + require.NotNil(t, ty0) + defer ty0.Close() + require.Equal(t, ComponentValTypeKindBool, ty0.Kind()) + + ty1 := tt.TypeNth(1) + require.NotNil(t, ty1) + defer ty1.Close() + require.Equal(t, ComponentValTypeKindS32, ty1.Kind()) + + ty2 := tt.TypeNth(2) + require.NotNil(t, ty2) + defer ty2.Close() + require.Equal(t, ComponentValTypeKindString, ty2.Kind()) + + // 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) { + 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.CaseCount()) + require.Equal(t, "red", et.CaseNth(0)) + require.Equal(t, "green", et.CaseNth(1)) + require.Equal(t, "blue", et.CaseNth(2)) + + // 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) { + 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.FlagCount()) + require.Equal(t, "read", ft.FlagNth(0)) + require.Equal(t, "write", ft.FlagNth(1)) + require.Equal(t, "exec", ft.FlagNth(2)) + + // FlagNth out of range returns "". + require.Equal(t, "", ft.FlagNth(3)) + + require.Nil(t, vt.List()) + require.Nil(t, vt.Record()) + require.Nil(t, vt.Tuple()) + require.Nil(t, vt.Enum()) +}