cgo: add support for #define constants
These are converted to Go constants where possible.
Этот коммит содержится в:
родитель
eb0ce8a298
коммит
99587fe073
6 изменённых файлов: 150 добавлений и 2 удалений
|
@ -18,6 +18,7 @@ type fileInfo struct {
|
||||||
*ast.File
|
*ast.File
|
||||||
*Package
|
*Package
|
||||||
filename string
|
filename string
|
||||||
|
constants map[string]*ast.BasicLit
|
||||||
functions map[string]*functionInfo
|
functions map[string]*functionInfo
|
||||||
globals map[string]*globalInfo
|
globals map[string]*globalInfo
|
||||||
typedefs map[string]*typedefInfo
|
typedefs map[string]*typedefInfo
|
||||||
|
@ -103,6 +104,7 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []er
|
||||||
File: f,
|
File: f,
|
||||||
Package: p,
|
Package: p,
|
||||||
filename: filename,
|
filename: filename,
|
||||||
|
constants: map[string]*ast.BasicLit{},
|
||||||
functions: map[string]*functionInfo{},
|
functions: map[string]*functionInfo{},
|
||||||
globals: map[string]*globalInfo{},
|
globals: map[string]*globalInfo{},
|
||||||
typedefs: map[string]*typedefInfo{},
|
typedefs: map[string]*typedefInfo{},
|
||||||
|
@ -163,6 +165,9 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []er
|
||||||
// Declare stub function pointer values found by libclang.
|
// Declare stub function pointer values found by libclang.
|
||||||
info.addFuncPtrDecls()
|
info.addFuncPtrDecls()
|
||||||
|
|
||||||
|
// Declare globals found by libclang.
|
||||||
|
info.addConstDecls()
|
||||||
|
|
||||||
// Declare globals found by libclang.
|
// Declare globals found by libclang.
|
||||||
info.addVarDecls()
|
info.addVarDecls()
|
||||||
|
|
||||||
|
@ -287,6 +292,49 @@ func (info *fileInfo) addFuncPtrDecls() {
|
||||||
info.Decls = append(info.Decls, gen)
|
info.Decls = append(info.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
|
||||||
|
// C.CONST_FLOAT = 5.8
|
||||||
|
// // ...
|
||||||
|
// )
|
||||||
|
func (info *fileInfo) addConstDecls() {
|
||||||
|
if len(info.constants) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: info.importCPos,
|
||||||
|
Tok: token.CONST,
|
||||||
|
Lparen: info.importCPos,
|
||||||
|
Rparen: info.importCPos,
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(info.constants))
|
||||||
|
for name := range info.constants {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
constVal := info.constants[name]
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Con,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{&ast.Ident{
|
||||||
|
NamePos: info.importCPos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Values: []ast.Expr{constVal},
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
}
|
||||||
|
info.Decls = append(info.Decls, gen)
|
||||||
|
}
|
||||||
|
|
||||||
// addVarDecls declares external C globals in the Go source.
|
// addVarDecls declares external C globals in the Go source.
|
||||||
// It adds code like the following to the AST:
|
// It adds code like the following to the AST:
|
||||||
//
|
//
|
||||||
|
@ -313,7 +361,7 @@ func (info *fileInfo) addVarDecls() {
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
global := info.globals[name]
|
global := info.globals[name]
|
||||||
obj := &ast.Object{
|
obj := &ast.Object{
|
||||||
Kind: ast.Typ,
|
Kind: ast.Var,
|
||||||
Name: "C." + name,
|
Name: "C." + name,
|
||||||
}
|
}
|
||||||
valueSpec := &ast.ValueSpec{
|
valueSpec := &ast.ValueSpec{
|
||||||
|
|
|
@ -47,6 +47,7 @@ CXType tinygo_clang_getCursorResultType(GoCXCursor c);
|
||||||
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
||||||
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
||||||
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
|
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
|
||||||
|
CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
|
||||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
||||||
|
|
||||||
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
|
@ -101,7 +102,7 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam
|
||||||
filenameC,
|
filenameC,
|
||||||
(**C.char)(cmdargsC), C.int(len(cflags)), // command line args
|
(**C.char)(cmdargsC), C.int(len(cflags)), // command line args
|
||||||
&unsavedFile, 1, // unsaved files
|
&unsavedFile, 1, // unsaved files
|
||||||
C.CXTranslationUnit_None,
|
C.CXTranslationUnit_DetailedPreprocessingRecord,
|
||||||
&unit)
|
&unit)
|
||||||
if errCode != 0 {
|
if errCode != 0 {
|
||||||
panic("loader: failed to parse source with libclang")
|
panic("loader: failed to parse source with libclang")
|
||||||
|
@ -220,6 +221,86 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||||
info.globals[name] = &globalInfo{
|
info.globals[name] = &globalInfo{
|
||||||
typeExpr: info.makeASTType(cursorType, pos),
|
typeExpr: info.makeASTType(cursorType, pos),
|
||||||
}
|
}
|
||||||
|
case C.CXCursor_MacroDefinition:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if _, required := info.missingSymbols[name]; !required {
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
||||||
|
start := C.clang_getRangeStart(sourceRange)
|
||||||
|
end := C.clang_getRangeEnd(sourceRange)
|
||||||
|
var file, endFile C.CXFile
|
||||||
|
var startOffset, endOffset C.unsigned
|
||||||
|
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
||||||
|
if file == nil {
|
||||||
|
panic("could not find file where macro is defined")
|
||||||
|
}
|
||||||
|
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
||||||
|
if file != endFile {
|
||||||
|
panic("expected start and end location of a #define to be in the same file")
|
||||||
|
}
|
||||||
|
if startOffset > endOffset {
|
||||||
|
panic("startOffset > endOffset")
|
||||||
|
}
|
||||||
|
|
||||||
|
// read file contents and extract the relevant byte range
|
||||||
|
tu := C.tinygo_clang_Cursor_getTranslationUnit(c)
|
||||||
|
var size C.size_t
|
||||||
|
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||||
|
if endOffset >= C.uint(size) {
|
||||||
|
panic("endOffset lies after end of file")
|
||||||
|
}
|
||||||
|
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
||||||
|
if !strings.HasPrefix(source, name) {
|
||||||
|
panic(fmt.Sprintf("expected #define value to start with %#v, got %#v", name, source))
|
||||||
|
}
|
||||||
|
value := strings.TrimSpace(source[len(name):])
|
||||||
|
for len(value) != 0 && value[0] == '(' && value[len(value)-1] == ')' {
|
||||||
|
value = strings.TrimSpace(value[1 : len(value)-1])
|
||||||
|
}
|
||||||
|
if len(value) == 0 {
|
||||||
|
// Pretend it doesn't exist at all.
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
// For information about integer literals:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/integer_literal
|
||||||
|
if value[0] == '"' {
|
||||||
|
// string constant
|
||||||
|
info.constants[name] = &ast.BasicLit{pos, token.STRING, value}
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
if value[0] == '\'' {
|
||||||
|
// char constant
|
||||||
|
info.constants[name] = &ast.BasicLit{pos, token.CHAR, value}
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
// assume it's a number (int or float)
|
||||||
|
value = strings.Replace(value, "'", "", -1) // remove ' chars
|
||||||
|
value = strings.TrimRight(value, "lu") // remove llu suffixes etc.
|
||||||
|
// find the first non-number
|
||||||
|
nonnum := byte(0)
|
||||||
|
for i := 0; i < len(value); i++ {
|
||||||
|
if value[i] < '0' || value[i] > '9' {
|
||||||
|
nonnum = value[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// determine number type based on the first non-number
|
||||||
|
switch nonnum {
|
||||||
|
case 0:
|
||||||
|
// no non-number found, must be an integer
|
||||||
|
info.constants[name] = &ast.BasicLit{pos, token.INT, value}
|
||||||
|
case 'x', 'X':
|
||||||
|
// hex integer constant
|
||||||
|
// TODO: may also be a floating point number per C++17.
|
||||||
|
info.constants[name] = &ast.BasicLit{pos, token.INT, value}
|
||||||
|
case '.', 'e':
|
||||||
|
// float constant
|
||||||
|
value = strings.TrimRight(value, "fFlL")
|
||||||
|
info.constants[name] = &ast.BasicLit{pos, token.FLOAT, value}
|
||||||
|
default:
|
||||||
|
// unknown type, ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,10 @@ CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) {
|
||||||
return clang_getCursorLocation(c);
|
return clang_getCursorLocation(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CXSourceRange tinygo_clang_getCursorExtent(CXCursor c) {
|
||||||
|
return clang_getCursorExtent(c);
|
||||||
|
}
|
||||||
|
|
||||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(CXCursor c) {
|
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(CXCursor c) {
|
||||||
return clang_Cursor_getTranslationUnit(c);
|
return clang_Cursor_getTranslationUnit(c);
|
||||||
}
|
}
|
||||||
|
|
4
testdata/cgo/main.go
предоставленный
4
testdata/cgo/main.go
предоставленный
|
@ -18,6 +18,10 @@ func main() {
|
||||||
var y C.longlong = -(1 << 40)
|
var y C.longlong = -(1 << 40)
|
||||||
println("longlong:", y)
|
println("longlong:", y)
|
||||||
println("global:", C.global)
|
println("global:", C.global)
|
||||||
|
println("defined ints:", C.CONST_INT, C.CONST_INT2)
|
||||||
|
println("defined floats:", C.CONST_FLOAT, C.CONST_FLOAT2)
|
||||||
|
println("defined string:", C.CONST_STRING)
|
||||||
|
println("defined char:", C.CONST_CHAR)
|
||||||
var ptr C.intPointer
|
var ptr C.intPointer
|
||||||
var n C.int = 15
|
var n C.int = 15
|
||||||
ptr = C.intPointer(&n)
|
ptr = C.intPointer(&n)
|
||||||
|
|
7
testdata/cgo/main.h
предоставленный
7
testdata/cgo/main.h
предоставленный
|
@ -10,6 +10,13 @@ int doCallback(int a, int b, binop_t cb);
|
||||||
typedef int * intPointer;
|
typedef int * intPointer;
|
||||||
void store(int value, int *ptr);
|
void store(int value, int *ptr);
|
||||||
|
|
||||||
|
# define CONST_INT 5
|
||||||
|
# define CONST_INT2 5llu
|
||||||
|
# define CONST_FLOAT 5.8
|
||||||
|
# define CONST_FLOAT2 5.8f
|
||||||
|
# define CONST_CHAR 'c'
|
||||||
|
# define CONST_STRING "defined string"
|
||||||
|
|
||||||
// this signature should not be included by CGo
|
// this signature should not be included by CGo
|
||||||
void unusedFunction2(int x, __builtin_va_list args);
|
void unusedFunction2(int x, __builtin_va_list args);
|
||||||
|
|
||||||
|
|
4
testdata/cgo/out.txt
предоставленный
4
testdata/cgo/out.txt
предоставленный
|
@ -4,6 +4,10 @@ myint: 3 5
|
||||||
myint size: 2
|
myint size: 2
|
||||||
longlong: -1099511627776
|
longlong: -1099511627776
|
||||||
global: 3
|
global: 3
|
||||||
|
defined ints: 5 5
|
||||||
|
defined floats: +5.800000e+000 +5.800000e+000
|
||||||
|
defined string: defined string
|
||||||
|
defined char: 99
|
||||||
15: 15
|
15: 15
|
||||||
25: 25
|
25: 25
|
||||||
callback 1: 50
|
callback 1: 50
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче