cgo: implement C.struct_ types
These types (called elaborated types in C) are used as part of linked lists, among others. This is part an extra feature (to be compatible with CGo C.struct_ types) and part a bugfix: linked lists would result in endless recursion leading to a stack overflow.
Этот коммит содержится в:
родитель
b716cf1afd
коммит
21a4c14e86
5 изменённых файлов: 89 добавлений и 13 удалений
|
@ -17,11 +17,12 @@ import (
|
||||||
type fileInfo struct {
|
type fileInfo struct {
|
||||||
*ast.File
|
*ast.File
|
||||||
*Package
|
*Package
|
||||||
filename string
|
filename string
|
||||||
functions map[string]*functionInfo
|
functions map[string]*functionInfo
|
||||||
globals map[string]*globalInfo
|
globals map[string]*globalInfo
|
||||||
typedefs map[string]*typedefInfo
|
typedefs map[string]*typedefInfo
|
||||||
importCPos token.Pos
|
elaboratedTypes map[string]ast.Expr
|
||||||
|
importCPos token.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
// functionInfo stores some information about a Cgo function found by libclang
|
// functionInfo stores some information about a Cgo function found by libclang
|
||||||
|
@ -81,12 +82,13 @@ typedef unsigned long long _Cgo_ulonglong;
|
||||||
// comment with libclang, and modifies the AST to use this information.
|
// comment with libclang, and modifies the AST to use this information.
|
||||||
func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []error {
|
func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []error {
|
||||||
info := &fileInfo{
|
info := &fileInfo{
|
||||||
File: f,
|
File: f,
|
||||||
Package: p,
|
Package: p,
|
||||||
filename: filename,
|
filename: filename,
|
||||||
functions: map[string]*functionInfo{},
|
functions: map[string]*functionInfo{},
|
||||||
globals: map[string]*globalInfo{},
|
globals: map[string]*globalInfo{},
|
||||||
typedefs: map[string]*typedefInfo{},
|
typedefs: map[string]*typedefInfo{},
|
||||||
|
elaboratedTypes: map[string]ast.Expr{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find `import "C"` statements in the file.
|
// Find `import "C"` statements in the file.
|
||||||
|
@ -142,9 +144,12 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []er
|
||||||
// Forward C types to Go types (like C.uint32_t -> uint32).
|
// Forward C types to Go types (like C.uint32_t -> uint32).
|
||||||
info.addTypeAliases()
|
info.addTypeAliases()
|
||||||
|
|
||||||
// Add type declarations for C types, declared using typeef in C.
|
// Add type declarations for C types, declared using typedef in C.
|
||||||
info.addTypedefs()
|
info.addTypedefs()
|
||||||
|
|
||||||
|
// Add elaborated types for C structs and unions.
|
||||||
|
info.addElaboratedTypes()
|
||||||
|
|
||||||
// Patch the AST to use the declared types and functions.
|
// Patch the AST to use the declared types and functions.
|
||||||
f = astutil.Apply(f, info.walker, nil).(*ast.File)
|
f = astutil.Apply(f, info.walker, nil).(*ast.File)
|
||||||
|
|
||||||
|
@ -376,6 +381,42 @@ func (info *fileInfo) addTypedefs() {
|
||||||
info.Decls = append(info.Decls, gen)
|
info.Decls = append(info.Decls, gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addElaboratedTypes adds C elaborated types as aliases. These are the "struct
|
||||||
|
// foo" or "union foo" types, often used in a typedef.
|
||||||
|
//
|
||||||
|
// See also:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/elaborated_type_specifier
|
||||||
|
func (info *fileInfo) addElaboratedTypes() {
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: info.importCPos,
|
||||||
|
Tok: token.TYPE,
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(info.elaboratedTypes))
|
||||||
|
for name := range info.elaboratedTypes {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
typ := info.elaboratedTypes[name]
|
||||||
|
typeName := "C.struct_" + name
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: typeName,
|
||||||
|
}
|
||||||
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: info.importCPos,
|
||||||
|
Name: typeName,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: typ,
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
|
}
|
||||||
|
info.Decls = append(info.Decls, gen)
|
||||||
|
}
|
||||||
|
|
||||||
// walker replaces all "C".<something> expressions to literal "C.<something>"
|
// walker replaces all "C".<something> expressions to literal "C.<something>"
|
||||||
// expressions. Such expressions are impossible to write in Go (a dot cannot be
|
// expressions. Such expressions are impossible to write in Go (a dot cannot be
|
||||||
// used in the middle of a name) so in practice all C identifiers live in a
|
// used in the middle of a name) so in practice all C identifiers live in a
|
||||||
|
|
|
@ -332,7 +332,25 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
|
||||||
}
|
}
|
||||||
case C.CXType_Elaborated:
|
case C.CXType_Elaborated:
|
||||||
underlying := C.clang_Type_getNamedType(typ)
|
underlying := C.clang_Type_getNamedType(typ)
|
||||||
return info.makeASTType(underlying)
|
switch underlying.kind {
|
||||||
|
case C.CXType_Record:
|
||||||
|
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||||
|
// It is possible that this is a recursive definition, for example
|
||||||
|
// in linked lists (structs contain a pointer to the next element
|
||||||
|
// of the same type). If the name exists in info.elaboratedTypes,
|
||||||
|
// it is being processed, although it may not be fully defined yet.
|
||||||
|
if _, ok := info.elaboratedTypes[name]; !ok {
|
||||||
|
info.elaboratedTypes[name] = nil // predeclare (to avoid endless recursion)
|
||||||
|
info.elaboratedTypes[name] = info.makeASTType(underlying)
|
||||||
|
}
|
||||||
|
return &ast.Ident{
|
||||||
|
NamePos: info.importCPos,
|
||||||
|
Name: "C.struct_" + name,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("unknown elaborated type")
|
||||||
|
}
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
fieldList := &ast.FieldList{
|
fieldList := &ast.FieldList{
|
||||||
|
|
8
testdata/cgo/main.go
предоставленный
8
testdata/cgo/main.go
предоставленный
|
@ -53,6 +53,14 @@ func main() {
|
||||||
C.unionSetData(5, 8, 1)
|
C.unionSetData(5, 8, 1)
|
||||||
println("union global data:", C.globalUnion.data[0], C.globalUnion.data[1], C.globalUnion.data[2])
|
println("union global data:", C.globalUnion.data[0], C.globalUnion.data[1], C.globalUnion.data[2])
|
||||||
println("union field:", printUnion(C.globalUnion).f)
|
println("union field:", printUnion(C.globalUnion).f)
|
||||||
|
|
||||||
|
// recursive types, test using a linked list
|
||||||
|
lastElement := &C.list_t{n: 7, next: nil}
|
||||||
|
list := &C.list_t{n: 3, next: &C.struct_list_t{n: 6, next: (*C.struct_list_t)(lastElement)}}
|
||||||
|
for list != nil {
|
||||||
|
println("n in chain:", list.n)
|
||||||
|
list = (*C.list_t)(list.next)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printUnion(union C.joined_t) C.joined_t {
|
func printUnion(union C.joined_t) C.joined_t {
|
||||||
|
|
6
testdata/cgo/main.h
предоставленный
6
testdata/cgo/main.h
предоставленный
|
@ -12,6 +12,12 @@ typedef struct collection {
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
} collection_t;
|
} collection_t;
|
||||||
|
|
||||||
|
// linked list
|
||||||
|
typedef struct list_t {
|
||||||
|
int n;
|
||||||
|
struct list_t *next;
|
||||||
|
} list_t;
|
||||||
|
|
||||||
typedef union joined {
|
typedef union joined {
|
||||||
myint s;
|
myint s;
|
||||||
float f;
|
float f;
|
||||||
|
|
3
testdata/cgo/out.txt
предоставленный
3
testdata/cgo/out.txt
предоставленный
|
@ -24,3 +24,6 @@ union local data: 5 8 1
|
||||||
union s method: -33 false
|
union s method: -33 false
|
||||||
union f: +6.280000e+000
|
union f: +6.280000e+000
|
||||||
union field: +6.280000e+000
|
union field: +6.280000e+000
|
||||||
|
n in chain: 3
|
||||||
|
n in chain: 6
|
||||||
|
n in chain: 7
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче