Add Component function calls and runtime values - #290
Conversation
| func (t *ComponentFuncType) HasResult() bool { | ||
| var result C.wasmtime_component_valtype_t | ||
| found := bool(C.wasmtime_component_func_type_result(t.ptr(), &result)) | ||
| if found { | ||
| C.wasmtime_component_valtype_delete(&result) | ||
| } | ||
| return found | ||
| } |
There was a problem hiding this comment.
This is a pretty expensive operation to get the result type just to delete it and return a bool. Could this instead be modeled as returning the entire result type? For example returning nil if it's not present?
|
|
||
| // Call invokes the component function synchronously. Returned values are owned | ||
| // and must be closed by the caller. | ||
| func (f *ComponentFunc) Call(store Storelike, args []*ComponentVal) ([]*ComponentVal, error) { |
There was a problem hiding this comment.
Personally I think it would be best to model a similar interface to core wasm here which takes args ...interface{}. That'll make it more ergonomic to invoke this and additionally doesn't require boxing as a *ComponentVal by the caller. There'd be a conversion internally which would convert interface{} to a *ComponentVal, handling the case it's already a ComponentVal.
| if typeInfo == nil || typeInfo._ptr == nil { | ||
| return nil, fmt.Errorf("component function type unavailable") | ||
| } |
There was a problem hiding this comment.
Is this AI generated? I don't believe that either of these conditions is possible.
| if typeInfo.HasResult() { | ||
| resultCount = 1 | ||
| } | ||
| argsMem := componentValArray(len(args)) |
There was a problem hiding this comment.
Why do arguments need to be malloc'd in C here as opposed to passing a Go slice?
| if argsMem != nil { | ||
| defer C.free(argsMem) | ||
| } |
There was a problem hiding this comment.
This error handling is a bit nonsensical, it looks for allocation failure, deallocates later on success, but then continues to use the failed allocation in the case of failure.
Please be sure to review all AI generated code yourself.
| results := make([]*ComponentVal, resultCount) | ||
| for i := range results { | ||
| results[i] = ownComponentVal(*C.go_component_val_at(resultsMem, C.size_t(i))) | ||
| } | ||
| return results, nil |
There was a problem hiding this comment.
Similar to core wasm I think it would be best to return interface{} from this function which allows unwrapping return values into native Go types as opposed to forcing everything through ComponentVal
| func NewComponentTuple(values []*ComponentVal) *ComponentVal { | ||
| v := newComponentVal(func(v *C.wasmtime_component_val_t) { C.go_component_val_tuple_init(v, C.size_t(len(values))) }) | ||
| for i, value := range values { | ||
| C.go_component_val_tuple_set(&v.val, C.size_t(i), value.ptr()) | ||
| } | ||
| runtime.KeepAlive(values) | ||
| return v | ||
| } | ||
|
|
There was a problem hiding this comment.
Similar to callign functions I think this would be best modeled as args ...interface{} and internally it would convert any Go value into a component value.
| func NewComponentList(values []*ComponentVal) *ComponentVal { | ||
| v := newComponentVal(func(v *C.wasmtime_component_val_t) { C.go_component_val_list_init(v, C.size_t(len(values))) }) | ||
| for i, value := range values { | ||
| C.go_component_val_list_set(&v.val, C.size_t(i), value.ptr()) | ||
| } | ||
| runtime.KeepAlive(values) | ||
| return v | ||
| } |
There was a problem hiding this comment.
I'm not sure how Go conversions work, but could this take interface{} instead of []*ComponentValue to support taking things like []uint8 or []uint32 to avoid boxing?
Failing test
upstream/mainexposes component loading and instantiation, but it has no API to retrieve or invoke an exported component function and no runtime component value representation. The added tests call an exported function repeatedly withlist<u8>values and exercise owned primitive and composite values.General correction
ComponentInstance.GetFuncand synchronousComponentFunc.Callbindings.ComponentValrepresentation for primitives, lists, records, tuples, variants, options, results, enums, and flags.ComponentFuncTypeexplicit close/finalizer behavior consistent with the existing bindings.This draft intentionally does not add
ComponentValTypecomposite wrappers. That work is already under review in #283. It tracks the M1 work in #280 and can be rebased as the existing type series lands.Validation
With the v47.0.0 release artifacts restored as ignored build inputs:
Both commands pass on Windows amd64; lint reports
0 issues.Compatibility and security impact
The API is additive and synchronous. It wraps the existing Wasmtime C component API and does not change core-module behavior. Returned values are cloned before the C-owned result vector is deleted, avoiding borrowed pointers escaping into Go.
Documentation
Public types and methods include Go documentation. The tests demonstrate call and ownership behavior.
Downstream removal condition
Downstream users can remove equivalent local bindings after an upstream release contains these APIs and their component call/value fixtures pass unchanged.
This change contains no TradePit product concepts or private data.