cgo: implement rudimentary C array decaying

This is just a first step. It's not complete, but it gets some real
world C code to parse.

This signature, from the ESP-IDF:

    esp_err_t esp_wifi_get_mac(wifi_interface_t ifx, uint8_t mac[6]);

Was previously converted to something like this (pseudocode):

    C.esp_err_t esp_wifi_get_mac(ifx C.wifi_interface_t, mac [6]uint8)

But this is not correct. C array parameters will decay. The array is
passed by reference instead of by value. Instead, this would be the
correct signature:

    C.esp_err_t esp_wifi_get_mac(ifx C.wifi_interface_t, mac *uint8)

So that it can be called like this (using CGo):

    var mac [6]byte
    errCode := C.esp_wifi_get_mac(C.ESP_IF_WIFI_AP, &mac[0])

This stores the result in the 6-element array mac.
Этот коммит содержится в:
Ayke van Laethem 2021-09-24 03:05:10 +02:00 коммит произвёл Ron Evans
родитель 04e8f44370
коммит 5c0a337c4f
4 изменённых файлов: 50 добавлений и 1 удалений

Просмотреть файл

@ -196,7 +196,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
} }
fn.args = append(fn.args, paramInfo{ fn.args = append(fn.args, paramInfo{
name: argName, name: argName,
typeExpr: p.makeASTType(argType, pos), typeExpr: p.makeDecayingASTType(argType, pos),
}) })
} }
resultType := C.tinygo_clang_getCursorResultType(c) resultType := C.tinygo_clang_getCursorResultType(c)
@ -391,6 +391,41 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
}) })
} }
// makeDecayingASTType does the same as makeASTType but takes care of decaying
// types (arrays in function parameters, etc). It is otherwise identical to
// makeASTType.
func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
// Strip typedefs, if any.
underlyingType := typ
if underlyingType.kind == C.CXType_Typedef {
c := C.tinygo_clang_getTypeDeclaration(typ)
underlyingType = C.tinygo_clang_getTypedefDeclUnderlyingType(c)
// TODO: support a chain of typedefs. At the moment, it seems to get
// stuck in an endless loop when trying to get to the most underlying
// type.
}
// Check for decaying type. An example would be an array type in a
// parameter. This declaration:
// void foo(char buf[6]);
// is the same as this one:
// void foo(char *buf);
// But this one:
// void bar(char buf[6][4]);
// equals this:
// void bar(char *buf[4]);
// so not all array dimensions should be stripped, just the first one.
// TODO: there are more kinds of decaying types.
if underlyingType.kind == C.CXType_ConstantArray {
// Apply type decaying.
pointeeType := C.clang_getElementType(underlyingType)
return &ast.StarExpr{
Star: pos,
X: p.makeASTType(pointeeType, pos),
}
}
return p.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 (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {

4
testdata/cgo/main.c предоставленный
Просмотреть файл

@ -61,3 +61,7 @@ void unionSetData(short f0, short f1, short f2) {
globalUnion.data[1] = 8; globalUnion.data[1] = 8;
globalUnion.data[2] = 1; globalUnion.data[2] = 1;
} }
void arraydecay(int buf1[5], int buf2[3][8], int buf3[4][7][2]) {
// Do nothing.
}

6
testdata/cgo/main.go предоставленный
Просмотреть файл

@ -128,6 +128,12 @@ func main() {
// Check whether CFLAGS are correctly passed on to compiled C files. // Check whether CFLAGS are correctly passed on to compiled C files.
println("CFLAGS value:", C.cflagsConstant) println("CFLAGS value:", C.cflagsConstant)
// Check array-to-pointer decaying. This signature:
// void arraydecay(int buf1[5], int buf2[3][8], int buf3[4][7][2]);
// decays to:
// void arraydecay(int *buf1, int *buf2[8], int *buf3[7][2]);
C.arraydecay((*C.int)(nil), (*[8]C.int)(nil), (*[7][2]C.int)(nil))
// libc: test whether C functions work at all. // libc: test whether C functions work at all.
buf1 := []byte("foobar\x00") buf1 := []byte("foobar\x00")
buf2 := make([]byte, len(buf1)) buf2 := make([]byte, len(buf1))

4
testdata/cgo/main.h предоставленный
Просмотреть файл

@ -144,3 +144,7 @@ extern int cflagsConstant;
// test duplicate definitions // test duplicate definitions
int add(int a, int b); int add(int a, int b);
extern int global; extern int global;
// Test array decaying into a pointer.
typedef int arraydecay_buf3[4][7][2];
void arraydecay(int buf1[5], int buf2[3][8], arraydecay_buf3 buf3);