loader/cgo: add support for pointer types
Этот коммит содержится в:
родитель
01f6aff422
коммит
35fb594f8f
7 изменённых файлов: 165 добавлений и 120 удалений
|
@ -1325,7 +1325,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
store := c.builder.CreateStore(llvmVal, llvmAddr)
|
store := c.builder.CreateStore(llvmVal, llvmAddr)
|
||||||
valType := instr.Addr.Type().(*types.Pointer).Elem()
|
valType := instr.Addr.Type().Underlying().(*types.Pointer).Elem()
|
||||||
if c.ir.IsVolatile(valType) {
|
if c.ir.IsVolatile(valType) {
|
||||||
// Volatile store, for memory-mapped registers.
|
// Volatile store, for memory-mapped registers.
|
||||||
store.SetVolatile(true)
|
store.SetVolatile(true)
|
||||||
|
@ -1957,7 +1957,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
var bufptr, buflen llvm.Value
|
var bufptr, buflen llvm.Value
|
||||||
switch ptrTyp := expr.X.Type().Underlying().(type) {
|
switch ptrTyp := expr.X.Type().Underlying().(type) {
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
typ := expr.X.Type().(*types.Pointer).Elem().Underlying()
|
typ := expr.X.Type().Underlying().(*types.Pointer).Elem().Underlying()
|
||||||
switch typ := typ.(type) {
|
switch typ := typ.(type) {
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
bufptr = val
|
bufptr = val
|
||||||
|
@ -2986,7 +2986,7 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
|
||||||
return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String())
|
return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String())
|
||||||
}
|
}
|
||||||
case token.MUL: // *x, dereference pointer
|
case token.MUL: // *x, dereference pointer
|
||||||
valType := unop.X.Type().(*types.Pointer).Elem()
|
valType := unop.X.Type().Underlying().(*types.Pointer).Elem()
|
||||||
if c.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
|
if c.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
|
||||||
// zero-length data
|
// zero-length data
|
||||||
return c.getZeroValue(x.Type().ElementType())
|
return c.getZeroValue(x.Type().ElementType())
|
||||||
|
|
149
loader/cgo.go
149
loader/cgo.go
|
@ -17,37 +17,33 @@ import (
|
||||||
type fileInfo struct {
|
type fileInfo struct {
|
||||||
*ast.File
|
*ast.File
|
||||||
filename string
|
filename string
|
||||||
functions []*functionInfo
|
functions map[string]*functionInfo
|
||||||
typedefs []*typedefInfo
|
globals map[string]*globalInfo
|
||||||
globals []*globalInfo
|
typedefs map[string]*typedefInfo
|
||||||
importCPos token.Pos
|
importCPos token.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
// functionInfo stores some information about a Cgo function found by libclang
|
// functionInfo stores some information about a Cgo function found by libclang
|
||||||
// and declared in the AST.
|
// and declared in the AST.
|
||||||
type functionInfo struct {
|
type functionInfo struct {
|
||||||
name string
|
args []paramInfo
|
||||||
args []paramInfo
|
results *ast.FieldList
|
||||||
result string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// paramInfo is a parameter of a Cgo function (see functionInfo).
|
// paramInfo is a parameter of a Cgo function (see functionInfo).
|
||||||
type paramInfo struct {
|
type paramInfo struct {
|
||||||
name string
|
name string
|
||||||
typeName string
|
typeExpr ast.Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
// typedefInfo contains information about a single typedef in C.
|
// typedefInfo contains information about a single typedef in C.
|
||||||
type typedefInfo struct {
|
type typedefInfo struct {
|
||||||
newName string // newly defined type name
|
typeExpr ast.Expr
|
||||||
oldName string // old type name, may be something like "unsigned long long"
|
|
||||||
size int // size in bytes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// globalInfo contains information about a declared global variable in C.
|
// globalInfo contains information about a declared global variable in C.
|
||||||
type globalInfo struct {
|
type globalInfo struct {
|
||||||
name string
|
typeExpr ast.Expr
|
||||||
typeName string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -84,8 +80,11 @@ typedef unsigned long long _Cgo_ulonglong;
|
||||||
// comment with libclang, and modifies the AST to use this information.
|
// comment with libclang, and modifies the AST to use this information.
|
||||||
func (p *Package) processCgo(filename string, f *ast.File, cflags []string) error {
|
func (p *Package) processCgo(filename string, f *ast.File, cflags []string) error {
|
||||||
info := &fileInfo{
|
info := &fileInfo{
|
||||||
File: f,
|
File: f,
|
||||||
filename: filename,
|
filename: filename,
|
||||||
|
functions: map[string]*functionInfo{},
|
||||||
|
globals: map[string]*globalInfo{},
|
||||||
|
typedefs: map[string]*typedefInfo{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find `import "C"` statements in the file.
|
// Find `import "C"` statements in the file.
|
||||||
|
@ -151,16 +150,22 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) erro
|
||||||
func (info *fileInfo) addFuncDecls() {
|
func (info *fileInfo) addFuncDecls() {
|
||||||
// TODO: replace all uses of importCPos with the real locations from
|
// TODO: replace all uses of importCPos with the real locations from
|
||||||
// libclang.
|
// libclang.
|
||||||
for _, fn := range info.functions {
|
names := make([]string, 0, len(info.functions))
|
||||||
|
for name := range info.functions {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
fn := info.functions[name]
|
||||||
obj := &ast.Object{
|
obj := &ast.Object{
|
||||||
Kind: ast.Fun,
|
Kind: ast.Fun,
|
||||||
Name: mapCgoType(fn.name),
|
Name: "C." + name,
|
||||||
}
|
}
|
||||||
args := make([]*ast.Field, len(fn.args))
|
args := make([]*ast.Field, len(fn.args))
|
||||||
decl := &ast.FuncDecl{
|
decl := &ast.FuncDecl{
|
||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: info.importCPos,
|
NamePos: info.importCPos,
|
||||||
Name: mapCgoType(fn.name),
|
Name: "C." + name,
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
},
|
},
|
||||||
Type: &ast.FuncType{
|
Type: &ast.FuncType{
|
||||||
|
@ -170,16 +175,7 @@ func (info *fileInfo) addFuncDecls() {
|
||||||
List: args,
|
List: args,
|
||||||
Closing: info.importCPos,
|
Closing: info.importCPos,
|
||||||
},
|
},
|
||||||
Results: &ast.FieldList{
|
Results: fn.results,
|
||||||
List: []*ast.Field{
|
|
||||||
&ast.Field{
|
|
||||||
Type: &ast.Ident{
|
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: mapCgoType(fn.result),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
obj.Decl = decl
|
obj.Decl = decl
|
||||||
|
@ -191,15 +187,12 @@ func (info *fileInfo) addFuncDecls() {
|
||||||
Name: arg.name,
|
Name: arg.name,
|
||||||
Obj: &ast.Object{
|
Obj: &ast.Object{
|
||||||
Kind: ast.Var,
|
Kind: ast.Var,
|
||||||
Name: mapCgoType(arg.name),
|
Name: arg.name,
|
||||||
Decl: decl,
|
Decl: decl,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Type: &ast.Ident{
|
Type: arg.typeExpr,
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: mapCgoType(arg.typeName),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info.Decls = append(info.Decls, decl)
|
info.Decls = append(info.Decls, decl)
|
||||||
|
@ -221,21 +214,24 @@ func (info *fileInfo) addVarDecls() {
|
||||||
Lparen: info.importCPos,
|
Lparen: info.importCPos,
|
||||||
Rparen: info.importCPos,
|
Rparen: info.importCPos,
|
||||||
}
|
}
|
||||||
for _, global := range info.globals {
|
names := make([]string, 0, len(info.globals))
|
||||||
|
for name := range info.globals {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
global := info.globals[name]
|
||||||
obj := &ast.Object{
|
obj := &ast.Object{
|
||||||
Kind: ast.Typ,
|
Kind: ast.Typ,
|
||||||
Name: mapCgoType(global.name),
|
Name: "C." + name,
|
||||||
}
|
}
|
||||||
valueSpec := &ast.ValueSpec{
|
valueSpec := &ast.ValueSpec{
|
||||||
Names: []*ast.Ident{&ast.Ident{
|
Names: []*ast.Ident{&ast.Ident{
|
||||||
NamePos: info.importCPos,
|
NamePos: info.importCPos,
|
||||||
Name: mapCgoType(global.name),
|
Name: "C." + name,
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
}},
|
}},
|
||||||
Type: &ast.Ident{
|
Type: global.typeExpr,
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: mapCgoType(global.typeName),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
obj.Decl = valueSpec
|
obj.Decl = valueSpec
|
||||||
gen.Specs = append(gen.Specs, valueSpec)
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
@ -292,55 +288,32 @@ func (info *fileInfo) addTypedefs() {
|
||||||
TokPos: info.importCPos,
|
TokPos: info.importCPos,
|
||||||
Tok: token.TYPE,
|
Tok: token.TYPE,
|
||||||
}
|
}
|
||||||
for _, typedef := range info.typedefs {
|
names := make([]string, 0, len(info.typedefs))
|
||||||
newType := mapCgoType(typedef.newName)
|
for name := range info.typedefs {
|
||||||
oldType := mapCgoType(typedef.oldName)
|
names = append(names, name)
|
||||||
switch oldType {
|
}
|
||||||
// TODO: plain char (may be signed or unsigned)
|
sort.Strings(names)
|
||||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
for _, name := range names {
|
||||||
switch typedef.size {
|
typedef := info.typedefs[name]
|
||||||
case 1:
|
typeName := "C." + name
|
||||||
oldType = "int8"
|
if strings.HasPrefix(name, "_Cgo_") {
|
||||||
case 2:
|
typeName = "C." + name[len("_Cgo_"):]
|
||||||
oldType = "int16"
|
|
||||||
case 4:
|
|
||||||
oldType = "int32"
|
|
||||||
case 8:
|
|
||||||
oldType = "int64"
|
|
||||||
}
|
|
||||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
|
||||||
switch typedef.size {
|
|
||||||
case 1:
|
|
||||||
oldType = "uint8"
|
|
||||||
case 2:
|
|
||||||
oldType = "uint16"
|
|
||||||
case 4:
|
|
||||||
oldType = "uint32"
|
|
||||||
case 8:
|
|
||||||
oldType = "uint64"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(newType, "C._Cgo_") {
|
if _, ok := cgoAliases[typeName]; ok {
|
||||||
newType = "C." + newType[len("C._Cgo_"):]
|
|
||||||
}
|
|
||||||
if _, ok := cgoAliases[newType]; ok {
|
|
||||||
// This is a type that also exists in Go (defined in stdint.h).
|
// This is a type that also exists in Go (defined in stdint.h).
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
obj := &ast.Object{
|
obj := &ast.Object{
|
||||||
Kind: ast.Typ,
|
Kind: ast.Typ,
|
||||||
Name: newType,
|
Name: typeName,
|
||||||
}
|
}
|
||||||
typeSpec := &ast.TypeSpec{
|
typeSpec := &ast.TypeSpec{
|
||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: info.importCPos,
|
NamePos: info.importCPos,
|
||||||
Name: newType,
|
Name: typeName,
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
},
|
},
|
||||||
Type: &ast.Ident{
|
Type: typedef.typeExpr,
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: oldType,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
obj.Decl = typeSpec
|
obj.Decl = typeSpec
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
|
@ -362,31 +335,9 @@ func (info *fileInfo) walker(cursor *astutil.Cursor) bool {
|
||||||
if x.Name == "C" {
|
if x.Name == "C" {
|
||||||
cursor.Replace(&ast.Ident{
|
cursor.Replace(&ast.Ident{
|
||||||
NamePos: x.NamePos,
|
NamePos: x.NamePos,
|
||||||
Name: mapCgoType(node.Sel.Name),
|
Name: "C." + node.Sel.Name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// mapCgoType converts a C type name into a Go type name with a "C." prefix.
|
|
||||||
func mapCgoType(t string) string {
|
|
||||||
switch t {
|
|
||||||
case "signed char":
|
|
||||||
return "C.schar"
|
|
||||||
case "long long":
|
|
||||||
return "C.longlong"
|
|
||||||
case "unsigned char":
|
|
||||||
return "C.schar"
|
|
||||||
case "unsigned short":
|
|
||||||
return "C.ushort"
|
|
||||||
case "unsigned int":
|
|
||||||
return "C.uint"
|
|
||||||
case "unsigned long":
|
|
||||||
return "C.ulong"
|
|
||||||
case "unsigned long long":
|
|
||||||
return "C.ulonglong"
|
|
||||||
default:
|
|
||||||
return "C." + t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ package loader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"go/ast"
|
||||||
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -89,37 +91,70 @@ func tinygo_clang_visitor(c, parent C.CXCursor, client_data C.CXClientData) C.in
|
||||||
return C.CXChildVisit_Continue // not supported
|
return C.CXChildVisit_Continue // not supported
|
||||||
}
|
}
|
||||||
numArgs := C.clang_Cursor_getNumArguments(c)
|
numArgs := C.clang_Cursor_getNumArguments(c)
|
||||||
fn := &functionInfo{name: name}
|
fn := &functionInfo{}
|
||||||
info.functions = append(info.functions, fn)
|
info.functions[name] = fn
|
||||||
for i := C.int(0); i < numArgs; i++ {
|
for i := C.int(0); i < numArgs; i++ {
|
||||||
arg := C.clang_Cursor_getArgument(c, C.uint(i))
|
arg := C.clang_Cursor_getArgument(c, C.uint(i))
|
||||||
argName := getString(C.clang_getCursorSpelling(arg))
|
argName := getString(C.clang_getCursorSpelling(arg))
|
||||||
argType := C.clang_getArgType(cursorType, C.uint(i))
|
argType := C.clang_getArgType(cursorType, C.uint(i))
|
||||||
argTypeName := getString(C.clang_getTypeSpelling(argType))
|
fn.args = append(fn.args, paramInfo{
|
||||||
fn.args = append(fn.args, paramInfo{argName, argTypeName})
|
name: argName,
|
||||||
|
typeExpr: info.makeASTType(argType),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
resultType := C.clang_getCursorResultType(c)
|
resultType := C.clang_getCursorResultType(c)
|
||||||
resultTypeName := getString(C.clang_getTypeSpelling(resultType))
|
if resultType.kind != C.CXType_Void {
|
||||||
fn.result = resultTypeName
|
fn.results = &ast.FieldList{
|
||||||
|
List: []*ast.Field{
|
||||||
|
&ast.Field{
|
||||||
|
Type: info.makeASTType(resultType),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
case C.CXCursor_TypedefDecl:
|
case C.CXCursor_TypedefDecl:
|
||||||
typedefType := C.clang_getCursorType(c)
|
typedefType := C.clang_getCursorType(c)
|
||||||
name := getString(C.clang_getTypedefName(typedefType))
|
name := getString(C.clang_getTypedefName(typedefType))
|
||||||
underlyingType := C.clang_getTypedefDeclUnderlyingType(c)
|
underlyingType := C.clang_getTypedefDeclUnderlyingType(c)
|
||||||
underlyingTypeName := getString(C.clang_getTypeSpelling(underlyingType))
|
expr := info.makeASTType(underlyingType)
|
||||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
if strings.HasPrefix(name, "_Cgo_") {
|
||||||
info.typedefs = append(info.typedefs, &typedefInfo{
|
expr := expr.(*ast.Ident)
|
||||||
newName: name,
|
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||||
oldName: underlyingTypeName,
|
switch expr.Name {
|
||||||
size: int(typeSize),
|
// TODO: plain char (may be signed or unsigned)
|
||||||
})
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info.typedefs[name] = &typedefInfo{
|
||||||
|
typeExpr: expr,
|
||||||
|
}
|
||||||
case C.CXCursor_VarDecl:
|
case C.CXCursor_VarDecl:
|
||||||
name := getString(C.clang_getCursorSpelling(c))
|
name := getString(C.clang_getCursorSpelling(c))
|
||||||
cursorType := C.clang_getCursorType(c)
|
cursorType := C.clang_getCursorType(c)
|
||||||
cursorTypeName := getString(C.clang_getTypeSpelling(cursorType))
|
info.globals[name] = &globalInfo{
|
||||||
info.globals = append(info.globals, &globalInfo{
|
typeExpr: info.makeASTType(cursorType),
|
||||||
name: name,
|
}
|
||||||
typeName: cursorTypeName,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
|
@ -130,3 +165,44 @@ func getString(clangString C.CXString) (s string) {
|
||||||
C.clang_disposeString(clangString)
|
C.clang_disposeString(clangString)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
|
||||||
|
var typeName string
|
||||||
|
switch typ.kind {
|
||||||
|
case C.CXType_SChar:
|
||||||
|
typeName = "schar"
|
||||||
|
case C.CXType_UChar:
|
||||||
|
typeName = "uchar"
|
||||||
|
case C.CXType_Short:
|
||||||
|
typeName = "short"
|
||||||
|
case C.CXType_UShort:
|
||||||
|
typeName = "ushort"
|
||||||
|
case C.CXType_Int:
|
||||||
|
typeName = "int"
|
||||||
|
case C.CXType_UInt:
|
||||||
|
typeName = "uint"
|
||||||
|
case C.CXType_Long:
|
||||||
|
typeName = "long"
|
||||||
|
case C.CXType_ULong:
|
||||||
|
typeName = "ulong"
|
||||||
|
case C.CXType_LongLong:
|
||||||
|
typeName = "longlong"
|
||||||
|
case C.CXType_ULongLong:
|
||||||
|
typeName = "ulonglong"
|
||||||
|
case C.CXType_Pointer:
|
||||||
|
return &ast.StarExpr{
|
||||||
|
Star: info.importCPos,
|
||||||
|
X: info.makeASTType(C.clang_getPointeeType(typ)),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Fallback, probably incorrect but at least the error points to an odd
|
||||||
|
// type name.
|
||||||
|
typeName = getString(C.clang_getTypeSpelling(typ))
|
||||||
|
}
|
||||||
|
return &ast.Ident{
|
||||||
|
NamePos: info.importCPos,
|
||||||
|
Name: "C." + typeName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
4
testdata/cgo/main.c
предоставленный
4
testdata/cgo/main.c
предоставленный
|
@ -9,3 +9,7 @@ int fortytwo() {
|
||||||
int add(int a, int b) {
|
int add(int a, int b) {
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void store(int value, int *ptr) {
|
||||||
|
*ptr = value;
|
||||||
|
}
|
||||||
|
|
6
testdata/cgo/main.go
предоставленный
6
testdata/cgo/main.go
предоставленный
|
@ -17,4 +17,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)
|
||||||
|
var ptr C.intPointer
|
||||||
|
var n C.int = 15
|
||||||
|
ptr = C.intPointer(&n)
|
||||||
|
println("15:", *ptr)
|
||||||
|
C.store(25, &n)
|
||||||
|
println("25:", *ptr)
|
||||||
}
|
}
|
||||||
|
|
6
testdata/cgo/main.h
предоставленный
6
testdata/cgo/main.h
предоставленный
|
@ -1,3 +1,9 @@
|
||||||
typedef short myint;
|
typedef short myint;
|
||||||
int add(int a, int b);
|
int add(int a, int b);
|
||||||
|
typedef int * intPointer;
|
||||||
|
extern int global;
|
||||||
|
void store(int value, int *ptr);
|
||||||
|
|
||||||
|
// test duplicate definitions
|
||||||
|
int add(int a, int b);
|
||||||
extern int global;
|
extern int global;
|
||||||
|
|
2
testdata/cgo/out.txt
предоставленный
2
testdata/cgo/out.txt
предоставленный
|
@ -4,3 +4,5 @@ myint: 3 5
|
||||||
myint size: 2
|
myint size: 2
|
||||||
longlong: -1099511627776
|
longlong: -1099511627776
|
||||||
global: 3
|
global: 3
|
||||||
|
15: 15
|
||||||
|
25: 25
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче