cgo: refactor
This is a large refactor of the cgo package. It should fix a number of smaller problems and be a bit more strict (like upstream CGo): it for example requires every Go file in a package to include the header files it needs instead of piggybacking on imports in earlier files. The main benefit is that it should be a bit more maintainable and easier to add new features in the future (like static functions). This breaks the tinygo.org/x/bluetooth package, which should be updated before this change lands.
Этот коммит содержится в:
родитель
1d2c39c3b9
коммит
5afb63df60
9 изменённых файлов: 925 добавлений и 945 удалений
890
cgo/cgo.go
890
cgo/cgo.go
|
@ -18,7 +18,6 @@ import (
|
||||||
"go/scanner"
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -35,45 +34,19 @@ type cgoPackage struct {
|
||||||
packageDir string // full path to the package to process
|
packageDir string // full path to the package to process
|
||||||
fset *token.FileSet
|
fset *token.FileSet
|
||||||
tokenFiles map[string]*token.File
|
tokenFiles map[string]*token.File
|
||||||
missingSymbols map[string]struct{}
|
definedGlobally map[string]ast.Node
|
||||||
constants map[string]constantInfo
|
anonDecls map[interface{}]string
|
||||||
functions map[string]*functionInfo
|
|
||||||
globals map[string]globalInfo
|
|
||||||
typedefs map[string]*typedefInfo
|
|
||||||
elaboratedTypes map[string]*elaboratedTypeInfo
|
|
||||||
enums map[string]enumInfo
|
|
||||||
anonStructNum int
|
|
||||||
cflags []string // CFlags from #cgo lines
|
cflags []string // CFlags from #cgo lines
|
||||||
ldflags []string // LDFlags from #cgo lines
|
ldflags []string // LDFlags from #cgo lines
|
||||||
visitedFiles map[string][]byte
|
visitedFiles map[string][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// constantInfo stores some information about a CGo constant found by libclang
|
// cgoFile holds information only for a single Go file (with one or more
|
||||||
// and declared in the Go AST.
|
// `import "C"` statements).
|
||||||
type constantInfo struct {
|
type cgoFile struct {
|
||||||
expr ast.Expr
|
*cgoPackage
|
||||||
pos token.Pos
|
defined map[string]ast.Node
|
||||||
}
|
names map[string]clangCursor
|
||||||
|
|
||||||
// functionInfo stores some information about a CGo function found by libclang
|
|
||||||
// and declared in the AST.
|
|
||||||
type functionInfo struct {
|
|
||||||
args []paramInfo
|
|
||||||
results *ast.FieldList
|
|
||||||
pos token.Pos
|
|
||||||
variadic bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// paramInfo is a parameter of a CGo function (see functionInfo).
|
|
||||||
type paramInfo struct {
|
|
||||||
name string
|
|
||||||
typeExpr ast.Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// typedefInfo contains information about a single typedef in C.
|
|
||||||
type typedefInfo struct {
|
|
||||||
typeExpr ast.Expr
|
|
||||||
pos token.Pos
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// elaboratedTypeInfo contains some information about an elaborated type
|
// elaboratedTypeInfo contains some information about an elaborated type
|
||||||
|
@ -97,18 +70,6 @@ type bitfieldInfo struct {
|
||||||
endBit int64 // may be 0 meaning "until the end of the field"
|
endBit int64 // may be 0 meaning "until the end of the field"
|
||||||
}
|
}
|
||||||
|
|
||||||
// enumInfo contains information about an enum in the C.
|
|
||||||
type enumInfo struct {
|
|
||||||
typeExpr ast.Expr
|
|
||||||
pos token.Pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// globalInfo contains information about a declared global variable in C.
|
|
||||||
type globalInfo struct {
|
|
||||||
typeExpr ast.Expr
|
|
||||||
pos token.Pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
||||||
// in both languages. See addTypeAliases.
|
// in both languages. See addTypeAliases.
|
||||||
var cgoAliases = map[string]string{
|
var cgoAliases = map[string]string{
|
||||||
|
@ -125,24 +86,24 @@ var cgoAliases = map[string]string{
|
||||||
|
|
||||||
// builtinAliases are handled specially because they only exist on the Go side
|
// builtinAliases are handled specially because they only exist on the Go side
|
||||||
// of CGo, not on the CGo side (they're prefixed with "_Cgo_" there).
|
// of CGo, not on the CGo side (they're prefixed with "_Cgo_" there).
|
||||||
var builtinAliases = map[string]struct{}{
|
var builtinAliases = []string{
|
||||||
"char": {},
|
"char",
|
||||||
"schar": {},
|
"schar",
|
||||||
"uchar": {},
|
"uchar",
|
||||||
"short": {},
|
"short",
|
||||||
"ushort": {},
|
"ushort",
|
||||||
"int": {},
|
"int",
|
||||||
"uint": {},
|
"uint",
|
||||||
"long": {},
|
"long",
|
||||||
"ulong": {},
|
"ulong",
|
||||||
"longlong": {},
|
"longlong",
|
||||||
"ulonglong": {},
|
"ulonglong",
|
||||||
}
|
}
|
||||||
|
|
||||||
// cgoTypes lists some C types with ambiguous sizes that must be retrieved
|
// builtinAliasTypedefs lists some C types with ambiguous sizes that must be
|
||||||
// somehow from C. This is done by adding some typedefs to get the size of each
|
// retrieved somehow from C. This is done by adding some typedefs to get the
|
||||||
// type.
|
// size of each type.
|
||||||
const cgoTypes = `
|
const builtinAliasTypedefs = `
|
||||||
# 1 "<cgo>"
|
# 1 "<cgo>"
|
||||||
typedef char _Cgo_char;
|
typedef char _Cgo_char;
|
||||||
typedef signed char _Cgo_schar;
|
typedef signed char _Cgo_schar;
|
||||||
|
@ -202,13 +163,8 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||||
currentDir: dir,
|
currentDir: dir,
|
||||||
fset: fset,
|
fset: fset,
|
||||||
tokenFiles: map[string]*token.File{},
|
tokenFiles: map[string]*token.File{},
|
||||||
missingSymbols: map[string]struct{}{},
|
definedGlobally: map[string]ast.Node{},
|
||||||
constants: map[string]constantInfo{},
|
anonDecls: map[interface{}]string{},
|
||||||
functions: map[string]*functionInfo{},
|
|
||||||
globals: map[string]globalInfo{},
|
|
||||||
typedefs: map[string]*typedefInfo{},
|
|
||||||
elaboratedTypes: map[string]*elaboratedTypeInfo{},
|
|
||||||
enums: map[string]enumInfo{},
|
|
||||||
visitedFiles: map[string][]byte{},
|
visitedFiles: map[string][]byte{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +194,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||||
// This is always a bug in the cgo package.
|
// This is always a bug in the cgo package.
|
||||||
panic("unexpected error: " + err.Error())
|
panic("unexpected error: " + err.Error())
|
||||||
}
|
}
|
||||||
// If the Comments field is not set to nil, the fmt package will get
|
// If the Comments field is not set to nil, the go/format package will get
|
||||||
// confused about where comments should go.
|
// confused about where comments should go.
|
||||||
p.generated.Comments = nil
|
p.generated.Comments = nil
|
||||||
// Adjust some of the functions in there.
|
// Adjust some of the functions in there.
|
||||||
|
@ -254,15 +210,10 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Patch some types, for example *C.char in C.CString.
|
// Patch some types, for example *C.char in C.CString.
|
||||||
astutil.Apply(p.generated, p.walker, nil)
|
cf := p.newCGoFile()
|
||||||
|
astutil.Apply(p.generated, func(cursor *astutil.Cursor) bool {
|
||||||
// Find all C.* symbols.
|
return cf.walker(cursor, nil)
|
||||||
for _, f := range files {
|
}, nil)
|
||||||
astutil.Apply(f, p.findMissingCGoNames, nil)
|
|
||||||
}
|
|
||||||
for name := range builtinAliases {
|
|
||||||
p.missingSymbols["_Cgo_"+name] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find `import "C"` C fragments in the file.
|
// Find `import "C"` C fragments in the file.
|
||||||
cgoHeaders := make([]string, len(files)) // combined CGo header fragment for each file
|
cgoHeaders := make([]string, len(files)) // combined CGo header fragment for each file
|
||||||
|
@ -337,38 +288,33 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||||
cflagsForCGo = append(cflagsForCGo, "-isystem", clangHeaders)
|
cflagsForCGo = append(cflagsForCGo, "-isystem", clangHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve types such as C.int, C.longlong, etc from C.
|
||||||
|
p.newCGoFile().readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) {
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.TYPE,
|
||||||
|
}
|
||||||
|
for _, name := range builtinAliases {
|
||||||
|
typeSpec := p.getIntegerType("C."+name, names["_Cgo_"+name])
|
||||||
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
|
}
|
||||||
|
p.generated.Decls = append(p.generated.Decls, gen)
|
||||||
|
})
|
||||||
|
|
||||||
// Process CGo imports for each file.
|
// Process CGo imports for each file.
|
||||||
for i, f := range files {
|
for i, f := range files {
|
||||||
p.parseFragment(cgoHeaders[i]+cgoTypes, cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()))
|
cf := p.newCGoFile()
|
||||||
|
cf.readNames(cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
|
||||||
|
for _, name := range builtinAliases {
|
||||||
|
// Names such as C.int should not be obtained from C.
|
||||||
|
// This works around an issue in picolibc that has `#define int`
|
||||||
|
// in a header file.
|
||||||
|
delete(names, name)
|
||||||
}
|
}
|
||||||
|
astutil.Apply(f, func(cursor *astutil.Cursor) bool {
|
||||||
// Declare functions found by libclang.
|
return cf.walker(cursor, names)
|
||||||
p.addFuncDecls()
|
}, nil)
|
||||||
|
})
|
||||||
// Declare stub function pointer values found by libclang.
|
|
||||||
p.addFuncPtrDecls()
|
|
||||||
|
|
||||||
// Declare globals found by libclang.
|
|
||||||
p.addConstDecls()
|
|
||||||
|
|
||||||
// Declare globals found by libclang.
|
|
||||||
p.addVarDecls()
|
|
||||||
|
|
||||||
// Forward C types to Go types (like C.uint32_t -> uint32).
|
|
||||||
p.addTypeAliases()
|
|
||||||
|
|
||||||
// Add type declarations for C types, declared using typedef in C.
|
|
||||||
p.addTypedefs()
|
|
||||||
|
|
||||||
// Add elaborated types for C structs and unions.
|
|
||||||
p.addElaboratedTypes()
|
|
||||||
|
|
||||||
// Add enum types and enum constants for C enums.
|
|
||||||
p.addEnumTypes()
|
|
||||||
|
|
||||||
// Patch the AST to use the declared types and functions.
|
|
||||||
for _, f := range files {
|
|
||||||
astutil.Apply(f, p.walker, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the newly generated in-memory AST, for debugging.
|
// Print the newly generated in-memory AST, for debugging.
|
||||||
|
@ -377,6 +323,14 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||||
return p.generated, cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
|
return p.generated, cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *cgoPackage) newCGoFile() *cgoFile {
|
||||||
|
return &cgoFile{
|
||||||
|
cgoPackage: p,
|
||||||
|
defined: make(map[string]ast.Node),
|
||||||
|
names: make(map[string]clangCursor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
|
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
|
||||||
// relative flags into absolute flags, if they are relative. This is necessary
|
// relative flags into absolute flags, if they are relative. This is necessary
|
||||||
// because the C compiler is usually not invoked from the package path.
|
// because the C compiler is usually not invoked from the package path.
|
||||||
|
@ -484,365 +438,6 @@ func (p *cgoPackage) parseCGoPreprocessorLines(text string, pos token.Pos) strin
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
// addFuncDecls adds the C function declarations found by libclang in the
|
|
||||||
// comment above the `import "C"` statement.
|
|
||||||
func (p *cgoPackage) addFuncDecls() {
|
|
||||||
names := make([]string, 0, len(p.functions))
|
|
||||||
for name := range p.functions {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
for _, name := range names {
|
|
||||||
fn := p.functions[name]
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Fun,
|
|
||||||
Name: "C." + name,
|
|
||||||
}
|
|
||||||
args := make([]*ast.Field, len(fn.args))
|
|
||||||
decl := &ast.FuncDecl{
|
|
||||||
Doc: &ast.CommentGroup{
|
|
||||||
List: []*ast.Comment{
|
|
||||||
{
|
|
||||||
Slash: fn.pos - 1,
|
|
||||||
Text: "//export " + name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Name: &ast.Ident{
|
|
||||||
NamePos: fn.pos,
|
|
||||||
Name: "C." + name,
|
|
||||||
Obj: obj,
|
|
||||||
},
|
|
||||||
Type: &ast.FuncType{
|
|
||||||
Func: fn.pos,
|
|
||||||
Params: &ast.FieldList{
|
|
||||||
Opening: fn.pos,
|
|
||||||
List: args,
|
|
||||||
Closing: fn.pos,
|
|
||||||
},
|
|
||||||
Results: fn.results,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if fn.variadic {
|
|
||||||
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
|
|
||||||
Slash: fn.pos - 1,
|
|
||||||
Text: "//go:variadic",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
obj.Decl = decl
|
|
||||||
for i, arg := range fn.args {
|
|
||||||
args[i] = &ast.Field{
|
|
||||||
Names: []*ast.Ident{
|
|
||||||
{
|
|
||||||
NamePos: fn.pos,
|
|
||||||
Name: arg.name,
|
|
||||||
Obj: &ast.Object{
|
|
||||||
Kind: ast.Var,
|
|
||||||
Name: arg.name,
|
|
||||||
Decl: decl,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Type: arg.typeExpr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.generated.Decls = append(p.generated.Decls, decl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// addFuncPtrDecls creates stub declarations of function pointer values. These
|
|
||||||
// values will later be replaced with the real values in the compiler.
|
|
||||||
// It adds code like the following to the AST:
|
|
||||||
//
|
|
||||||
// var (
|
|
||||||
// C.add unsafe.Pointer
|
|
||||||
// C.mul unsafe.Pointer
|
|
||||||
// // ...
|
|
||||||
// )
|
|
||||||
func (p *cgoPackage) addFuncPtrDecls() {
|
|
||||||
if len(p.functions) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
names := make([]string, 0, len(p.functions))
|
|
||||||
for name := range p.functions {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
for _, name := range names {
|
|
||||||
gen := &ast.GenDecl{
|
|
||||||
TokPos: token.NoPos,
|
|
||||||
Tok: token.VAR,
|
|
||||||
Lparen: token.NoPos,
|
|
||||||
Rparen: token.NoPos,
|
|
||||||
}
|
|
||||||
fn := p.functions[name]
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: "C." + name + "$funcaddr",
|
|
||||||
}
|
|
||||||
valueSpec := &ast.ValueSpec{
|
|
||||||
Names: []*ast.Ident{{
|
|
||||||
NamePos: fn.pos,
|
|
||||||
Name: "C." + name + "$funcaddr",
|
|
||||||
Obj: obj,
|
|
||||||
}},
|
|
||||||
Type: &ast.SelectorExpr{
|
|
||||||
X: &ast.Ident{
|
|
||||||
NamePos: fn.pos,
|
|
||||||
Name: "unsafe",
|
|
||||||
},
|
|
||||||
Sel: &ast.Ident{
|
|
||||||
NamePos: fn.pos,
|
|
||||||
Name: "Pointer",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
obj.Decl = valueSpec
|
|
||||||
gen.Specs = append(gen.Specs, valueSpec)
|
|
||||||
p.generated.Decls = append(p.generated.Decls, gen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// addConstDecls declares external C constants in the Go source.
|
|
||||||
// It adds code like the following to the AST:
|
|
||||||
//
|
|
||||||
// const C.CONST_INT = 5
|
|
||||||
// const C.CONST_FLOAT = 5.8
|
|
||||||
// // ...
|
|
||||||
func (p *cgoPackage) addConstDecls() {
|
|
||||||
if len(p.constants) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
names := make([]string, 0, len(p.constants))
|
|
||||||
for name := range p.constants {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
for _, name := range names {
|
|
||||||
gen := &ast.GenDecl{
|
|
||||||
TokPos: token.NoPos,
|
|
||||||
Tok: token.CONST,
|
|
||||||
Lparen: token.NoPos,
|
|
||||||
Rparen: token.NoPos,
|
|
||||||
}
|
|
||||||
constVal := p.constants[name]
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Con,
|
|
||||||
Name: "C." + name,
|
|
||||||
}
|
|
||||||
valueSpec := &ast.ValueSpec{
|
|
||||||
Names: []*ast.Ident{{
|
|
||||||
NamePos: constVal.pos,
|
|
||||||
Name: "C." + name,
|
|
||||||
Obj: obj,
|
|
||||||
}},
|
|
||||||
Values: []ast.Expr{constVal.expr},
|
|
||||||
}
|
|
||||||
obj.Decl = valueSpec
|
|
||||||
gen.Specs = append(gen.Specs, valueSpec)
|
|
||||||
p.generated.Decls = append(p.generated.Decls, gen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// addVarDecls declares external C globals in the Go source.
|
|
||||||
// It adds code like the following to the AST:
|
|
||||||
//
|
|
||||||
// var C.globalInt int
|
|
||||||
// var C.globalBool bool
|
|
||||||
// // ...
|
|
||||||
func (p *cgoPackage) addVarDecls() {
|
|
||||||
if len(p.globals) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
names := make([]string, 0, len(p.globals))
|
|
||||||
for name := range p.globals {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
for _, name := range names {
|
|
||||||
global := p.globals[name]
|
|
||||||
gen := &ast.GenDecl{
|
|
||||||
TokPos: global.pos,
|
|
||||||
Tok: token.VAR,
|
|
||||||
Lparen: token.NoPos,
|
|
||||||
Rparen: token.NoPos,
|
|
||||||
Doc: &ast.CommentGroup{
|
|
||||||
List: []*ast.Comment{
|
|
||||||
{
|
|
||||||
Slash: global.pos - 1,
|
|
||||||
Text: "//go:extern " + name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Var,
|
|
||||||
Name: "C." + name,
|
|
||||||
}
|
|
||||||
valueSpec := &ast.ValueSpec{
|
|
||||||
Names: []*ast.Ident{{
|
|
||||||
NamePos: global.pos,
|
|
||||||
Name: "C." + name,
|
|
||||||
Obj: obj,
|
|
||||||
}},
|
|
||||||
Type: global.typeExpr,
|
|
||||||
}
|
|
||||||
obj.Decl = valueSpec
|
|
||||||
gen.Specs = append(gen.Specs, valueSpec)
|
|
||||||
p.generated.Decls = append(p.generated.Decls, gen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// addTypeAliases aliases some built-in Go types with their equivalent C types.
|
|
||||||
// It adds code like the following to the AST:
|
|
||||||
//
|
|
||||||
// type C.int8_t = int8
|
|
||||||
// type C.int16_t = int16
|
|
||||||
// // ...
|
|
||||||
func (p *cgoPackage) addTypeAliases() {
|
|
||||||
aliasKeys := make([]string, 0, len(cgoAliases))
|
|
||||||
for key := range cgoAliases {
|
|
||||||
aliasKeys = append(aliasKeys, key)
|
|
||||||
}
|
|
||||||
sort.Strings(aliasKeys)
|
|
||||||
for _, typeName := range aliasKeys {
|
|
||||||
gen := &ast.GenDecl{
|
|
||||||
TokPos: token.NoPos,
|
|
||||||
Tok: token.TYPE,
|
|
||||||
Lparen: token.NoPos,
|
|
||||||
Rparen: token.NoPos,
|
|
||||||
}
|
|
||||||
goTypeName := cgoAliases[typeName]
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: typeName,
|
|
||||||
}
|
|
||||||
typeSpec := &ast.TypeSpec{
|
|
||||||
Name: &ast.Ident{
|
|
||||||
NamePos: token.NoPos,
|
|
||||||
Name: typeName,
|
|
||||||
Obj: obj,
|
|
||||||
},
|
|
||||||
Assign: p.generatedPos,
|
|
||||||
Type: &ast.Ident{
|
|
||||||
NamePos: token.NoPos,
|
|
||||||
Name: goTypeName,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
obj.Decl = typeSpec
|
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
|
||||||
p.generated.Decls = append(p.generated.Decls, gen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *cgoPackage) addTypedefs() {
|
|
||||||
if len(p.typedefs) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
names := make([]string, 0, len(p.typedefs))
|
|
||||||
for name := range p.typedefs {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
for _, name := range names {
|
|
||||||
gen := &ast.GenDecl{
|
|
||||||
TokPos: token.NoPos,
|
|
||||||
Tok: token.TYPE,
|
|
||||||
}
|
|
||||||
typedef := p.typedefs[name]
|
|
||||||
typeName := "C." + name
|
|
||||||
isAlias := true
|
|
||||||
if strings.HasPrefix(name, "_Cgo_") {
|
|
||||||
typeName = "C." + name[len("_Cgo_"):]
|
|
||||||
isAlias = false // C.short etc. should not be aliased to the equivalent Go type (not portable)
|
|
||||||
}
|
|
||||||
if _, ok := cgoAliases[typeName]; ok {
|
|
||||||
// This is a type that also exists in Go (defined in stdint.h).
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: typeName,
|
|
||||||
}
|
|
||||||
typeSpec := &ast.TypeSpec{
|
|
||||||
Name: &ast.Ident{
|
|
||||||
NamePos: typedef.pos,
|
|
||||||
Name: typeName,
|
|
||||||
Obj: obj,
|
|
||||||
},
|
|
||||||
Type: typedef.typeExpr,
|
|
||||||
}
|
|
||||||
if isAlias {
|
|
||||||
typeSpec.Assign = typedef.pos
|
|
||||||
}
|
|
||||||
obj.Decl = typeSpec
|
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
|
||||||
p.generated.Decls = append(p.generated.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 (p *cgoPackage) addElaboratedTypes() {
|
|
||||||
if len(p.elaboratedTypes) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
names := make([]string, 0, len(p.elaboratedTypes))
|
|
||||||
for name := range p.elaboratedTypes {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
for _, name := range names {
|
|
||||||
gen := &ast.GenDecl{
|
|
||||||
TokPos: token.NoPos,
|
|
||||||
Tok: token.TYPE,
|
|
||||||
}
|
|
||||||
typ := p.elaboratedTypes[name]
|
|
||||||
typeName := "C." + name
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: typeName,
|
|
||||||
}
|
|
||||||
typeExpr := typ.typeExpr
|
|
||||||
if typ.unionSize != 0 {
|
|
||||||
// Create getters/setters.
|
|
||||||
for _, field := range typ.typeExpr.Fields.List {
|
|
||||||
if len(field.Names) != 1 {
|
|
||||||
p.addError(typ.pos, fmt.Sprintf("union must have field with a single name, it has %d names", len(field.Names)))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
p.createUnionAccessor(field, typeName)
|
|
||||||
}
|
|
||||||
// Convert to a single-field struct type.
|
|
||||||
typeExpr = p.makeUnionField(typ)
|
|
||||||
if typeExpr == nil {
|
|
||||||
// There was an error, that was already added to the list of
|
|
||||||
// errors.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
typeSpec := &ast.TypeSpec{
|
|
||||||
Name: &ast.Ident{
|
|
||||||
NamePos: typ.pos,
|
|
||||||
Name: typeName,
|
|
||||||
Obj: obj,
|
|
||||||
},
|
|
||||||
Type: typeExpr,
|
|
||||||
}
|
|
||||||
obj.Decl = typeSpec
|
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
|
||||||
// If this struct has bitfields, create getters for them.
|
|
||||||
for _, bitfield := range typ.bitfields {
|
|
||||||
p.createBitfieldGetter(bitfield, typeName)
|
|
||||||
p.createBitfieldSetter(bitfield, typeName)
|
|
||||||
}
|
|
||||||
p.generated.Decls = append(p.generated.Decls, gen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeUnionField creates a new struct from an existing *elaboratedTypeInfo,
|
// makeUnionField creates a new struct from an existing *elaboratedTypeInfo,
|
||||||
// that has just a single field that must be accessed through special accessors.
|
// that has just a single field that must be accessed through special accessors.
|
||||||
// It returns nil when there is an error. In case of an error, that error has
|
// It returns nil when there is an error. In case of an error, that error has
|
||||||
|
@ -1304,80 +899,319 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||||
p.generated.Decls = append(p.generated.Decls, setter)
|
p.generated.Decls = append(p.generated.Decls, setter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// addEnumTypes adds C enums to the AST. For example, the following C code:
|
// isEquivalentAST returns whether the given two AST nodes are equivalent as far
|
||||||
//
|
// as CGo is concerned. This is used to check that C types, globals, etc defined
|
||||||
// enum option {
|
// in different CGo header snippets are actually the same type (and probably
|
||||||
// optionA,
|
// even defined in the same header file, just in different translation units).
|
||||||
// optionB = 5,
|
func (p *cgoPackage) isEquivalentAST(a, b ast.Node) bool {
|
||||||
// };
|
switch node := a.(type) {
|
||||||
//
|
case *ast.ArrayType:
|
||||||
// is translated to the following Go code equivalent:
|
b, ok := b.(*ast.ArrayType)
|
||||||
//
|
if !ok {
|
||||||
// type C.enum_option int32
|
return false
|
||||||
//
|
|
||||||
// The constants are treated just like macros so are inserted into the AST by
|
|
||||||
// addConstDecls.
|
|
||||||
// See also: https://en.cppreference.com/w/c/language/enum
|
|
||||||
func (p *cgoPackage) addEnumTypes() {
|
|
||||||
if len(p.enums) == 0 {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
names := make([]string, 0, len(p.enums))
|
if !p.isEquivalentAST(node.Len, b.Len) {
|
||||||
for name := range p.enums {
|
return false
|
||||||
names = append(names, name)
|
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
return p.isEquivalentAST(node.Elt, b.Elt)
|
||||||
for _, name := range names {
|
case *ast.BasicLit:
|
||||||
gen := &ast.GenDecl{
|
b, ok := b.(*ast.BasicLit)
|
||||||
TokPos: token.NoPos,
|
if !ok {
|
||||||
Tok: token.TYPE,
|
return false
|
||||||
}
|
}
|
||||||
typ := p.enums[name]
|
// Note: this comparison is not correct in general ("1e2" equals "100"),
|
||||||
typeName := "C.enum_" + name
|
// but is correct for its use in the cgo package.
|
||||||
obj := &ast.Object{
|
return node.Value == b.Value
|
||||||
Kind: ast.Typ,
|
case *ast.CommentGroup:
|
||||||
Name: typeName,
|
b, ok := b.(*ast.CommentGroup)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
typeSpec := &ast.TypeSpec{
|
if len(node.List) != len(b.List) {
|
||||||
Name: &ast.Ident{
|
return false
|
||||||
NamePos: typ.pos,
|
|
||||||
Name: typeName,
|
|
||||||
Obj: obj,
|
|
||||||
},
|
|
||||||
Type: typ.typeExpr,
|
|
||||||
}
|
}
|
||||||
obj.Decl = typeSpec
|
for i, c := range node.List {
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
if c.Text != b.List[i].Text {
|
||||||
p.generated.Decls = append(p.generated.Decls, gen)
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case *ast.FieldList:
|
||||||
|
b, ok := b.(*ast.FieldList)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(node.List) != len(b.List) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, f := range node.List {
|
||||||
|
if !p.isEquivalentAST(f, b.List[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case *ast.Field:
|
||||||
|
b, ok := b.(*ast.Field)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !p.isEquivalentAST(node.Type, b.Type) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(node.Names) != len(b.Names) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, name := range node.Names {
|
||||||
|
if name.Name != b.Names[i].Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
b, ok := b.(*ast.FuncDecl)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if node.Name.Name != b.Name.Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if node.Doc != b.Doc {
|
||||||
|
if !p.isEquivalentAST(node.Doc, b.Doc) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.Recv != b.Recv {
|
||||||
|
if !p.isEquivalentAST(node.Recv, b.Recv) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !p.isEquivalentAST(node.Type.Params, b.Type.Params) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return p.isEquivalentAST(node.Type.Results, b.Type.Results)
|
||||||
|
case *ast.GenDecl:
|
||||||
|
b, ok := b.(*ast.GenDecl)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if node.Doc != b.Doc {
|
||||||
|
if !p.isEquivalentAST(node.Doc, b.Doc) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(node.Specs) != len(b.Specs) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, s := range node.Specs {
|
||||||
|
if !p.isEquivalentAST(s, b.Specs[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case *ast.Ident:
|
||||||
|
b, ok := b.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return node.Name == b.Name
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
b, ok := b.(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !p.isEquivalentAST(node.Sel, b.Sel) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return p.isEquivalentAST(node.X, b.X)
|
||||||
|
case *ast.StarExpr:
|
||||||
|
b, ok := b.(*ast.StarExpr)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return p.isEquivalentAST(node.X, b.X)
|
||||||
|
case *ast.StructType:
|
||||||
|
b, ok := b.(*ast.StructType)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return p.isEquivalentAST(node.Fields, b.Fields)
|
||||||
|
case *ast.TypeSpec:
|
||||||
|
b, ok := b.(*ast.TypeSpec)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if node.Name.Name != b.Name.Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if node.Assign.IsValid() != b.Assign.IsValid() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return p.isEquivalentAST(node.Type, b.Type)
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
b, ok := b.(*ast.ValueSpec)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(node.Names) != len(b.Names) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, name := range node.Names {
|
||||||
|
if name.Name != b.Names[i].Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.Type != b.Type && !p.isEquivalentAST(node.Type, b.Type) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(node.Values) != len(b.Values) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, value := range node.Values {
|
||||||
|
if !p.isEquivalentAST(value, b.Values[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case nil:
|
||||||
|
p.addError(token.NoPos, "internal error: AST node is nil")
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
p.addError(a.Pos(), fmt.Sprintf("internal error: unknown AST node: %T", a))
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// findMissingCGoNames traverses the AST and finds all C.something names. Only
|
// getPos returns node.Pos(), and tries to obtain a closely related position if
|
||||||
// these symbols are extracted from the parsed C AST and converted to the Go
|
// that fails.
|
||||||
// equivalent.
|
func getPos(node ast.Node) token.Pos {
|
||||||
func (p *cgoPackage) findMissingCGoNames(cursor *astutil.Cursor) bool {
|
pos := node.Pos()
|
||||||
switch node := cursor.Node().(type) {
|
if pos.IsValid() {
|
||||||
case *ast.SelectorExpr:
|
return pos
|
||||||
x, ok := node.X.(*ast.Ident)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
if x.Name == "C" {
|
if decl, ok := node.(*ast.GenDecl); ok {
|
||||||
name := node.Sel.Name
|
// *ast.GenDecl often doesn't have TokPos defined, so look at the first
|
||||||
if _, ok := builtinAliases[name]; ok {
|
// spec.
|
||||||
name = "_Cgo_" + name
|
return getPos(decl.Specs[0])
|
||||||
}
|
}
|
||||||
p.missingSymbols[name] = struct{}{}
|
return token.NoPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUnnamedDeclName creates a name (with the given prefix) for the given C
|
||||||
|
// declaration. This is used for structs, unions, and enums that are often
|
||||||
|
// defined without a name and used in a typedef.
|
||||||
|
func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string {
|
||||||
|
if name, ok := p.anonDecls[itf]; ok {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
name := prefix + strconv.Itoa(len(p.anonDecls))
|
||||||
|
p.anonDecls[itf] = name
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// getASTDeclName will declare the given C AST node (if not already defined) and
|
||||||
|
// will return its name, in the form of C.foo.
|
||||||
|
func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) string {
|
||||||
|
// Some types are defined in stdint.h and map directly to a particular Go
|
||||||
|
// type.
|
||||||
|
if alias := cgoAliases["C."+name]; alias != "" {
|
||||||
|
return alias
|
||||||
|
}
|
||||||
|
node := f.getASTDeclNode(name, found, iscall)
|
||||||
|
if _, ok := node.(*ast.FuncDecl); ok && !iscall {
|
||||||
|
return "C." + name + "$funcaddr"
|
||||||
|
}
|
||||||
|
return "C." + name
|
||||||
|
}
|
||||||
|
|
||||||
|
// getASTDeclNode will declare the given C AST node (if not already defined) and
|
||||||
|
// returns it.
|
||||||
|
func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) ast.Node {
|
||||||
|
if node, ok := f.defined[name]; ok {
|
||||||
|
// Declaration was found in the current file, so return it immediately.
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
if node, ok := f.definedGlobally[name]; ok {
|
||||||
|
// Declaration was previously created, but not for the current file. It
|
||||||
|
// may be different (because it comes from a different CGo snippet), so
|
||||||
|
// we need to check whether the AST for this definition is equivalent.
|
||||||
|
f.defined[name] = nil
|
||||||
|
newNode, _ := f.createASTNode(name, found)
|
||||||
|
if !f.isEquivalentAST(node, newNode) {
|
||||||
|
// It's not. Return a nice error with both locations.
|
||||||
|
// Original cgo reports an error like
|
||||||
|
// cgo: inconsistent definitions for C.myint
|
||||||
|
// which is far less helpful.
|
||||||
|
f.addError(getPos(node), "defined previously at "+f.fset.Position(getPos(newNode)).String()+" with a different type")
|
||||||
|
}
|
||||||
|
f.defined[name] = node
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
// The declaration has no AST node. Create it now.
|
||||||
|
f.defined[name] = nil
|
||||||
|
node, elaboratedType := f.createASTNode(name, found)
|
||||||
|
f.defined[name] = node
|
||||||
|
f.definedGlobally[name] = node
|
||||||
|
switch node := node.(type) {
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
f.generated.Decls = append(f.generated.Decls, node)
|
||||||
|
// Also add a declaration like the following:
|
||||||
|
// var C.foo$funcaddr unsafe.Pointer
|
||||||
|
f.generated.Decls = append(f.generated.Decls, &ast.GenDecl{
|
||||||
|
Tok: token.VAR,
|
||||||
|
Specs: []ast.Spec{
|
||||||
|
&ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{{Name: "C." + name + "$funcaddr"}},
|
||||||
|
Type: &ast.SelectorExpr{
|
||||||
|
X: &ast.Ident{Name: "unsafe"},
|
||||||
|
Sel: &ast.Ident{Name: "Pointer"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case *ast.GenDecl:
|
||||||
|
f.generated.Decls = append(f.generated.Decls, node)
|
||||||
|
case *ast.TypeSpec:
|
||||||
|
f.generated.Decls = append(f.generated.Decls, &ast.GenDecl{
|
||||||
|
Tok: token.TYPE,
|
||||||
|
Specs: []ast.Spec{node},
|
||||||
|
})
|
||||||
|
case nil:
|
||||||
|
// Node may be nil in case of an error. In that case, just don't add it
|
||||||
|
// as a declaration.
|
||||||
|
default:
|
||||||
|
panic("unexpected AST node")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a struct or union it may need bitfields or union accessor
|
||||||
|
// methods.
|
||||||
|
if elaboratedType != nil {
|
||||||
|
// Add struct bitfields.
|
||||||
|
for _, bitfield := range elaboratedType.bitfields {
|
||||||
|
f.createBitfieldGetter(bitfield, "C."+name)
|
||||||
|
f.createBitfieldSetter(bitfield, "C."+name)
|
||||||
|
}
|
||||||
|
if elaboratedType.unionSize != 0 {
|
||||||
|
// Create union getters/setters.
|
||||||
|
for _, field := range elaboratedType.typeExpr.Fields.List {
|
||||||
|
if len(field.Names) != 1 {
|
||||||
|
f.addError(elaboratedType.pos, fmt.Sprintf("union must have field with a single name, it has %d names", len(field.Names)))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
f.createUnionAccessor(field, "C."+name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
// separate namespace (no _Cgo_ hacks like in gc).
|
// separate namespace (no _Cgo_ hacks like in gc).
|
||||||
func (p *cgoPackage) walker(cursor *astutil.Cursor) bool {
|
func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) bool {
|
||||||
switch node := cursor.Node().(type) {
|
switch node := cursor.Node().(type) {
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
fun, ok := node.Fun.(*ast.SelectorExpr)
|
fun, ok := node.Fun.(*ast.SelectorExpr)
|
||||||
|
@ -1388,10 +1222,10 @@ func (p *cgoPackage) walker(cursor *astutil.Cursor) bool {
|
||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if _, ok := p.functions[fun.Sel.Name]; ok && x.Name == "C" {
|
if found, ok := names[fun.Sel.Name]; ok && x.Name == "C" {
|
||||||
node.Fun = &ast.Ident{
|
node.Fun = &ast.Ident{
|
||||||
NamePos: x.NamePos,
|
NamePos: x.NamePos,
|
||||||
Name: "C." + fun.Sel.Name,
|
Name: f.getASTDeclName(fun.Sel.Name, found, true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *ast.SelectorExpr:
|
case *ast.SelectorExpr:
|
||||||
|
@ -1401,8 +1235,8 @@ func (p *cgoPackage) walker(cursor *astutil.Cursor) bool {
|
||||||
}
|
}
|
||||||
if x.Name == "C" {
|
if x.Name == "C" {
|
||||||
name := "C." + node.Sel.Name
|
name := "C." + node.Sel.Name
|
||||||
if _, ok := p.functions[node.Sel.Name]; ok {
|
if found, ok := names[node.Sel.Name]; ok {
|
||||||
name += "$funcaddr"
|
name = f.getASTDeclName(node.Sel.Name, found, false)
|
||||||
}
|
}
|
||||||
cursor.Replace(&ast.Ident{
|
cursor.Replace(&ast.Ident{
|
||||||
NamePos: x.NamePos,
|
NamePos: x.NamePos,
|
||||||
|
|
566
cgo/libclang.go
566
cgo/libclang.go
|
@ -72,7 +72,11 @@ var diagnosticSeverity = [...]string{
|
||||||
C.CXDiagnostic_Fatal: "fatal",
|
C.CXDiagnostic_Fatal: "fatal",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename string) {
|
// Alias so that cgo.go (which doesn't import Clang related stuff and is in
|
||||||
|
// theory decoupled from Clang) can also use this type.
|
||||||
|
type clangCursor = C.GoCXCursor
|
||||||
|
|
||||||
|
func (f *cgoFile) readNames(fragment string, cflags []string, filename string, callback func(map[string]clangCursor)) {
|
||||||
index := C.clang_createIndex(0, 0)
|
index := C.clang_createIndex(0, 0)
|
||||||
defer C.clang_disposeIndex(index)
|
defer C.clang_disposeIndex(index)
|
||||||
|
|
||||||
|
@ -119,8 +123,8 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
||||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||||
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
||||||
location := C.clang_getDiagnosticLocation(diagnostic)
|
location := C.clang_getDiagnosticLocation(diagnostic)
|
||||||
pos := p.getClangLocationPosition(location, unit)
|
pos := f.getClangLocationPosition(location, unit)
|
||||||
p.addError(pos, severity+": "+spelling)
|
f.addError(pos, severity+": "+spelling)
|
||||||
}
|
}
|
||||||
for i := 0; i < numDiagnostics; i++ {
|
for i := 0; i < numDiagnostics; i++ {
|
||||||
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
||||||
|
@ -135,7 +139,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract information required by CGo.
|
// Extract information required by CGo.
|
||||||
ref := storedRefs.Put(p)
|
ref := storedRefs.Put(f)
|
||||||
defer storedRefs.Remove(ref)
|
defer storedRefs.Remove(ref)
|
||||||
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
||||||
|
@ -155,35 +159,64 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
||||||
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
||||||
|
|
||||||
// Hash the contents if it isn't hashed yet.
|
// Hash the contents if it isn't hashed yet.
|
||||||
if _, ok := p.visitedFiles[path]; !ok {
|
if _, ok := f.visitedFiles[path]; !ok {
|
||||||
// already stored
|
// already stored
|
||||||
sum := sha512.Sum512_224(data)
|
sum := sha512.Sum512_224(data)
|
||||||
p.visitedFiles[path] = sum[:]
|
f.visitedFiles[path] = sum[:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
|
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
|
||||||
defer storedRefs.Remove(inclusionCallbackRef)
|
defer storedRefs.Remove(inclusionCallbackRef)
|
||||||
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
|
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
|
||||||
|
|
||||||
|
// Do all the C AST operations inside a callback. This makes sure that
|
||||||
|
// libclang related memory is only freed after it is not necessary anymore.
|
||||||
|
callback(f.names)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export tinygo_clang_globals_visitor
|
// Convert the AST node under the given Clang cursor to a Go AST node and return
|
||||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
// it.
|
||||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaboratedTypeInfo) {
|
||||||
kind := C.tinygo_clang_getCursorKind(c)
|
kind := C.tinygo_clang_getCursorKind(c)
|
||||||
pos := p.getCursorPosition(c)
|
pos := f.getCursorPosition(c)
|
||||||
switch kind {
|
switch kind {
|
||||||
case C.CXCursor_FunctionDecl:
|
case C.CXCursor_FunctionDecl:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||||
fn := &functionInfo{
|
obj := &ast.Object{
|
||||||
pos: pos,
|
Kind: ast.Fun,
|
||||||
variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0,
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
args := make([]*ast.Field, numArgs)
|
||||||
|
decl := &ast.FuncDecl{
|
||||||
|
Doc: &ast.CommentGroup{
|
||||||
|
List: []*ast.Comment{
|
||||||
|
{
|
||||||
|
Slash: pos - 1,
|
||||||
|
Text: "//export " + name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: &ast.FuncType{
|
||||||
|
Func: pos,
|
||||||
|
Params: &ast.FieldList{
|
||||||
|
Opening: pos,
|
||||||
|
List: args,
|
||||||
|
Closing: pos,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
||||||
|
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
|
||||||
|
Slash: pos - 1,
|
||||||
|
Text: "//go:variadic",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
p.functions[name] = fn
|
|
||||||
for i := 0; i < numArgs; i++ {
|
for i := 0; i < numArgs; i++ {
|
||||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||||
|
@ -191,50 +224,108 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||||
if argName == "" {
|
if argName == "" {
|
||||||
argName = "$" + strconv.Itoa(i)
|
argName = "$" + strconv.Itoa(i)
|
||||||
}
|
}
|
||||||
fn.args = append(fn.args, paramInfo{
|
args[i] = &ast.Field{
|
||||||
name: argName,
|
Names: []*ast.Ident{
|
||||||
typeExpr: p.makeDecayingASTType(argType, pos),
|
{
|
||||||
})
|
NamePos: pos,
|
||||||
|
Name: argName,
|
||||||
|
Obj: &ast.Object{
|
||||||
|
Kind: ast.Var,
|
||||||
|
Name: argName,
|
||||||
|
Decl: decl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: f.makeDecayingASTType(argType, pos),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
resultType := C.tinygo_clang_getCursorResultType(c)
|
resultType := C.tinygo_clang_getCursorResultType(c)
|
||||||
if resultType.kind != C.CXType_Void {
|
if resultType.kind != C.CXType_Void {
|
||||||
fn.results = &ast.FieldList{
|
decl.Type.Results = &ast.FieldList{
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
{
|
{
|
||||||
Type: p.makeASTType(resultType, pos),
|
Type: f.makeASTType(resultType, pos),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case C.CXCursor_StructDecl:
|
obj.Decl = decl
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
return decl, nil
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
|
||||||
if _, required := p.missingSymbols["struct_"+name]; !required {
|
typ := f.makeASTRecordType(c, pos)
|
||||||
return C.CXChildVisit_Continue
|
typeName := "C." + name
|
||||||
|
typeExpr := typ.typeExpr
|
||||||
|
if typ.unionSize != 0 {
|
||||||
|
// Convert to a single-field struct type.
|
||||||
|
typeExpr = f.makeUnionField(typ)
|
||||||
}
|
}
|
||||||
p.makeASTType(typ, pos)
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: typeName,
|
||||||
|
}
|
||||||
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: typ.pos,
|
||||||
|
Name: typeName,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: typeExpr,
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
return typeSpec, typ
|
||||||
case C.CXCursor_TypedefDecl:
|
case C.CXCursor_TypedefDecl:
|
||||||
typedefType := C.tinygo_clang_getCursorType(c)
|
typeName := "C." + name
|
||||||
name := getString(C.clang_getTypedefName(typedefType))
|
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
obj := &ast.Object{
|
||||||
return C.CXChildVisit_Continue
|
Kind: ast.Typ,
|
||||||
|
Name: typeName,
|
||||||
}
|
}
|
||||||
p.makeASTType(typedefType, pos)
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: typeName,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: f.makeASTType(underlyingType, pos),
|
||||||
|
}
|
||||||
|
if underlyingType.kind != C.CXType_Enum {
|
||||||
|
typeSpec.Assign = pos
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
return typeSpec, nil
|
||||||
case C.CXCursor_VarDecl:
|
case C.CXCursor_VarDecl:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
p.globals[name] = globalInfo{
|
typeExpr := f.makeASTType(cursorType, pos)
|
||||||
typeExpr: p.makeASTType(cursorType, pos),
|
gen := &ast.GenDecl{
|
||||||
pos: pos,
|
TokPos: pos,
|
||||||
|
Tok: token.VAR,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
|
Doc: &ast.CommentGroup{
|
||||||
|
List: []*ast.Comment{
|
||||||
|
{
|
||||||
|
Slash: pos - 1,
|
||||||
|
Text: "//go:extern " + name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Var,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Type: typeExpr,
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
return gen, nil
|
||||||
case C.CXCursor_MacroDefinition:
|
case C.CXCursor_MacroDefinition:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
||||||
start := C.clang_getRangeStart(sourceRange)
|
start := C.clang_getRangeStart(sourceRange)
|
||||||
end := C.clang_getRangeEnd(sourceRange)
|
end := C.clang_getRangeEnd(sourceRange)
|
||||||
|
@ -242,17 +333,17 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||||
var startOffset, endOffset C.unsigned
|
var startOffset, endOffset C.unsigned
|
||||||
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
||||||
if file == nil {
|
if file == nil {
|
||||||
p.addError(pos, "internal error: could not find file where macro is defined")
|
f.addError(pos, "internal error: could not find file where macro is defined")
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
||||||
if file != endFile {
|
if file != endFile {
|
||||||
p.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
f.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
if startOffset > endOffset {
|
if startOffset > endOffset {
|
||||||
p.addError(pos, "internal error: start offset of macro is after end offset")
|
f.addError(pos, "internal error: start offset of macro is after end offset")
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// read file contents and extract the relevant byte range
|
// read file contents and extract the relevant byte range
|
||||||
|
@ -260,31 +351,94 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||||
var size C.size_t
|
var size C.size_t
|
||||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||||
if endOffset >= C.uint(size) {
|
if endOffset >= C.uint(size) {
|
||||||
p.addError(pos, "internal error: end offset of macro lies after end of file")
|
f.addError(pos, "internal error: end offset of macro lies after end of file")
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
||||||
if !strings.HasPrefix(source, name) {
|
if !strings.HasPrefix(source, name) {
|
||||||
p.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
value := source[len(name):]
|
value := source[len(name):]
|
||||||
// Try to convert this #define into a Go constant expression.
|
// Try to convert this #define into a Go constant expression.
|
||||||
expr, scannerError := parseConst(pos+token.Pos(len(name)), p.fset, value)
|
expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value)
|
||||||
if scannerError != nil {
|
if scannerError != nil {
|
||||||
p.errors = append(p.errors, *scannerError)
|
f.errors = append(f.errors, *scannerError)
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
if expr != nil {
|
|
||||||
// Parsing was successful.
|
gen := &ast.GenDecl{
|
||||||
p.constants[name] = constantInfo{expr, pos}
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.CONST,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
}
|
}
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Con,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Values: []ast.Expr{expr},
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
return gen, nil
|
||||||
case C.CXCursor_EnumDecl:
|
case C.CXCursor_EnumDecl:
|
||||||
// Visit all enums, because the fields may be used even when the enum
|
obj := &ast.Object{
|
||||||
// type itself is not.
|
Kind: ast.Typ,
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
Name: "C." + name,
|
||||||
p.makeASTType(typ, pos)
|
}
|
||||||
|
underlying := C.tinygo_clang_getEnumDeclIntegerType(c)
|
||||||
|
// TODO: gc's CGo implementation uses types such as `uint32` for enums
|
||||||
|
// instead of types such as C.int, which are used here.
|
||||||
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Assign: pos,
|
||||||
|
Type: f.makeASTType(underlying, pos),
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
return typeSpec, nil
|
||||||
|
case C.CXCursor_EnumConstantDecl:
|
||||||
|
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||||
|
expr := &ast.BasicLit{
|
||||||
|
ValuePos: pos,
|
||||||
|
Kind: token.INT,
|
||||||
|
Value: strconv.FormatInt(int64(value), 10),
|
||||||
|
}
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.CONST,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
|
}
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Con,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Values: []ast.Expr{expr},
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
return gen, nil
|
||||||
|
default:
|
||||||
|
f.addError(pos, fmt.Sprintf("internal error: unknown cursor type: %d", kind))
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getString(clangString C.CXString) (s string) {
|
func getString(clangString C.CXString) (s string) {
|
||||||
|
@ -294,6 +448,49 @@ func getString(clangString C.CXString) (s string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export tinygo_clang_globals_visitor
|
||||||
|
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
|
f := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoFile)
|
||||||
|
switch C.tinygo_clang_getCursorKind(c) {
|
||||||
|
case C.CXCursor_FunctionDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
f.names[name] = c
|
||||||
|
case C.CXCursor_StructDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if name != "" {
|
||||||
|
f.names["struct_"+name] = c
|
||||||
|
}
|
||||||
|
case C.CXCursor_UnionDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if name != "" {
|
||||||
|
f.names["union_"+name] = c
|
||||||
|
}
|
||||||
|
case C.CXCursor_TypedefDecl:
|
||||||
|
typedefType := C.tinygo_clang_getCursorType(c)
|
||||||
|
name := getString(C.clang_getTypedefName(typedefType))
|
||||||
|
f.names[name] = c
|
||||||
|
case C.CXCursor_VarDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
f.names[name] = c
|
||||||
|
case C.CXCursor_MacroDefinition:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
f.names[name] = c
|
||||||
|
case C.CXCursor_EnumDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if name != "" {
|
||||||
|
// Named enum, which can be referenced from Go using C.enum_foo.
|
||||||
|
f.names["enum_"+name] = c
|
||||||
|
}
|
||||||
|
// The enum fields are in global scope, so recurse to visit them.
|
||||||
|
return C.CXChildVisit_Recurse
|
||||||
|
case C.CXCursor_EnumConstantDecl:
|
||||||
|
// We arrive here because of the "Recurse" above.
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
f.names[name] = c
|
||||||
|
}
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
|
||||||
// getCursorPosition returns a usable token.Pos from a libclang cursor.
|
// getCursorPosition returns a usable token.Pos from a libclang cursor.
|
||||||
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||||
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
|
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
|
||||||
|
@ -391,7 +588,7 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
|
||||||
// makeDecayingASTType does the same as makeASTType but takes care of decaying
|
// makeDecayingASTType does the same as makeASTType but takes care of decaying
|
||||||
// types (arrays in function parameters, etc). It is otherwise identical to
|
// types (arrays in function parameters, etc). It is otherwise identical to
|
||||||
// makeASTType.
|
// makeASTType.
|
||||||
func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
// Strip typedefs, if any.
|
// Strip typedefs, if any.
|
||||||
underlyingType := typ
|
underlyingType := typ
|
||||||
if underlyingType.kind == C.CXType_Typedef {
|
if underlyingType.kind == C.CXType_Typedef {
|
||||||
|
@ -417,15 +614,15 @@ func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
pointeeType := C.clang_getElementType(underlyingType)
|
pointeeType := C.clang_getElementType(underlyingType)
|
||||||
return &ast.StarExpr{
|
return &ast.StarExpr{
|
||||||
Star: pos,
|
Star: pos,
|
||||||
X: p.makeASTType(pointeeType, pos),
|
X: f.makeASTType(pointeeType, pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return p.makeASTType(typ, pos)
|
return f.makeASTType(typ, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
||||||
// it converts a libclang type to a type in the Go AST.
|
// it converts a libclang type to a type in the Go AST.
|
||||||
func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
var typeName string
|
var typeName string
|
||||||
switch typ.kind {
|
switch typ.kind {
|
||||||
case C.CXType_Char_S, C.CXType_Char_U:
|
case C.CXType_Char_S, C.CXType_Char_U:
|
||||||
|
@ -486,7 +683,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
}
|
}
|
||||||
return &ast.StarExpr{
|
return &ast.StarExpr{
|
||||||
Star: pos,
|
Star: pos,
|
||||||
X: p.makeASTType(pointeeType, pos),
|
X: f.makeASTType(pointeeType, pos),
|
||||||
}
|
}
|
||||||
case C.CXType_ConstantArray:
|
case C.CXType_ConstantArray:
|
||||||
return &ast.ArrayType{
|
return &ast.ArrayType{
|
||||||
|
@ -496,7 +693,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
Kind: token.INT,
|
Kind: token.INT,
|
||||||
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
||||||
},
|
},
|
||||||
Elt: p.makeASTType(C.clang_getElementType(typ), pos),
|
Elt: f.makeASTType(C.clang_getElementType(typ), pos),
|
||||||
}
|
}
|
||||||
case C.CXType_FunctionProto:
|
case C.CXType_FunctionProto:
|
||||||
// Be compatible with gc, which uses the *[0]byte type for function
|
// Be compatible with gc, which uses the *[0]byte type for function
|
||||||
|
@ -517,71 +714,21 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
}
|
}
|
||||||
case C.CXType_Typedef:
|
case C.CXType_Typedef:
|
||||||
name := getString(C.clang_getTypedefName(typ))
|
name := getString(C.clang_getTypedefName(typ))
|
||||||
if _, ok := p.typedefs[name]; !ok {
|
|
||||||
p.typedefs[name] = nil // don't recurse
|
|
||||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
|
||||||
expr := p.makeASTType(underlyingType, pos)
|
|
||||||
if strings.HasPrefix(name, "_Cgo_") {
|
|
||||||
expr := expr.(*ast.Ident)
|
|
||||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
|
||||||
switch expr.Name {
|
|
||||||
case "C.char":
|
|
||||||
if typeSize != 1 {
|
|
||||||
// This happens for some very special purpose architectures
|
|
||||||
// (DSPs etc.) that are not currently targeted.
|
|
||||||
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
|
||||||
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
|
|
||||||
}
|
|
||||||
switch underlyingType.kind {
|
|
||||||
case C.CXType_Char_S:
|
|
||||||
expr.Name = "int8"
|
|
||||||
case C.CXType_Char_U:
|
|
||||||
expr.Name = "uint8"
|
|
||||||
}
|
|
||||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
|
||||||
switch typeSize {
|
|
||||||
case 1:
|
|
||||||
expr.Name = "int8"
|
|
||||||
case 2:
|
|
||||||
expr.Name = "int16"
|
|
||||||
case 4:
|
|
||||||
expr.Name = "int32"
|
|
||||||
case 8:
|
|
||||||
expr.Name = "int64"
|
|
||||||
}
|
|
||||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
|
||||||
switch typeSize {
|
|
||||||
case 1:
|
|
||||||
expr.Name = "uint8"
|
|
||||||
case 2:
|
|
||||||
expr.Name = "uint16"
|
|
||||||
case 4:
|
|
||||||
expr.Name = "uint32"
|
|
||||||
case 8:
|
|
||||||
expr.Name = "uint64"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.typedefs[name] = &typedefInfo{
|
|
||||||
typeExpr: expr,
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "C." + name,
|
Name: f.getASTDeclName(name, c, false),
|
||||||
}
|
}
|
||||||
case C.CXType_Elaborated:
|
case C.CXType_Elaborated:
|
||||||
underlying := C.clang_Type_getNamedType(typ)
|
underlying := C.clang_Type_getNamedType(typ)
|
||||||
switch underlying.kind {
|
switch underlying.kind {
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
return p.makeASTType(underlying, pos)
|
return f.makeASTType(underlying, pos)
|
||||||
case C.CXType_Enum:
|
case C.CXType_Enum:
|
||||||
return p.makeASTType(underlying, pos)
|
return f.makeASTType(underlying, pos)
|
||||||
default:
|
default:
|
||||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
|
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
|
||||||
p.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||||
typeName = "<unknown>"
|
typeName = "<unknown>"
|
||||||
}
|
}
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
|
@ -599,63 +746,46 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
}
|
}
|
||||||
if name == "" {
|
if name == "" {
|
||||||
// Anonymous record, probably inside a typedef.
|
// Anonymous record, probably inside a typedef.
|
||||||
typeInfo := p.makeASTRecordType(cursor, pos)
|
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
||||||
if typeInfo.bitfields != nil || typeInfo.unionSize != 0 {
|
var file C.CXFile
|
||||||
// This record is a union or is a struct with bitfields, so we
|
var line C.unsigned
|
||||||
// have to declare it as a named type (for getters/setters to
|
var column C.unsigned
|
||||||
// work).
|
C.clang_getFileLocation(clangLocation, &file, &line, &column, nil)
|
||||||
p.anonStructNum++
|
location := token.Position{
|
||||||
cgoName := cgoRecordPrefix + strconv.Itoa(p.anonStructNum)
|
Filename: getString(C.clang_getFileName(file)),
|
||||||
p.elaboratedTypes[cgoName] = typeInfo
|
Line: int(line),
|
||||||
return &ast.Ident{
|
Column: int(column),
|
||||||
NamePos: pos,
|
|
||||||
Name: "C." + cgoName,
|
|
||||||
}
|
}
|
||||||
|
if location.Filename == "" || location.Line == 0 {
|
||||||
|
// Not sure when this would happen, but protect from it anyway.
|
||||||
|
f.addError(pos, "could not find file/line information")
|
||||||
}
|
}
|
||||||
return typeInfo.typeExpr
|
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
|
||||||
} else {
|
} else {
|
||||||
cgoName := cgoRecordPrefix + name
|
name = cgoRecordPrefix + name
|
||||||
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
|
||||||
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
|
||||||
p.elaboratedTypes[cgoName] = p.makeASTRecordType(cursor, pos)
|
|
||||||
}
|
}
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "C." + cgoName,
|
Name: f.getASTDeclName(name, cursor, false),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case C.CXType_Enum:
|
case C.CXType_Enum:
|
||||||
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))
|
||||||
underlying := C.tinygo_clang_getEnumDeclIntegerType(cursor)
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
// anonymous enum
|
name = f.getUnnamedDeclName("_Ctype_enum___", cursor)
|
||||||
ref := storedRefs.Put(p)
|
|
||||||
defer storedRefs.Remove(ref)
|
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
|
||||||
return p.makeASTType(underlying, pos)
|
|
||||||
} else {
|
} else {
|
||||||
// named enum
|
name = "enum_" + name
|
||||||
if _, ok := p.enums[name]; !ok {
|
|
||||||
ref := storedRefs.Put(p)
|
|
||||||
defer storedRefs.Remove(ref)
|
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
|
||||||
p.enums[name] = enumInfo{
|
|
||||||
typeExpr: p.makeASTType(underlying, pos),
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "C.enum_" + name,
|
Name: f.getASTDeclName(name, cursor, false),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if typeName == "" {
|
if typeName == "" {
|
||||||
// Report this as an error.
|
// Report this as an error.
|
||||||
typeSpelling := getString(C.clang_getTypeSpelling(typ))
|
typeSpelling := getString(C.clang_getTypeSpelling(typ))
|
||||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind))
|
typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind))
|
||||||
p.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
||||||
typeName = "C.<unknown>"
|
typeName = "C.<unknown>"
|
||||||
}
|
}
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
|
@ -664,9 +794,80 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getIntegerType returns an AST node that defines types such as C.int.
|
||||||
|
func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSpec {
|
||||||
|
pos := p.getCursorPosition(cursor)
|
||||||
|
|
||||||
|
// Find a Go type that matches the size and signedness of the given C type.
|
||||||
|
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(cursor)
|
||||||
|
var goName string
|
||||||
|
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||||
|
switch name {
|
||||||
|
case "C.char":
|
||||||
|
if typeSize != 1 {
|
||||||
|
// This happens for some very special purpose architectures
|
||||||
|
// (DSPs etc.) that are not currently targeted.
|
||||||
|
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
||||||
|
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
|
||||||
|
}
|
||||||
|
switch underlyingType.kind {
|
||||||
|
case C.CXType_Char_S:
|
||||||
|
goName = "int8"
|
||||||
|
case C.CXType_Char_U:
|
||||||
|
goName = "uint8"
|
||||||
|
}
|
||||||
|
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||||
|
switch typeSize {
|
||||||
|
case 1:
|
||||||
|
goName = "int8"
|
||||||
|
case 2:
|
||||||
|
goName = "int16"
|
||||||
|
case 4:
|
||||||
|
goName = "int32"
|
||||||
|
case 8:
|
||||||
|
goName = "int64"
|
||||||
|
}
|
||||||
|
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||||
|
switch typeSize {
|
||||||
|
case 1:
|
||||||
|
goName = "uint8"
|
||||||
|
case 2:
|
||||||
|
goName = "uint16"
|
||||||
|
case 4:
|
||||||
|
goName = "uint32"
|
||||||
|
case 8:
|
||||||
|
goName = "uint64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if goName == "" { // should not happen
|
||||||
|
p.addError(pos, "internal error: did not find Go type for C type "+name)
|
||||||
|
goName = "int"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct an *ast.TypeSpec for this type.
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
spec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: name,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: goName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
obj.Decl = spec
|
||||||
|
return spec
|
||||||
|
}
|
||||||
|
|
||||||
// makeASTRecordType parses a C record (struct or union) and translates it into
|
// makeASTRecordType parses a C record (struct or union) and translates it into
|
||||||
// a Go struct type.
|
// a Go struct type.
|
||||||
func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
||||||
fieldList := &ast.FieldList{
|
fieldList := &ast.FieldList{
|
||||||
Opening: pos,
|
Opening: pos,
|
||||||
Closing: pos,
|
Closing: pos,
|
||||||
|
@ -676,11 +877,11 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
||||||
bitfieldNum := 0
|
bitfieldNum := 0
|
||||||
ref := storedRefs.Put(struct {
|
ref := storedRefs.Put(struct {
|
||||||
fieldList *ast.FieldList
|
fieldList *ast.FieldList
|
||||||
pkg *cgoPackage
|
file *cgoFile
|
||||||
inBitfield *bool
|
inBitfield *bool
|
||||||
bitfieldNum *int
|
bitfieldNum *int
|
||||||
bitfieldList *[]bitfieldInfo
|
bitfieldList *[]bitfieldInfo
|
||||||
}{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
|
}{fieldList, f, &inBitfield, &bitfieldNum, &bitfieldList})
|
||||||
defer storedRefs.Remove(ref)
|
defer storedRefs.Remove(ref)
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||||
renameFieldKeywords(fieldList)
|
renameFieldKeywords(fieldList)
|
||||||
|
@ -709,13 +910,13 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
||||||
}
|
}
|
||||||
if bitfieldList != nil {
|
if bitfieldList != nil {
|
||||||
// This is valid C... but please don't do this.
|
// This is valid C... but please don't do this.
|
||||||
p.addError(pos, "bitfield in a union is not supported")
|
f.addError(pos, "bitfield in a union is not supported")
|
||||||
}
|
}
|
||||||
typ := C.tinygo_clang_getCursorType(cursor)
|
typ := C.tinygo_clang_getCursorType(cursor)
|
||||||
alignInBytes := int64(C.clang_Type_getAlignOf(typ))
|
alignInBytes := int64(C.clang_Type_getAlignOf(typ))
|
||||||
sizeInBytes := int64(C.clang_Type_getSizeOf(typ))
|
sizeInBytes := int64(C.clang_Type_getSizeOf(typ))
|
||||||
if sizeInBytes == 0 {
|
if sizeInBytes == 0 {
|
||||||
p.addError(pos, "zero-length union is not supported")
|
f.addError(pos, "zero-length union is not supported")
|
||||||
}
|
}
|
||||||
typeInfo.unionSize = sizeInBytes
|
typeInfo.unionSize = sizeInBytes
|
||||||
typeInfo.unionAlign = alignInBytes
|
typeInfo.unionAlign = alignInBytes
|
||||||
|
@ -723,7 +924,7 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
||||||
default:
|
default:
|
||||||
cursorKind := C.tinygo_clang_getCursorKind(cursor)
|
cursorKind := C.tinygo_clang_getCursorKind(cursor)
|
||||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||||
p.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
f.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
||||||
return &elaboratedTypeInfo{
|
return &elaboratedTypeInfo{
|
||||||
typeExpr: &ast.StructType{
|
typeExpr: &ast.StructType{
|
||||||
Struct: pos,
|
Struct: pos,
|
||||||
|
@ -737,17 +938,17 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
||||||
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 {
|
||||||
fieldList *ast.FieldList
|
fieldList *ast.FieldList
|
||||||
pkg *cgoPackage
|
file *cgoFile
|
||||||
inBitfield *bool
|
inBitfield *bool
|
||||||
bitfieldNum *int
|
bitfieldNum *int
|
||||||
bitfieldList *[]bitfieldInfo
|
bitfieldList *[]bitfieldInfo
|
||||||
})
|
})
|
||||||
fieldList := passed.fieldList
|
fieldList := passed.fieldList
|
||||||
p := passed.pkg
|
f := passed.file
|
||||||
inBitfield := passed.inBitfield
|
inBitfield := passed.inBitfield
|
||||||
bitfieldNum := passed.bitfieldNum
|
bitfieldNum := passed.bitfieldNum
|
||||||
bitfieldList := passed.bitfieldList
|
bitfieldList := passed.bitfieldList
|
||||||
pos := p.getCursorPosition(c)
|
pos := f.getCursorPosition(c)
|
||||||
switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind {
|
switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind {
|
||||||
case C.CXCursor_FieldDecl:
|
case C.CXCursor_FieldDecl:
|
||||||
// Expected. This is a regular field.
|
// Expected. This is a regular field.
|
||||||
|
@ -756,7 +957,7 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
default:
|
default:
|
||||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||||
p.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
|
f.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
@ -767,14 +968,14 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||||
}
|
}
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
typ := C.tinygo_clang_getCursorType(c)
|
||||||
field := &ast.Field{
|
field := &ast.Field{
|
||||||
Type: p.makeASTType(typ, p.getCursorPosition(c)),
|
Type: f.makeASTType(typ, f.getCursorPosition(c)),
|
||||||
}
|
}
|
||||||
offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name)))
|
offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name)))
|
||||||
alignOf := int64(C.clang_Type_getAlignOf(typ) * 8)
|
alignOf := int64(C.clang_Type_getAlignOf(typ) * 8)
|
||||||
bitfieldOffset := offsetof % alignOf
|
bitfieldOffset := offsetof % alignOf
|
||||||
if bitfieldOffset != 0 {
|
if bitfieldOffset != 0 {
|
||||||
if C.tinygo_clang_Cursor_isBitField(c) != 1 {
|
if C.tinygo_clang_Cursor_isBitField(c) != 1 {
|
||||||
p.addError(pos, "expected a bitfield")
|
f.addError(pos, "expected a bitfield")
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
if !*inBitfield {
|
if !*inBitfield {
|
||||||
|
@ -821,23 +1022,6 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
//export tinygo_clang_enum_visitor
|
|
||||||
func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
|
||||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
pos := p.getCursorPosition(c)
|
|
||||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
|
||||||
p.constants[name] = constantInfo{
|
|
||||||
expr: &ast.BasicLit{
|
|
||||||
ValuePos: pos,
|
|
||||||
Kind: token.INT,
|
|
||||||
Value: strconv.FormatInt(int64(value), 10),
|
|
||||||
},
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
|
|
||||||
//export tinygo_clang_inclusion_visitor
|
//export tinygo_clang_inclusion_visitor
|
||||||
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
|
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
|
||||||
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
|
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
|
||||||
|
|
33
cgo/testdata/basic.out.go
предоставленный
33
cgo/testdata/basic.out.go
предоставленный
|
@ -24,23 +24,16 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
type C.int16_t = int16
|
type (
|
||||||
type C.int32_t = int32
|
C.char uint8
|
||||||
type C.int64_t = int64
|
C.schar int8
|
||||||
type C.int8_t = int8
|
C.uchar uint8
|
||||||
type C.uint16_t = uint16
|
C.short int16
|
||||||
type C.uint32_t = uint32
|
C.ushort uint16
|
||||||
type C.uint64_t = uint64
|
C.int int32
|
||||||
type C.uint8_t = uint8
|
C.uint uint32
|
||||||
type C.uintptr_t = uintptr
|
C.long int32
|
||||||
type C.char uint8
|
C.ulong uint32
|
||||||
type C.int int32
|
C.longlong int64
|
||||||
type C.long int32
|
C.ulonglong uint64
|
||||||
type C.longlong int64
|
)
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
|
|
37
cgo/testdata/const.out.go
предоставленный
37
cgo/testdata/const.out.go
предоставленный
|
@ -24,26 +24,19 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
const C.bar = C.foo
|
type (
|
||||||
const C.foo = 3
|
C.char uint8
|
||||||
|
C.schar int8
|
||||||
|
C.uchar uint8
|
||||||
|
C.short int16
|
||||||
|
C.ushort uint16
|
||||||
|
C.int int32
|
||||||
|
C.uint uint32
|
||||||
|
C.long int32
|
||||||
|
C.ulong uint32
|
||||||
|
C.longlong int64
|
||||||
|
C.ulonglong uint64
|
||||||
|
)
|
||||||
|
|
||||||
type C.int16_t = int16
|
const C.foo = 3
|
||||||
type C.int32_t = int32
|
const C.bar = C.foo
|
||||||
type C.int64_t = int64
|
|
||||||
type C.int8_t = int8
|
|
||||||
type C.uint16_t = uint16
|
|
||||||
type C.uint32_t = uint32
|
|
||||||
type C.uint64_t = uint64
|
|
||||||
type C.uint8_t = uint8
|
|
||||||
type C.uintptr_t = uintptr
|
|
||||||
type C.char uint8
|
|
||||||
type C.int int32
|
|
||||||
type C.long int32
|
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
|
|
2
cgo/testdata/errors.go
предоставленный
2
cgo/testdata/errors.go
предоставленный
|
@ -27,7 +27,7 @@ import "C"
|
||||||
//line errors.go:100
|
//line errors.go:100
|
||||||
var (
|
var (
|
||||||
// constant too large
|
// constant too large
|
||||||
_ C.uint8_t = 2 << 10
|
_ C.char = 2 << 10
|
||||||
|
|
||||||
// z member does not exist
|
// z member does not exist
|
||||||
_ C.point_t = C.point_t{z: 3}
|
_ C.point_t = C.point_t{z: 3}
|
||||||
|
|
42
cgo/testdata/errors.out.go
предоставленный
42
cgo/testdata/errors.out.go
предоставленный
|
@ -6,7 +6,7 @@
|
||||||
// testdata/errors.go:19:26: unexpected token ), expected end of expression
|
// testdata/errors.go:19:26: unexpected token ), expected end of expression
|
||||||
|
|
||||||
// Type checking errors after CGo processing:
|
// Type checking errors after CGo processing:
|
||||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
|
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
|
||||||
// testdata/errors.go:105: unknown field z in struct literal
|
// testdata/errors.go:105: unknown field z in struct literal
|
||||||
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
|
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
|
||||||
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
||||||
|
@ -38,29 +38,23 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
const C.SOME_CONST_3 = 1234
|
type (
|
||||||
|
C.char uint8
|
||||||
type C.int16_t = int16
|
C.schar int8
|
||||||
type C.int32_t = int32
|
C.uchar uint8
|
||||||
type C.int64_t = int64
|
C.short int16
|
||||||
type C.int8_t = int8
|
C.ushort uint16
|
||||||
type C.uint16_t = uint16
|
C.int int32
|
||||||
type C.uint32_t = uint32
|
C.uint uint32
|
||||||
type C.uint64_t = uint64
|
C.long int32
|
||||||
type C.uint8_t = uint8
|
C.ulong uint32
|
||||||
type C.uintptr_t = uintptr
|
C.longlong int64
|
||||||
type C.char uint8
|
C.ulonglong uint64
|
||||||
type C.int int32
|
)
|
||||||
type C.long int32
|
type C._Ctype_struct___0 struct {
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
type C.point_t = struct {
|
|
||||||
x C.int
|
x C.int
|
||||||
y C.int
|
y C.int
|
||||||
}
|
}
|
||||||
|
type C.point_t = C._Ctype_struct___0
|
||||||
|
|
||||||
|
const C.SOME_CONST_3 = 1234
|
||||||
|
|
35
cgo/testdata/flags.out.go
предоставленный
35
cgo/testdata/flags.out.go
предоставленный
|
@ -29,26 +29,19 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
C.char uint8
|
||||||
|
C.schar int8
|
||||||
|
C.uchar uint8
|
||||||
|
C.short int16
|
||||||
|
C.ushort uint16
|
||||||
|
C.int int32
|
||||||
|
C.uint uint32
|
||||||
|
C.long int32
|
||||||
|
C.ulong uint32
|
||||||
|
C.longlong int64
|
||||||
|
C.ulonglong uint64
|
||||||
|
)
|
||||||
|
|
||||||
const C.BAR = 3
|
const C.BAR = 3
|
||||||
const C.FOO_H = 1
|
const C.FOO_H = 1
|
||||||
|
|
||||||
type C.int16_t = int16
|
|
||||||
type C.int32_t = int32
|
|
||||||
type C.int64_t = int64
|
|
||||||
type C.int8_t = int8
|
|
||||||
type C.uint16_t = uint16
|
|
||||||
type C.uint32_t = uint32
|
|
||||||
type C.uint64_t = uint64
|
|
||||||
type C.uint8_t = uint8
|
|
||||||
type C.uintptr_t = uintptr
|
|
||||||
type C.char uint8
|
|
||||||
type C.int int32
|
|
||||||
type C.long int32
|
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
|
|
41
cgo/testdata/symbols.out.go
предоставленный
41
cgo/testdata/symbols.out.go
предоставленный
|
@ -24,41 +24,36 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
C.char uint8
|
||||||
|
C.schar int8
|
||||||
|
C.uchar uint8
|
||||||
|
C.short int16
|
||||||
|
C.ushort uint16
|
||||||
|
C.int int32
|
||||||
|
C.uint uint32
|
||||||
|
C.long int32
|
||||||
|
C.ulong uint32
|
||||||
|
C.longlong int64
|
||||||
|
C.ulonglong uint64
|
||||||
|
)
|
||||||
|
|
||||||
//export foo
|
//export foo
|
||||||
func C.foo(a C.int, b C.int) C.int
|
func C.foo(a C.int, b C.int) C.int
|
||||||
|
|
||||||
|
var C.foo$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
//export variadic0
|
//export variadic0
|
||||||
//go:variadic
|
//go:variadic
|
||||||
func C.variadic0()
|
func C.variadic0()
|
||||||
|
|
||||||
|
var C.variadic0$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
//export variadic2
|
//export variadic2
|
||||||
//go:variadic
|
//go:variadic
|
||||||
func C.variadic2(x C.int, y C.int)
|
func C.variadic2(x C.int, y C.int)
|
||||||
|
|
||||||
var C.foo$funcaddr unsafe.Pointer
|
|
||||||
var C.variadic0$funcaddr unsafe.Pointer
|
|
||||||
var C.variadic2$funcaddr unsafe.Pointer
|
var C.variadic2$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
//go:extern someValue
|
//go:extern someValue
|
||||||
var C.someValue C.int
|
var C.someValue C.int
|
||||||
|
|
||||||
type C.int16_t = int16
|
|
||||||
type C.int32_t = int32
|
|
||||||
type C.int64_t = int64
|
|
||||||
type C.int8_t = int8
|
|
||||||
type C.uint16_t = uint16
|
|
||||||
type C.uint32_t = uint32
|
|
||||||
type C.uint64_t = uint64
|
|
||||||
type C.uint8_t = uint8
|
|
||||||
type C.uintptr_t = uintptr
|
|
||||||
type C.char uint8
|
|
||||||
type C.int int32
|
|
||||||
type C.long int32
|
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
|
|
202
cgo/testdata/types.out.go
предоставленный
202
cgo/testdata/types.out.go
предоставленный
|
@ -24,132 +24,126 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
const C.option2A = 20
|
type (
|
||||||
const C.optionA = 0
|
C.char uint8
|
||||||
const C.optionB = 1
|
C.schar int8
|
||||||
const C.optionC = -5
|
C.uchar uint8
|
||||||
const C.optionD = -4
|
C.short int16
|
||||||
const C.optionE = 10
|
C.ushort uint16
|
||||||
const C.optionF = 11
|
C.int int32
|
||||||
const C.optionG = 12
|
C.uint uint32
|
||||||
const C.unused1 = 5
|
C.long int32
|
||||||
|
C.ulong uint32
|
||||||
type C.int16_t = int16
|
C.longlong int64
|
||||||
type C.int32_t = int32
|
C.ulonglong uint64
|
||||||
type C.int64_t = int64
|
)
|
||||||
type C.int8_t = int8
|
|
||||||
type C.uint16_t = uint16
|
|
||||||
type C.uint32_t = uint32
|
|
||||||
type C.uint64_t = uint64
|
|
||||||
type C.uint8_t = uint8
|
|
||||||
type C.uintptr_t = uintptr
|
|
||||||
type C.char uint8
|
|
||||||
type C.int int32
|
|
||||||
type C.long int32
|
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
type C.bitfield_t = C.struct_4
|
|
||||||
type C.myIntArray = [10]C.int
|
|
||||||
type C.myint = C.int
|
type C.myint = C.int
|
||||||
type C.option2_t = C.uint
|
type C._Ctype_struct___0 struct {
|
||||||
type C.option_t = C.enum_option
|
|
||||||
type C.point2d_t = struct {
|
|
||||||
x C.int
|
x C.int
|
||||||
y C.int
|
y C.int
|
||||||
}
|
}
|
||||||
type C.point3d_t = C.struct_point3d
|
type C.point2d_t = C._Ctype_struct___0
|
||||||
type C.struct_nested_t = struct {
|
|
||||||
begin C.point2d_t
|
|
||||||
end C.point2d_t
|
|
||||||
tag C.int
|
|
||||||
|
|
||||||
coord C.union_2
|
|
||||||
}
|
|
||||||
type C.types_t = struct {
|
|
||||||
f float32
|
|
||||||
d float64
|
|
||||||
ptr *C.int
|
|
||||||
}
|
|
||||||
type C.union1_t = struct{ i C.int }
|
|
||||||
type C.union2d_t = C.union_union2d
|
|
||||||
type C.union3_t = C.union_1
|
|
||||||
type C.union_nested_t = C.union_3
|
|
||||||
type C.unionarray_t = struct{ arr [10]C.uchar }
|
|
||||||
|
|
||||||
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
|
||||||
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
|
|
||||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) bitfield_b() C.uchar {
|
|
||||||
return s.__bitfield_1 >> 5 & 0x1
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) set_bitfield_b(value C.uchar) {
|
|
||||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) bitfield_c() C.uchar {
|
|
||||||
return s.__bitfield_1 >> 6
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) set_bitfield_c(value C.uchar,
|
|
||||||
|
|
||||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
|
||||||
|
|
||||||
type C.struct_4 struct {
|
|
||||||
start C.uchar
|
|
||||||
__bitfield_1 C.uchar
|
|
||||||
|
|
||||||
d C.uchar
|
|
||||||
e C.uchar
|
|
||||||
}
|
|
||||||
type C.struct_point3d struct {
|
type C.struct_point3d struct {
|
||||||
x C.int
|
x C.int
|
||||||
y C.int
|
y C.int
|
||||||
z C.int
|
z C.int
|
||||||
}
|
}
|
||||||
|
type C.point3d_t = C.struct_point3d
|
||||||
type C.struct_type1 struct {
|
type C.struct_type1 struct {
|
||||||
_type C.int
|
_type C.int
|
||||||
__type C.int
|
__type C.int
|
||||||
___type C.int
|
___type C.int
|
||||||
}
|
}
|
||||||
type C.struct_type2 struct{ _type C.int }
|
type C.struct_type2 struct{ _type C.int }
|
||||||
|
type C._Ctype_union___1 struct{ i C.int }
|
||||||
|
type C.union1_t = C._Ctype_union___1
|
||||||
|
type C._Ctype_union___2 struct{ $union uint64 }
|
||||||
|
|
||||||
func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
func (union *C._Ctype_union___2) unionfield_i() *C.int {
|
||||||
func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
|
return (*C.int)(unsafe.Pointer(&union.$union))
|
||||||
func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
|
|
||||||
|
|
||||||
type C.union_1 struct{ $union uint64 }
|
|
||||||
|
|
||||||
func (union *C.union_2) unionfield_area() *C.point2d_t {
|
|
||||||
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
}
|
||||||
func (union *C.union_2) unionfield_solid() *C.point3d_t {
|
func (union *C._Ctype_union___2) unionfield_d() *float64 {
|
||||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
return (*float64)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
func (union *C._Ctype_union___2) unionfield_s() *C.short {
|
||||||
|
return (*C.short)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
|
|
||||||
type C.union_2 struct{ $union [3]uint32 }
|
type C.union3_t = C._Ctype_union___2
|
||||||
|
type C.union_union2d struct{ $union [2]uint64 }
|
||||||
func (union *C.union_3) unionfield_point() *C.point3d_t {
|
|
||||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
func (union *C.union_3) unionfield_array() *C.unionarray_t {
|
|
||||||
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
func (union *C.union_3) unionfield_thing() *C.union3_t {
|
|
||||||
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
|
|
||||||
type C.union_3 struct{ $union [2]uint64 }
|
|
||||||
|
|
||||||
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||||
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
||||||
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
|
|
||||||
type C.union_union2d struct{ $union [2]uint64 }
|
type C.union2d_t = C.union_union2d
|
||||||
type C.enum_option C.int
|
type C._Ctype_union___3 struct{ arr [10]C.uchar }
|
||||||
type C.enum_unused C.uint
|
type C.unionarray_t = C._Ctype_union___3
|
||||||
|
type C._Ctype_union___5 struct{ $union [3]uint32 }
|
||||||
|
|
||||||
|
func (union *C._Ctype_union___5) unionfield_area() *C.point2d_t {
|
||||||
|
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
func (union *C._Ctype_union___5) unionfield_solid() *C.point3d_t {
|
||||||
|
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
|
||||||
|
type C._Ctype_struct___4 struct {
|
||||||
|
begin C.point2d_t
|
||||||
|
end C.point2d_t
|
||||||
|
tag C.int
|
||||||
|
|
||||||
|
coord C._Ctype_union___5
|
||||||
|
}
|
||||||
|
type C.struct_nested_t = C._Ctype_struct___4
|
||||||
|
type C._Ctype_union___6 struct{ $union [2]uint64 }
|
||||||
|
|
||||||
|
func (union *C._Ctype_union___6) unionfield_point() *C.point3d_t {
|
||||||
|
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
func (union *C._Ctype_union___6) unionfield_array() *C.unionarray_t {
|
||||||
|
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
func (union *C._Ctype_union___6) unionfield_thing() *C.union3_t {
|
||||||
|
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
|
||||||
|
type C.union_nested_t = C._Ctype_union___6
|
||||||
|
type C.enum_option = C.int
|
||||||
|
type C.option_t = C.enum_option
|
||||||
|
type C._Ctype_enum___7 = C.uint
|
||||||
|
type C.option2_t = C._Ctype_enum___7
|
||||||
|
type C._Ctype_struct___8 struct {
|
||||||
|
f float32
|
||||||
|
d float64
|
||||||
|
ptr *C.int
|
||||||
|
}
|
||||||
|
type C.types_t = C._Ctype_struct___8
|
||||||
|
type C.myIntArray = [10]C.int
|
||||||
|
type C._Ctype_struct___9 struct {
|
||||||
|
start C.uchar
|
||||||
|
__bitfield_1 C.uchar
|
||||||
|
|
||||||
|
d C.uchar
|
||||||
|
e C.uchar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||||
|
func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) {
|
||||||
|
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||||
|
}
|
||||||
|
func (s *C._Ctype_struct___9) bitfield_b() C.uchar {
|
||||||
|
return s.__bitfield_1 >> 5 & 0x1
|
||||||
|
}
|
||||||
|
func (s *C._Ctype_struct___9) set_bitfield_b(value C.uchar) {
|
||||||
|
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||||
|
}
|
||||||
|
func (s *C._Ctype_struct___9) bitfield_c() C.uchar {
|
||||||
|
return s.__bitfield_1 >> 6
|
||||||
|
}
|
||||||
|
func (s *C._Ctype_struct___9) set_bitfield_c(value C.uchar,
|
||||||
|
|
||||||
|
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||||
|
|
||||||
|
type C.bitfield_t = C._Ctype_struct___9
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче