cgo: add support for anonymous structs
Этот коммит содержится в:
родитель
1047c9bd05
коммит
0ce4d90779
4 изменённых файлов: 125 добавлений и 83 удалений
|
@ -36,6 +36,7 @@ type cgoPackage struct {
|
||||||
typedefs map[string]*typedefInfo
|
typedefs map[string]*typedefInfo
|
||||||
elaboratedTypes map[string]*elaboratedTypeInfo
|
elaboratedTypes map[string]*elaboratedTypeInfo
|
||||||
enums map[string]enumInfo
|
enums map[string]enumInfo
|
||||||
|
anonStructNum int
|
||||||
}
|
}
|
||||||
|
|
||||||
// constantInfo stores some information about a CGo constant found by libclang
|
// constantInfo stores some information about a CGo constant found by libclang
|
||||||
|
@ -68,7 +69,7 @@ type typedefInfo struct {
|
||||||
// elaboratedTypeInfo contains some information about an elaborated type
|
// elaboratedTypeInfo contains some information about an elaborated type
|
||||||
// (struct, union) found in the C AST.
|
// (struct, union) found in the C AST.
|
||||||
type elaboratedTypeInfo struct {
|
type elaboratedTypeInfo struct {
|
||||||
typeExpr ast.Expr
|
typeExpr *ast.StructType
|
||||||
pos token.Pos
|
pos token.Pos
|
||||||
bitfields []bitfieldInfo
|
bitfields []bitfieldInfo
|
||||||
}
|
}
|
||||||
|
|
186
cgo/libclang.go
186
cgo/libclang.go
|
@ -513,92 +513,48 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||||
var cgoName string
|
if name == "" {
|
||||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
// Anonymous record, probably inside a typedef.
|
||||||
case C.CXCursor_StructDecl:
|
typeExpr, bitfieldList := p.makeASTRecordType(cursor, pos)
|
||||||
cgoName = "struct_" + name
|
if bitfieldList != nil {
|
||||||
case C.CXCursor_UnionDecl:
|
// This struct has bitfields, so we have to declare it as a
|
||||||
cgoName = "union_" + name
|
// named type (for bitfield getters/setters to work).
|
||||||
default:
|
p.anonStructNum++
|
||||||
panic("unknown record declaration")
|
cgoName := "struct_" + strconv.Itoa(p.anonStructNum)
|
||||||
}
|
|
||||||
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
|
||||||
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
|
||||||
fieldList := &ast.FieldList{
|
|
||||||
Opening: pos,
|
|
||||||
Closing: pos,
|
|
||||||
}
|
|
||||||
var bitfieldList []bitfieldInfo
|
|
||||||
inBitfield := false
|
|
||||||
bitfieldNum := 0
|
|
||||||
ref := storedRefs.Put(struct {
|
|
||||||
fieldList *ast.FieldList
|
|
||||||
pkg *cgoPackage
|
|
||||||
inBitfield *bool
|
|
||||||
bitfieldNum *int
|
|
||||||
bitfieldList *[]bitfieldInfo
|
|
||||||
}{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
|
|
||||||
defer storedRefs.Remove(ref)
|
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
|
||||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
|
||||||
case C.CXCursor_StructDecl:
|
|
||||||
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
||||||
typeExpr: &ast.StructType{
|
typeExpr: typeExpr,
|
||||||
Struct: pos,
|
|
||||||
Fields: fieldList,
|
|
||||||
},
|
|
||||||
pos: pos,
|
pos: pos,
|
||||||
bitfields: bitfieldList,
|
bitfields: bitfieldList,
|
||||||
}
|
}
|
||||||
case C.CXCursor_UnionDecl:
|
return &ast.Ident{
|
||||||
if bitfieldList != nil {
|
NamePos: pos,
|
||||||
// This is valid C... but please don't do this.
|
Name: "C." + cgoName,
|
||||||
p.errors = append(p.errors, scanner.Error{
|
|
||||||
Pos: p.fset.PositionFor(pos, true),
|
|
||||||
Msg: fmt.Sprintf("bitfield in a union is not supported"),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
if len(fieldList.List) > 1 {
|
|
||||||
// Insert a special field at the front (of zero width) as a
|
|
||||||
// marker that this is struct is actually a union. This is done
|
|
||||||
// by giving the field a name that cannot be expressed directly
|
|
||||||
// in Go.
|
|
||||||
// Other parts of the compiler look at the first element in a
|
|
||||||
// struct (of size > 2) to know whether this is a union.
|
|
||||||
// Note that we don't have to insert it for single-element
|
|
||||||
// unions as they're basically equivalent to a struct.
|
|
||||||
unionMarker := &ast.Field{
|
|
||||||
Type: &ast.StructType{
|
|
||||||
Struct: pos,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
unionMarker.Names = []*ast.Ident{
|
|
||||||
&ast.Ident{
|
|
||||||
NamePos: pos,
|
|
||||||
Name: "C union",
|
|
||||||
Obj: &ast.Object{
|
|
||||||
Kind: ast.Var,
|
|
||||||
Name: "C union",
|
|
||||||
Decl: unionMarker,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
|
||||||
}
|
|
||||||
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
|
||||||
typeExpr: &ast.StructType{
|
|
||||||
Struct: pos,
|
|
||||||
Fields: fieldList,
|
|
||||||
},
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
}
|
||||||
}
|
return typeExpr
|
||||||
return &ast.Ident{
|
} else {
|
||||||
NamePos: pos,
|
var cgoName string
|
||||||
Name: "C." + cgoName,
|
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||||
|
case C.CXCursor_StructDecl:
|
||||||
|
cgoName = "struct_" + name
|
||||||
|
case C.CXCursor_UnionDecl:
|
||||||
|
cgoName = "union_" + name
|
||||||
|
default:
|
||||||
|
panic("unknown record declaration")
|
||||||
|
}
|
||||||
|
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
||||||
|
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
||||||
|
typeExpr, bitfieldList := p.makeASTRecordType(cursor, pos)
|
||||||
|
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
||||||
|
typeExpr: typeExpr,
|
||||||
|
pos: pos,
|
||||||
|
bitfields: bitfieldList,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + cgoName,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case C.CXType_Enum:
|
case C.CXType_Enum:
|
||||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
|
@ -644,6 +600,76 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeASTRecordType parses a C record (struct or union) and translates it into
|
||||||
|
// a Go struct type. Unions are implemented by setting the first field to a
|
||||||
|
// zero-lengt "C union" field, which cannot be written in Go directly.
|
||||||
|
func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) (*ast.StructType, []bitfieldInfo) {
|
||||||
|
fieldList := &ast.FieldList{
|
||||||
|
Opening: pos,
|
||||||
|
Closing: pos,
|
||||||
|
}
|
||||||
|
var bitfieldList []bitfieldInfo
|
||||||
|
inBitfield := false
|
||||||
|
bitfieldNum := 0
|
||||||
|
ref := storedRefs.Put(struct {
|
||||||
|
fieldList *ast.FieldList
|
||||||
|
pkg *cgoPackage
|
||||||
|
inBitfield *bool
|
||||||
|
bitfieldNum *int
|
||||||
|
bitfieldList *[]bitfieldInfo
|
||||||
|
}{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
|
||||||
|
defer storedRefs.Remove(ref)
|
||||||
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||||
|
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||||
|
case C.CXCursor_StructDecl:
|
||||||
|
return &ast.StructType{
|
||||||
|
Struct: pos,
|
||||||
|
Fields: fieldList,
|
||||||
|
}, bitfieldList
|
||||||
|
case C.CXCursor_UnionDecl:
|
||||||
|
if bitfieldList != nil {
|
||||||
|
// This is valid C... but please don't do this.
|
||||||
|
p.errors = append(p.errors, scanner.Error{
|
||||||
|
Pos: p.fset.PositionFor(pos, true),
|
||||||
|
Msg: fmt.Sprintf("bitfield in a union is not supported"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(fieldList.List) > 1 {
|
||||||
|
// Insert a special field at the front (of zero width) as a
|
||||||
|
// marker that this is struct is actually a union. This is done
|
||||||
|
// by giving the field a name that cannot be expressed directly
|
||||||
|
// in Go.
|
||||||
|
// Other parts of the compiler look at the first element in a
|
||||||
|
// struct (of size > 2) to know whether this is a union.
|
||||||
|
// Note that we don't have to insert it for single-element
|
||||||
|
// unions as they're basically equivalent to a struct.
|
||||||
|
unionMarker := &ast.Field{
|
||||||
|
Type: &ast.StructType{
|
||||||
|
Struct: pos,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
unionMarker.Names = []*ast.Ident{
|
||||||
|
&ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C union",
|
||||||
|
Obj: &ast.Object{
|
||||||
|
Kind: ast.Var,
|
||||||
|
Name: "C union",
|
||||||
|
Decl: unionMarker,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
||||||
|
}
|
||||||
|
return &ast.StructType{
|
||||||
|
Struct: pos,
|
||||||
|
Fields: fieldList,
|
||||||
|
}, bitfieldList
|
||||||
|
default:
|
||||||
|
panic("unknown record declaration")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//export tinygo_clang_struct_visitor
|
//export tinygo_clang_struct_visitor
|
||||||
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
||||||
|
|
6
testdata/cgo/main.go
предоставленный
6
testdata/cgo/main.go
предоставленный
|
@ -71,9 +71,13 @@ func main() {
|
||||||
printBitfield(&C.globalBitfield)
|
printBitfield(&C.globalBitfield)
|
||||||
|
|
||||||
// elaborated type
|
// elaborated type
|
||||||
p := C.struct_point{x: 3, y: 5}
|
p := C.struct_point2d{x: 3, y: 5}
|
||||||
println("struct:", p.x, p.y)
|
println("struct:", p.x, p.y)
|
||||||
|
|
||||||
|
// multiple anonymous structs (inside a typedef)
|
||||||
|
var _ C.point2d_t = C.point2d_t{x: 3, y: 5}
|
||||||
|
var _ C.point3d_t = C.point3d_t{x: 3, y: 5, z: 7}
|
||||||
|
|
||||||
// recursive types, test using a linked list
|
// recursive types, test using a linked list
|
||||||
list := &C.list_t{n: 3, next: &C.struct_list_t{n: 6, next: &C.list_t{n: 7, next: nil}}}
|
list := &C.list_t{n: 3, next: &C.struct_list_t{n: 6, next: &C.list_t{n: 7, next: nil}}}
|
||||||
for list != nil {
|
for list != nil {
|
||||||
|
|
13
testdata/cgo/main.h
предоставленный
13
testdata/cgo/main.h
предоставленный
|
@ -27,11 +27,22 @@ typedef struct collection {
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
} collection_t;
|
} collection_t;
|
||||||
|
|
||||||
struct point {
|
struct point2d {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
} point2d_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int z;
|
||||||
|
} point3d_t;
|
||||||
|
|
||||||
// linked list
|
// linked list
|
||||||
typedef struct list_t {
|
typedef struct list_t {
|
||||||
int n;
|
int n;
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче