full test params passing of exported functions
This commit is contained in:
@@ -113,6 +113,9 @@ func (hw *cheaderWriter) processDependentTypes(t types.Type, visiting map[string
|
||||
// For named types, handle the underlying type dependencies
|
||||
underlying := typ.Underlying()
|
||||
if structType, ok := underlying.(*types.Struct); ok {
|
||||
if ssa.IsClosure(structType) {
|
||||
return fmt.Errorf("closure type %s can't export to C header", typ.Obj().Name())
|
||||
}
|
||||
// For named struct types, handle field dependencies directly
|
||||
for i := 0; i < structType.NumFields(); i++ {
|
||||
field := structType.Field(i)
|
||||
@@ -216,6 +219,9 @@ func (hw *cheaderWriter) goCTypeName(t types.Type) string {
|
||||
case *types.Interface:
|
||||
return "GoInterface"
|
||||
case *types.Struct:
|
||||
if ssa.IsClosure(typ) {
|
||||
panic("closure type can't export to C header")
|
||||
}
|
||||
// For anonymous structs, generate a descriptive name
|
||||
var fields []string
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
@@ -230,12 +236,41 @@ func (hw *cheaderWriter) goCTypeName(t types.Type) string {
|
||||
return fmt.Sprintf("%s_%s", pkg.Name(), typ.Obj().Name())
|
||||
case *types.Signature:
|
||||
// Function types are represented as function pointers in C
|
||||
// For simplicity, we use void* to represent function pointers
|
||||
return "void*"
|
||||
// Generate proper function pointer syntax
|
||||
return hw.generateFunctionPointerType(typ)
|
||||
}
|
||||
panic(fmt.Errorf("unsupported type: %v", t))
|
||||
}
|
||||
|
||||
// generateFunctionPointerType generates C function pointer type for Go function signatures
|
||||
func (hw *cheaderWriter) generateFunctionPointerType(sig *types.Signature) string {
|
||||
// Generate return type
|
||||
var returnType string
|
||||
results := sig.Results()
|
||||
if results == nil || results.Len() == 0 {
|
||||
returnType = "void"
|
||||
} else if results.Len() == 1 {
|
||||
returnType = hw.goCTypeName(results.At(0).Type())
|
||||
} else {
|
||||
panic("multiple return values can't export to C header")
|
||||
}
|
||||
|
||||
// Generate parameter types
|
||||
var paramTypes []string
|
||||
params := sig.Params()
|
||||
if params == nil || params.Len() == 0 {
|
||||
paramTypes = []string{"void"}
|
||||
} else {
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
paramType := hw.goCTypeName(params.At(i).Type())
|
||||
paramTypes = append(paramTypes, paramType)
|
||||
}
|
||||
}
|
||||
|
||||
// Return function pointer type: returnType (*)(paramType1, paramType2, ...)
|
||||
return fmt.Sprintf("%s (*)(%s)", returnType, strings.Join(paramTypes, ", "))
|
||||
}
|
||||
|
||||
// generateTypedef generates C typedef declaration for complex types
|
||||
func (hw *cheaderWriter) generateTypedef(t types.Type) string {
|
||||
switch typ := t.(type) {
|
||||
@@ -248,10 +283,41 @@ func (hw *cheaderWriter) generateTypedef(t types.Type) string {
|
||||
// For named struct types, generate the typedef directly
|
||||
return hw.generateNamedStructTypedef(typ, structType)
|
||||
}
|
||||
|
||||
cTypeName := hw.goCTypeName(typ)
|
||||
|
||||
// Special handling for function types
|
||||
if sig, ok := underlying.(*types.Signature); ok {
|
||||
// Generate return type
|
||||
var returnType string
|
||||
results := sig.Results()
|
||||
if results == nil || results.Len() == 0 {
|
||||
returnType = "void"
|
||||
} else if results.Len() == 1 {
|
||||
returnType = hw.goCTypeName(results.At(0).Type())
|
||||
} else {
|
||||
panic("multiple return values can't export to C header")
|
||||
}
|
||||
|
||||
// Generate parameter types
|
||||
var paramTypes []string
|
||||
params := sig.Params()
|
||||
if params == nil || params.Len() == 0 {
|
||||
paramTypes = []string{"void"}
|
||||
} else {
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
paramType := hw.goCTypeName(params.At(i).Type())
|
||||
paramTypes = append(paramTypes, paramType)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate proper function pointer typedef: typedef returnType (*typeName)(params);
|
||||
return fmt.Sprintf("typedef %s (*%s)(%s);", returnType, cTypeName, strings.Join(paramTypes, ", "))
|
||||
}
|
||||
|
||||
// For other named types, create a typedef to the underlying type
|
||||
underlyingCType := hw.goCTypeName(underlying)
|
||||
if underlyingCType != "" {
|
||||
cTypeName := hw.goCTypeName(typ)
|
||||
return fmt.Sprintf("typedef %s %s;", underlyingCType, cTypeName)
|
||||
}
|
||||
}
|
||||
@@ -312,6 +378,75 @@ func (hw *cheaderWriter) ensureArrayStruct(arr *types.Array) string {
|
||||
return structName
|
||||
}
|
||||
|
||||
// generateParameterDeclaration generates C parameter declaration for function parameters
|
||||
func (hw *cheaderWriter) generateParameterDeclaration(paramType types.Type, paramName string) string {
|
||||
var cType string
|
||||
|
||||
switch typ := paramType.(type) {
|
||||
case *types.Array:
|
||||
// Handle multidimensional arrays by collecting all dimensions
|
||||
var dimensions []int64
|
||||
baseType := types.Type(typ)
|
||||
|
||||
// Traverse all array dimensions
|
||||
for {
|
||||
if arr, ok := baseType.(*types.Array); ok {
|
||||
dimensions = append(dimensions, arr.Len())
|
||||
baseType = arr.Elem()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Get base element type
|
||||
elemType := hw.goCTypeName(baseType)
|
||||
|
||||
// For parameters, preserve all array dimensions
|
||||
// In C, array parameters need special handling for syntax
|
||||
cType = elemType
|
||||
|
||||
// Store dimensions for later use with parameter name
|
||||
var dimStr strings.Builder
|
||||
for _, dim := range dimensions {
|
||||
dimStr.WriteString(fmt.Sprintf("[%d]", dim))
|
||||
}
|
||||
|
||||
// For single dimension, we can use pointer syntax
|
||||
if len(dimensions) == 1 {
|
||||
cType = elemType + "*"
|
||||
} else {
|
||||
// For multi-dimensional, we need to handle it when adding parameter name
|
||||
// Store the dimension info in a special way
|
||||
cType = elemType + "ARRAY_DIMS" + dimStr.String()
|
||||
}
|
||||
case *types.Pointer:
|
||||
pointeeType := hw.goCTypeName(typ.Elem())
|
||||
cType = pointeeType + "*"
|
||||
default:
|
||||
// Regular types
|
||||
cType = hw.goCTypeName(paramType)
|
||||
}
|
||||
|
||||
// Handle special array dimension syntax
|
||||
if strings.Contains(cType, "ARRAY_DIMS") {
|
||||
parts := strings.Split(cType, "ARRAY_DIMS")
|
||||
elemType := parts[0]
|
||||
dimStr := parts[1]
|
||||
|
||||
if paramName == "" {
|
||||
// For unnamed parameters, keep dimension info: type[dim1][dim2]
|
||||
return elemType + dimStr
|
||||
}
|
||||
// For named parameters, use proper array syntax: type name[dim1][dim2]
|
||||
return elemType + " " + paramName + dimStr
|
||||
}
|
||||
|
||||
if paramName == "" {
|
||||
return cType
|
||||
}
|
||||
return cType + " " + paramName
|
||||
}
|
||||
|
||||
// generateFieldDeclaration generates C field declaration with correct array syntax
|
||||
func (hw *cheaderWriter) generateFieldDeclaration(fieldType types.Type, fieldName string) string {
|
||||
switch fieldType.(type) {
|
||||
@@ -461,15 +596,9 @@ func (hw *cheaderWriter) writeFunctionDecl(fullName, linkName string, fn ssa.Fun
|
||||
}
|
||||
|
||||
paramName := param.Name()
|
||||
if paramName == "" {
|
||||
paramName = fmt.Sprintf("param%d", i)
|
||||
}
|
||||
|
||||
// Use generateFieldDeclaration logic for consistent parameter syntax
|
||||
paramDecl := hw.generateFieldDeclaration(paramType, paramName)
|
||||
// Remove the leading spaces and semicolon to get just the declaration
|
||||
paramDecl = strings.TrimSpace(paramDecl)
|
||||
paramDecl = strings.TrimSuffix(paramDecl, ";")
|
||||
// Generate parameter declaration
|
||||
paramDecl := hw.generateParameterDeclaration(paramType, paramName)
|
||||
params = append(params, paramDecl)
|
||||
}
|
||||
|
||||
@@ -571,7 +700,7 @@ func genHeader(p ssa.Program, pkgs []ssa.Package, w io.Writer) error {
|
||||
link := exports[name] // link is cName
|
||||
fn := pkg.FuncOf(link)
|
||||
if fn == nil {
|
||||
continue
|
||||
return fmt.Errorf("function %s not found", link)
|
||||
}
|
||||
|
||||
// Write function declaration with proper C types
|
||||
|
||||
@@ -235,7 +235,7 @@ func TestGoCTypeName(t *testing.T) {
|
||||
{
|
||||
name: "signature type",
|
||||
goType: types.NewSignature(nil, nil, nil, false),
|
||||
expected: "void*",
|
||||
expected: "void (*)(void)",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -504,6 +504,153 @@ func TestProcessDependentTypesEdgeCases(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("processSignatureTypes(no results) error = %v", err)
|
||||
}
|
||||
|
||||
// Test function type (callback) parameters - IntCallback
|
||||
intCallbackParams := types.NewTuple(types.NewVar(0, nil, "x", types.Typ[types.Int]))
|
||||
intCallbackResults := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||
intCallbackSig := types.NewSignatureType(nil, nil, nil, intCallbackParams, intCallbackResults, false)
|
||||
err = hw.writeTypedefRecursive(intCallbackSig, make(map[string]bool))
|
||||
if err != nil {
|
||||
t.Errorf("writeTypedefRecursive(IntCallback) error = %v", err)
|
||||
}
|
||||
|
||||
// Test function type (callback) parameters - StringCallback
|
||||
stringCallbackParams := types.NewTuple(types.NewVar(0, nil, "s", types.Typ[types.String]))
|
||||
stringCallbackResults := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.String]))
|
||||
stringCallbackSig := types.NewSignatureType(nil, nil, nil, stringCallbackParams, stringCallbackResults, false)
|
||||
err = hw.writeTypedefRecursive(stringCallbackSig, make(map[string]bool))
|
||||
if err != nil {
|
||||
t.Errorf("writeTypedefRecursive(StringCallback) error = %v", err)
|
||||
}
|
||||
|
||||
// Test function type (callback) parameters - VoidCallback
|
||||
voidCallbackSig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
|
||||
err = hw.writeTypedefRecursive(voidCallbackSig, make(map[string]bool))
|
||||
if err != nil {
|
||||
t.Errorf("writeTypedefRecursive(VoidCallback) error = %v", err)
|
||||
}
|
||||
|
||||
// Test Named function type - this should trigger the function typedef generation
|
||||
pkg := types.NewPackage("test", "test")
|
||||
callbackParams := types.NewTuple(types.NewVar(0, nil, "x", types.Typ[types.Int]))
|
||||
callbackSig := types.NewSignatureType(nil, nil, nil, callbackParams, nil, false)
|
||||
callbackTypeName := types.NewTypeName(0, pkg, "Callback", nil)
|
||||
namedCallback := types.NewNamed(callbackTypeName, callbackSig, nil)
|
||||
|
||||
// Test Named function type with no parameters - NoParamCallback func() int
|
||||
noParamCallbackResults := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||
noParamCallbackSig := types.NewSignatureType(nil, nil, nil, nil, noParamCallbackResults, false)
|
||||
noParamCallbackTypeName := types.NewTypeName(0, pkg, "NoParamCallback", nil)
|
||||
namedNoParamCallback := types.NewNamed(noParamCallbackTypeName, noParamCallbackSig, nil)
|
||||
|
||||
err = hw.writeTypedef(namedCallback)
|
||||
if err != nil {
|
||||
t.Errorf("writeTypedef(named function) error = %v", err)
|
||||
}
|
||||
|
||||
err = hw.writeTypedef(namedNoParamCallback)
|
||||
if err != nil {
|
||||
t.Errorf("writeTypedef(no param callback) error = %v", err)
|
||||
}
|
||||
|
||||
// Verify the generated typedef contains function pointer syntax
|
||||
output := hw.typeBuf.String()
|
||||
if !strings.Contains(output, "test_Callback") {
|
||||
t.Errorf("Expected named function typedef in output")
|
||||
}
|
||||
if !strings.Contains(output, "(*test_Callback)") {
|
||||
t.Errorf("Expected function pointer syntax in typedef: %s", output)
|
||||
}
|
||||
if !strings.Contains(output, "test_NoParamCallback") {
|
||||
t.Errorf("Expected no-param callback typedef in output")
|
||||
}
|
||||
if !strings.Contains(output, "(*test_NoParamCallback)(void)") {
|
||||
t.Errorf("Expected no-param function pointer syntax in typedef: %s", output)
|
||||
}
|
||||
|
||||
// Test function signature with unnamed parameters (like //export ProcessThreeUnnamedParams)
|
||||
unnamedParams := types.NewTuple(
|
||||
types.NewVar(0, nil, "", types.Typ[types.Int]),
|
||||
types.NewVar(0, nil, "", types.Typ[types.String]),
|
||||
types.NewVar(0, nil, "", types.Typ[types.Bool]),
|
||||
)
|
||||
unnamedResults := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Float64]))
|
||||
unnamedSig := types.NewSignatureType(nil, nil, nil, unnamedParams, unnamedResults, false)
|
||||
err = hw.writeTypedefRecursive(unnamedSig, make(map[string]bool))
|
||||
if err != nil {
|
||||
t.Errorf("writeTypedefRecursive(unnamed params) error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test generateParameterDeclaration function
|
||||
func TestGenerateParameterDeclaration(t *testing.T) {
|
||||
prog := ssa.NewProgram(nil)
|
||||
hw := newCHeaderWriter(prog)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
paramType types.Type
|
||||
paramName string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "basic type with name",
|
||||
paramType: types.Typ[types.Int],
|
||||
paramName: "x",
|
||||
expected: "intptr_t x",
|
||||
},
|
||||
{
|
||||
name: "basic type without name",
|
||||
paramType: types.Typ[types.Int],
|
||||
paramName: "",
|
||||
expected: "intptr_t",
|
||||
},
|
||||
{
|
||||
name: "array type with name",
|
||||
paramType: types.NewArray(types.Typ[types.Int], 5),
|
||||
paramName: "arr",
|
||||
expected: "intptr_t* arr",
|
||||
},
|
||||
{
|
||||
name: "array type without name",
|
||||
paramType: types.NewArray(types.Typ[types.Int], 5),
|
||||
paramName: "",
|
||||
expected: "intptr_t*",
|
||||
},
|
||||
{
|
||||
name: "multidimensional array with name",
|
||||
paramType: types.NewArray(types.NewArray(types.Typ[types.Int], 4), 3),
|
||||
paramName: "matrix",
|
||||
expected: "intptr_t matrix[3][4]",
|
||||
},
|
||||
{
|
||||
name: "multidimensional array without name",
|
||||
paramType: types.NewArray(types.NewArray(types.Typ[types.Int], 4), 3),
|
||||
paramName: "",
|
||||
expected: "intptr_t[3][4]",
|
||||
},
|
||||
{
|
||||
name: "pointer type with name",
|
||||
paramType: types.NewPointer(types.Typ[types.Int]),
|
||||
paramName: "ptr",
|
||||
expected: "intptr_t* ptr",
|
||||
},
|
||||
{
|
||||
name: "pointer type without name",
|
||||
paramType: types.NewPointer(types.Typ[types.Int]),
|
||||
paramName: "",
|
||||
expected: "intptr_t*",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := hw.generateParameterDeclaration(tt.paramType, tt.paramName)
|
||||
if got != tt.expected {
|
||||
t.Errorf("generateParameterDeclaration() = %q, want %q", got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test generateNamedStructTypedef with forward declaration
|
||||
|
||||
Reference in New Issue
Block a user