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
|
||||
*Package
|
||||
filename string
|
||||
constants map[string]*ast.BasicLit
|
||||
functions map[string]*functionInfo
|
||||
globals map[string]*globalInfo
|
||||
typedefs map[string]*typedefInfo
|
||||
|
@ -103,6 +104,7 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []er
|
|||
File: f,
|
||||
Package: p,
|
||||
filename: filename,
|
||||
constants: map[string]*ast.BasicLit{},
|
||||
functions: map[string]*functionInfo{},
|
||||
globals: map[string]*globalInfo{},
|
||||
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.
|
||||
info.addFuncPtrDecls()
|
||||
|
||||
// Declare globals found by libclang.
|
||||
info.addConstDecls()
|
||||
|
||||
// Declare globals found by libclang.
|
||||
info.addVarDecls()
|
||||
|
||||
|
@ -287,6 +292,49 @@ func (info *fileInfo) addFuncPtrDecls() {
|
|||
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.
|
||||
// It adds code like the following to the AST:
|
||||
//
|
||||
|
@ -313,7 +361,7 @@ func (info *fileInfo) addVarDecls() {
|
|||
for _, name := range names {
|
||||
global := info.globals[name]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Kind: ast.Var,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
|
|
|
@ -47,6 +47,7 @@ CXType tinygo_clang_getCursorResultType(GoCXCursor c);
|
|||
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
||||
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
||||
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
|
||||
CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
|
||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
||||
|
||||
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,
|
||||
(**C.char)(cmdargsC), C.int(len(cflags)), // command line args
|
||||
&unsavedFile, 1, // unsaved files
|
||||
C.CXTranslationUnit_None,
|
||||
C.CXTranslationUnit_DetailedPreprocessingRecord,
|
||||
&unit)
|
||||
if errCode != 0 {
|
||||
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{
|
||||
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
|
||||
}
|
||||
|
|
|
@ -49,6 +49,10 @@ CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) {
|
|||
return clang_getCursorLocation(c);
|
||||
}
|
||||
|
||||
CXSourceRange tinygo_clang_getCursorExtent(CXCursor c) {
|
||||
return clang_getCursorExtent(c);
|
||||
}
|
||||
|
||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(CXCursor 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)
|
||||
println("longlong:", y)
|
||||
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 n C.int = 15
|
||||
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;
|
||||
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
|
||||
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
|
||||
longlong: -1099511627776
|
||||
global: 3
|
||||
defined ints: 5 5
|
||||
defined floats: +5.800000e+000 +5.800000e+000
|
||||
defined string: defined string
|
||||
defined char: 99
|
||||
15: 15
|
||||
25: 25
|
||||
callback 1: 50
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче