cgo: add support for C typedefs
Этот коммит содержится в:
родитель
ecf6ffa62e
коммит
e8c1b5ab6e
4 изменённых файлов: 122 добавлений и 21 удалений
126
loader/cgo.go
126
loader/cgo.go
|
@ -6,6 +6,7 @@ package loader
|
||||||
import (
|
import (
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ type fileInfo struct {
|
||||||
*ast.File
|
*ast.File
|
||||||
filename string
|
filename string
|
||||||
functions []*functionInfo
|
functions []*functionInfo
|
||||||
types []string
|
typedefs []*typedefInfo
|
||||||
importCPos token.Pos
|
importCPos token.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,11 +33,25 @@ type paramInfo struct {
|
||||||
typeName string
|
typeName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// aliasInfo encapsulates aliases between C and Go, like C.int32_t -> int32. See
|
// typedefInfo contains information about a single typedef in C.
|
||||||
// addTypeAliases.
|
type typedefInfo struct {
|
||||||
type aliasInfo struct {
|
newName string // newly defined type name
|
||||||
typeName string
|
oldName string // old type name, may be something like "unsigned long long"
|
||||||
goTypeName string
|
size int // size in bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
||||||
|
// in both languages. See addTypeAliases.
|
||||||
|
var cgoAliases = map[string]string{
|
||||||
|
"C.int8_t": "int8",
|
||||||
|
"C.int16_t": "int16",
|
||||||
|
"C.int32_t": "int32",
|
||||||
|
"C.int64_t": "int64",
|
||||||
|
"C.uint8_t": "uint8",
|
||||||
|
"C.uint16_t": "uint16",
|
||||||
|
"C.uint32_t": "uint32",
|
||||||
|
"C.uint64_t": "uint64",
|
||||||
|
"C.uintptr_t": "uintptr",
|
||||||
}
|
}
|
||||||
|
|
||||||
// processCgo extracts the `import "C"` statement from the AST, parses the
|
// processCgo extracts the `import "C"` statement from the AST, parses the
|
||||||
|
@ -93,6 +108,9 @@ func (p *Package) processCgo(filename string, f *ast.File) error {
|
||||||
// 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.
|
||||||
|
info.addTypedefs()
|
||||||
|
|
||||||
// Patch the AST to use the declared types and functions.
|
// Patch the AST to use the declared types and functions.
|
||||||
ast.Inspect(f, info.walker)
|
ast.Inspect(f, info.walker)
|
||||||
|
|
||||||
|
@ -168,38 +186,91 @@ func (info *fileInfo) addFuncDecls() {
|
||||||
// // ...
|
// // ...
|
||||||
// )
|
// )
|
||||||
func (info *fileInfo) addTypeAliases() {
|
func (info *fileInfo) addTypeAliases() {
|
||||||
aliases := []aliasInfo{
|
aliasKeys := make([]string, 0, len(cgoAliases))
|
||||||
aliasInfo{"C.int8_t", "int8"},
|
for key := range cgoAliases {
|
||||||
aliasInfo{"C.int16_t", "int16"},
|
aliasKeys = append(aliasKeys, key)
|
||||||
aliasInfo{"C.int32_t", "int32"},
|
|
||||||
aliasInfo{"C.int64_t", "int64"},
|
|
||||||
aliasInfo{"C.uint8_t", "uint8"},
|
|
||||||
aliasInfo{"C.uint16_t", "uint16"},
|
|
||||||
aliasInfo{"C.uint32_t", "uint32"},
|
|
||||||
aliasInfo{"C.uint64_t", "uint64"},
|
|
||||||
aliasInfo{"C.uintptr_t", "uintptr"},
|
|
||||||
}
|
}
|
||||||
|
sort.Strings(aliasKeys)
|
||||||
gen := &ast.GenDecl{
|
gen := &ast.GenDecl{
|
||||||
TokPos: info.importCPos,
|
TokPos: info.importCPos,
|
||||||
Tok: token.TYPE,
|
Tok: token.TYPE,
|
||||||
Lparen: info.importCPos,
|
Lparen: info.importCPos,
|
||||||
Rparen: info.importCPos,
|
Rparen: info.importCPos,
|
||||||
}
|
}
|
||||||
for _, alias := range aliases {
|
for _, typeName := range aliasKeys {
|
||||||
|
goTypeName := cgoAliases[typeName]
|
||||||
obj := &ast.Object{
|
obj := &ast.Object{
|
||||||
Kind: ast.Typ,
|
Kind: ast.Typ,
|
||||||
Name: alias.typeName,
|
Name: typeName,
|
||||||
}
|
}
|
||||||
typeSpec := &ast.TypeSpec{
|
typeSpec := &ast.TypeSpec{
|
||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: info.importCPos,
|
NamePos: info.importCPos,
|
||||||
Name: alias.typeName,
|
Name: typeName,
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
},
|
},
|
||||||
Assign: info.importCPos,
|
Assign: info.importCPos,
|
||||||
Type: &ast.Ident{
|
Type: &ast.Ident{
|
||||||
NamePos: info.importCPos,
|
NamePos: info.importCPos,
|
||||||
Name: alias.goTypeName,
|
Name: goTypeName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
|
}
|
||||||
|
info.Decls = append(info.Decls, gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *fileInfo) addTypedefs() {
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: info.importCPos,
|
||||||
|
Tok: token.TYPE,
|
||||||
|
}
|
||||||
|
for _, typedef := range info.typedefs {
|
||||||
|
newType := "C." + typedef.newName
|
||||||
|
oldType := "C." + typedef.oldName
|
||||||
|
if _, ok := cgoAliases[newType]; ok {
|
||||||
|
// This is a type that also exists in Go (defined in stdint.h).
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: newType,
|
||||||
|
}
|
||||||
|
switch oldType {
|
||||||
|
// TODO: plain char (may be signed or unsigned)
|
||||||
|
case "C.signed char", "C.short", "C.int", "C.long", "C.long long":
|
||||||
|
switch typedef.size {
|
||||||
|
case 1:
|
||||||
|
oldType = "int8"
|
||||||
|
case 2:
|
||||||
|
oldType = "int16"
|
||||||
|
case 4:
|
||||||
|
oldType = "int32"
|
||||||
|
case 8:
|
||||||
|
oldType = "int64"
|
||||||
|
}
|
||||||
|
case "C.unsigned char", "C.unsigned short", "C.unsigned int", "C.unsigned long", "C.unsigned long long":
|
||||||
|
switch typedef.size {
|
||||||
|
case 1:
|
||||||
|
oldType = "uint8"
|
||||||
|
case 2:
|
||||||
|
oldType = "uint16"
|
||||||
|
case 4:
|
||||||
|
oldType = "uint32"
|
||||||
|
case 8:
|
||||||
|
oldType = "uint64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: info.importCPos,
|
||||||
|
Name: newType,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: &ast.Ident{
|
||||||
|
NamePos: info.importCPos,
|
||||||
|
Name: oldType,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
obj.Decl = typeSpec
|
obj.Decl = typeSpec
|
||||||
|
@ -229,6 +300,21 @@ func (info *fileInfo) walker(node ast.Node) bool {
|
||||||
Name: "C." + fun.Sel.Name,
|
Name: "C." + fun.Sel.Name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
typ, ok := node.Type.(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
x, ok := typ.X.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if x.Name == "C" {
|
||||||
|
node.Type = &ast.Ident{
|
||||||
|
NamePos: x.NamePos,
|
||||||
|
Name: "C." + typ.Sel.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,17 @@ func tinygo_clang_visitor(c, parent C.CXCursor, client_data C.CXClientData) C.in
|
||||||
resultType := C.clang_getCursorResultType(c)
|
resultType := C.clang_getCursorResultType(c)
|
||||||
resultTypeName := getString(C.clang_getTypeSpelling(resultType))
|
resultTypeName := getString(C.clang_getTypeSpelling(resultType))
|
||||||
fn.result = resultTypeName
|
fn.result = resultTypeName
|
||||||
|
case C.CXCursor_TypedefDecl:
|
||||||
|
typedefType := C.clang_getCursorType(c)
|
||||||
|
name := getString(C.clang_getTypedefName(typedefType))
|
||||||
|
underlyingType := C.clang_getTypedefDeclUnderlyingType(c)
|
||||||
|
underlyingTypeName := getString(C.clang_getTypeSpelling(underlyingType))
|
||||||
|
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||||
|
info.typedefs = append(info.typedefs, &typedefInfo{
|
||||||
|
newName: name,
|
||||||
|
oldName: underlyingTypeName,
|
||||||
|
size: int(typeSize),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
|
|
5
testdata/cgo/main.go
предоставленный
5
testdata/cgo/main.go
предоставленный
|
@ -4,10 +4,13 @@ package main
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
int32_t fortytwo(void);
|
int32_t fortytwo(void);
|
||||||
int32_t mul(int32_t a, int32_t b);
|
int32_t mul(int32_t a, int32_t b);
|
||||||
|
typedef int32_t myint;
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
println("fortytwo:", C.fortytwo())
|
println("fortytwo:", C.fortytwo())
|
||||||
println("mul:", C.mul(int32(3), 5))
|
println("mul:", C.mul(C.int32_t(3), 5))
|
||||||
|
var x C.myint = 3
|
||||||
|
println("myint:", x, C.myint(5))
|
||||||
}
|
}
|
||||||
|
|
1
testdata/cgo/out.txt
предоставленный
1
testdata/cgo/out.txt
предоставленный
|
@ -1,2 +1,3 @@
|
||||||
fortytwo: 42
|
fortytwo: 42
|
||||||
mul: 15
|
mul: 15
|
||||||
|
myint: 3 5
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче