github.com/gotranspile/cxgo@v0.3.8-0.20240118201721-29871598a6a2/c_type.go (about) 1 package cxgo 2 3 import ( 4 "fmt" 5 "unicode" 6 "unicode/utf8" 7 8 "modernc.org/cc/v3" 9 "modernc.org/token" 10 11 "github.com/gotranspile/cxgo/types" 12 ) 13 14 func (g *translator) convertTypeOper(p cc.Operand, where token.Position) types.Type { 15 defer func() { 16 switch r := recover().(type) { 17 case nil: 18 case error: 19 panic(ErrorWithPos(r, where)) 20 default: 21 panic(ErrorWithPos(fmt.Errorf("%v", r), where)) 22 } 23 }() 24 if d := p.Declarator(); d != nil { 25 where = d.Position() 26 } 27 var conf IdentConfig 28 if d := p.Declarator(); d != nil { 29 conf = g.idents[d.Name().String()] 30 } 31 return g.convertTypeRoot(conf, p.Type(), where) 32 } 33 34 // convertType is similar to newTypeCC but it will first consult the type cache. 35 func (g *translator) convertType(conf IdentConfig, t cc.Type, where token.Position) types.Type { 36 defer func() { 37 if r := recover(); r != nil { 38 panic(fmt.Errorf("type conversion failed at %v: %v", where, r)) 39 } 40 }() 41 // custom type overrides coming from the config 42 // note that we don't save them since they might depend 43 // not only on the input type, but also on a field name 44 switch conf.Type { 45 case HintBool: 46 return g.env.Go().Bool() 47 case HintIface: 48 return g.env.Go().Any() 49 case HintString: 50 return g.env.Go().String() 51 case HintSlice: 52 ct := g.newTypeCC(IdentConfig{}, t, where) 53 var elem types.Type 54 switch ct := ct.(type) { 55 case types.PtrType: 56 elem = ct.Elem() 57 case types.ArrayType: 58 elem = ct.Elem() 59 default: 60 panic(fmt.Errorf("expected an array or a pointer, got: %v, %#v; defined at: %v", ct, ct, where)) 61 } 62 if elem == types.UintT(1) { 63 elem = g.env.Go().Byte() 64 } 65 return types.SliceT(elem) 66 } 67 // allow invalid types, they might still be useful 68 // since one may define them in a separate Go file 69 // and make the code valid 70 if t.Kind() == cc.Invalid { 71 return types.UnkT(g.env.PtrSize()) 72 } 73 if ct, ok := g.ctypes[t]; ok { 74 return ct 75 } 76 ct := g.newTypeCC(conf, t, where) 77 g.ctypes[t] = ct 78 return ct 79 } 80 81 // convertTypeRoot is the same as convertType, but it applies a workaround for 82 // C function pointers. 83 func (g *translator) convertTypeRoot(conf IdentConfig, t cc.Type, where token.Position) types.Type { 84 ft := g.convertType(conf, t, where) 85 if p, ok := ft.(types.PtrType); ok && p.ElemKind().IsFunc() { 86 ft = p.Elem() 87 } 88 if p, ok := ft.(types.ArrayType); ok && p.Len() == 0 { 89 ft = types.SliceT(p.Elem()) 90 } 91 return ft 92 } 93 94 // convertTypeOpt is similar to convertType, but it also allows void type by returning nil. 95 func (g *translator) convertTypeOpt(conf IdentConfig, t cc.Type, where token.Position) types.Type { 96 if t == nil || t.Kind() == cc.Void || t.Kind() == cc.Invalid { 97 return nil 98 } 99 return g.convertType(conf, t, where) 100 } 101 102 // convertTypeRootOpt is similar to convertTypeRoot, but it also allows void type by returning nil. 103 func (g *translator) convertTypeRootOpt(conf IdentConfig, t cc.Type, where token.Position) types.Type { 104 if t == nil || t.Kind() == cc.Void || t.Kind() == cc.Invalid { 105 return nil 106 } 107 return g.convertTypeRoot(conf, t, where) 108 } 109 110 // replaceType checks if the type needs to be replaced. It usually happens for builtin types. 111 func (g *translator) replaceType(name string) (types.Type, bool) { 112 if t, ok := g.env.TypeByName(name); ok { 113 return t, true 114 } 115 if t := g.env.C().Type(name); t != nil { 116 return t, true 117 } 118 return nil, false 119 } 120 121 // newNamedTypeAt finds or creates a named type defined by specified CC types and tokens. 122 func (g *translator) newNamedTypeAt(name string, typ, elem cc.Type, where token.Position) types.Type { 123 if typ == elem { 124 switch typ.Kind() { 125 case cc.Struct, cc.Union: 126 default: 127 panic(fmt.Errorf("name: %s, elem: (%T) %v", name, elem, elem)) 128 } 129 } 130 conf := g.idents[name] 131 if typ, ok := g.replaceType(name); ok { 132 return typ 133 } 134 if c, ok := g.idents[name]; ok && c.Alias { 135 sub := g.convertTypeRoot(conf, elem, where) 136 g.ctypes[typ] = sub 137 g.aliases[name] = sub 138 return sub 139 } 140 return g.newOrFindNamedType(name, func() types.Type { 141 return g.convertTypeRoot(conf, elem, where) 142 }) 143 } 144 145 func (g *translator) newOrFindNamedTypedef(name string, underlying func() types.Type) types.Named { 146 if c, ok := g.idents[name]; ok && c.Alias { 147 if _, ok := g.aliases[name]; ok { 148 return nil 149 } 150 // we should register the underlying type with the current name, 151 // so all the accesses will use underlying type 152 t := underlying() 153 g.aliases[name] = t 154 // and we suppress the definition of this type 155 return nil 156 } 157 return g.newOrFindNamedType(name, underlying) 158 } 159 160 // newOrFindNamedType finds or creates a new named type with a given underlying type. 161 // The function is given because types may be recursive. 162 func (g *translator) newOrFindNamedType(name string, underlying func() types.Type) types.Named { 163 if _, ok := g.aliases[name]; ok { 164 panic("alias") 165 } 166 if typ, ok := g.named[name]; ok { 167 return typ 168 } 169 und := underlying() 170 if typ, ok := g.named[name]; ok { 171 return typ 172 } 173 return g.newNamedType(name, und) 174 } 175 176 // newNamedType creates a new named type based on the underlying type. 177 func (g *translator) newNamedType(name string, underlying types.Type) types.Named { 178 if _, ok := g.named[name]; ok { 179 panic("type with a same name already exists: " + name) 180 } 181 goname := "" 182 if c, ok := g.idents[name]; ok && c.Rename != "" { 183 goname = c.Rename 184 } 185 nt := types.NamedTGo(name, goname, underlying) 186 g.named[name] = nt 187 return nt 188 } 189 190 // newNamedTypeFrom creates a new named type based on a given CC type. 191 // It is similar to newNamedType, but accepts a CC type that should be bound to a new type. 192 func (g *translator) newNamedTypeFrom(name string, underlying types.Type, from cc.Type) types.Named { 193 if _, ok := g.ctypes[from]; ok { 194 panic("same C type already exists") 195 } 196 nt := g.newNamedType(name, underlying) 197 g.ctypes[from] = nt 198 return nt 199 } 200 201 // newOrFindNamedTypeFrom finds or creates a type with a given name, underlying type and source C type. 202 // It cannot return the NamedType because the type may have an override. 203 func (g *translator) newOrFindNamedTypeFrom(name string, elem func() types.Type, from cc.Type) types.Type { 204 if t, ok := g.ctypes[from]; ok { 205 return t 206 } 207 nt := g.newOrFindNamedType(name, elem) 208 g.ctypes[from] = nt 209 return nt 210 } 211 212 // newOrFindIncompleteNamedTypeFrom finds or creates a incomplete type with a given name and source C type. 213 // It cannot return the NamedType because the type may have an override, or the type may have been resolved to something else. 214 func (g *translator) newOrFindIncompleteNamedTypeFrom(name string, from cc.Type) types.Type { 215 return g.newOrFindNamedTypeFrom(name, nil, from) 216 } 217 218 func asExportedName(s string) string { 219 if len(s) == 0 { 220 return "" 221 } 222 r, n := utf8.DecodeRuneInString(s) 223 return string(unicode.ToUpper(r)) + s[n:] 224 } 225 226 // newTypeCC creates a new type based on a specified C type. This function will not consult the cache for a given type. 227 // It will recursively convert all the underlying and sub-types using convertType. 228 func (g *translator) newTypeCC(conf IdentConfig, t cc.Type, where token.Position) types.Type { 229 sname := t.Name().String() 230 if nt, ok := g.replaceType(sname); ok { 231 g.ctypes[t] = nt 232 return nt 233 } 234 // it's handled separately because it's the only type that is allowed to be incomplete 235 if t != t.Alias() { 236 if t.IsIncomplete() { 237 if nt, ok := g.named[sname]; ok { 238 return nt 239 } 240 return g.newOrFindNamedType(sname, func() types.Type { 241 return types.StructT(nil) 242 }) 243 } 244 if t, ok := g.aliases[sname]; ok { 245 return t 246 } 247 conf := g.idents[sname] 248 u := t.Alias() 249 sub := g.convertType(conf, u, where) 250 if sub, ok := sub.(types.Named); ok { 251 if name := t.Name(); name == u.Name() { 252 g.named[name.String()] = sub 253 g.ctypes[t] = sub 254 return sub 255 } 256 } 257 return g.newNamedTypeAt(sname, t, u, where) 258 } 259 switch t.Kind() { 260 case cc.Struct, cc.Union: 261 return g.convertStructType(conf, t, where) 262 } 263 if u := t.Alias(); t != u { 264 panic(fmt.Errorf("unhandled alias type: %T", t)) 265 } 266 switch kind := t.Kind(); kind { 267 case cc.UInt64, cc.UInt32, cc.UInt16, cc.UInt8: 268 return types.UintT(int(t.Size())) 269 case cc.Int64, cc.Int32, cc.Int16, cc.Int8: 270 return types.IntT(int(t.Size())) 271 case cc.SChar: 272 return g.env.C().SignedChar() 273 case cc.UChar: 274 return g.env.C().UnsignedChar() 275 case cc.Short: 276 return g.env.C().Short() 277 case cc.UShort: 278 return g.env.C().UnsignedShort() 279 case cc.Int: 280 return g.env.C().Int() 281 case cc.UInt: 282 return g.env.C().UnsignedInt() 283 case cc.Long: 284 return g.env.C().Long() 285 case cc.ULong: 286 return g.env.C().UnsignedLong() 287 case cc.LongLong: 288 return g.env.C().LongLong() 289 case cc.ULongLong: 290 return g.env.C().UnsignedLongLong() 291 case cc.Float: 292 return g.env.C().Float() 293 case cc.Double: 294 return g.env.C().Double() 295 case cc.LongDouble: 296 return types.FloatT(int(t.Size())) 297 case cc.Char: 298 return g.env.C().Char() 299 case cc.Bool: 300 return g.env.C().Bool() 301 case cc.Function: 302 return g.convertFuncType(conf, nil, t, where) 303 case cc.Ptr: 304 if t.Elem().Kind() == cc.Char { 305 return g.env.C().String() 306 } 307 if e := t.Elem(); e.Kind() == cc.Struct && e.NumField() == 1 { 308 // Go slices defined via cxgo builtins 309 f := e.FieldByIndex([]int{0}) 310 if f.Name().String() == types.GoPrefix+"slice_data" { 311 elem := g.convertType(IdentConfig{}, f.Type(), where) 312 return types.SliceT(elem) 313 } 314 } 315 var ptr types.PtrType 316 if name := t.Elem().Name(); name != 0 { 317 if pt, ok := g.namedPtrs[name.String()]; ok { 318 return pt 319 } 320 ptr = g.env.PtrT(nil) // incomplete 321 g.namedPtrs[name.String()] = ptr 322 } 323 // use Opt because of the void* 324 elem := g.convertTypeOpt(IdentConfig{}, t.Elem(), where) 325 if ptr != nil { 326 ptr.SetElem(elem) 327 return ptr 328 } 329 return g.env.PtrT(elem) 330 case cc.Array: 331 if t.Elem().Kind() == cc.Char { 332 return types.ArrayT(g.env.Go().Byte(), int(t.Len())) 333 } 334 elem := g.convertType(IdentConfig{}, t.Elem(), where) 335 return types.ArrayT( 336 elem, 337 int(t.Len()), 338 ) 339 case cc.Union: 340 if name := t.Name(); name != 0 { 341 u := t.Alias() 342 if name == u.Name() { 343 u = u.Alias() 344 } 345 return g.newNamedTypeAt(name.String(), t, u, where) 346 } 347 fconf := make(map[string]IdentConfig) 348 for _, f := range conf.Fields { 349 fconf[f.Name] = f 350 } 351 var fields []*types.Field 352 for i := 0; i < t.NumField(); i++ { 353 f := t.FieldByIndex([]int{i}) 354 name := f.Name().String() 355 fc := fconf[name] 356 ft := g.convertTypeRoot(fc, f.Type(), where) 357 fields = append(fields, &types.Field{ 358 Name: g.newIdent(name, ft), 359 }) 360 } 361 return types.UnionT(fields) 362 case cc.Enum: 363 return g.newTypeCC(IdentConfig{}, t.EnumType(), where) 364 default: 365 panic(fmt.Errorf("%T, %s (%s)", t, kind, t.String())) 366 } 367 } 368 369 func (g *translator) convertStructType(conf IdentConfig, t cc.Type, where token.Position) types.Type { 370 sname := t.Name().String() 371 if c, ok := g.idents[sname]; ok { 372 conf = c 373 } 374 fconf := make(map[string]IdentConfig) 375 for _, f := range conf.Fields { 376 fconf[f.Name] = f 377 } 378 buildType := func() types.Type { 379 var fields []*types.Field 380 for i := 0; i < t.NumField(); i++ { 381 f := t.FieldByIndex([]int{i}) 382 fc := fconf[f.Name().String()] 383 ft := g.convertTypeRoot(fc, f.Type(), where) 384 if f.Name() == 0 { 385 st := types.Unwrap(ft).(*types.StructType) 386 fields = append(fields, st.Fields()...) 387 continue 388 } 389 fname := g.newIdent(f.Name().String(), ft) 390 if fc.Rename != "" { 391 fname.GoName = fc.Rename 392 } else if !g.conf.UnexportedFields { 393 fname.GoName = asExportedName(fname.Name) 394 } 395 fields = append(fields, &types.Field{ 396 Name: fname, 397 }) 398 } 399 if !where.IsValid() { 400 panic(where) 401 } 402 var s *types.StructType 403 if t.Kind() == cc.Union { 404 s = types.UnionT(fields) 405 } else { 406 s = types.StructT(fields) 407 } 408 s.Where = where.String() 409 if t.Name() == 0 { 410 return s 411 } 412 return s 413 } 414 if t.Name() == 0 { 415 return buildType() 416 } 417 return g.newOrFindNamedType(sname, buildType) 418 } 419 420 func (g *translator) convertFuncType(conf IdentConfig, d *cc.Declarator, t cc.Type, where token.Position) *types.FuncType { 421 if kind := t.Kind(); kind != cc.Function { 422 panic(kind) 423 } 424 if d != nil { 425 where = d.Position() 426 } 427 var rconf IdentConfig 428 aconf := make(map[string]IdentConfig) 429 iconf := make(map[int]IdentConfig) 430 for _, f := range conf.Fields { 431 if f.Name != "" { 432 if f.Name == "return" { 433 rconf = f 434 } else { 435 aconf[f.Name] = f 436 } 437 } else { 438 iconf[f.Index] = f 439 } 440 } 441 var ( 442 args []*types.Field 443 named int 444 ) 445 for i, p := range t.Parameters() { 446 pt := p.Type() 447 if pt.Kind() == cc.Void { 448 continue 449 } 450 var fc IdentConfig 451 if ac, ok := aconf[p.Name().String()]; ok { 452 fc = ac 453 } else if ac, ok = iconf[i]; ok { 454 fc = ac 455 } 456 at := g.convertTypeRoot(fc, pt, where) 457 var name *types.Ident 458 if d != nil && p.Name() != 0 { 459 name = g.convertIdent(d.ParamScope(), p.Declarator().NameTok(), at).Ident 460 named++ 461 } else if p.Name() != 0 { 462 name = g.convertIdentWith(p.Declarator().NameTok().String(), at, p.Declarator()).Ident 463 named++ 464 } else { 465 name = types.NewUnnamed(at) 466 } 467 args = append(args, &types.Field{ 468 Name: name, 469 }) 470 } 471 if named != 0 && len(args) != named { 472 for i, a := range args { 473 if a.Name.Name == "" && a.Name.GoName == "" { 474 a.Name.GoName = fmt.Sprintf("a%d", i+1) 475 } 476 } 477 } 478 ret := g.convertTypeRootOpt(rconf, t.Result(), where) 479 if t.IsVariadic() { 480 return g.env.VarFuncT(ret, args...) 481 } 482 return g.env.FuncT(ret, args...) 483 } 484 485 func propagateConst(t types.Type) bool { 486 switch t := t.(type) { 487 case types.PtrType: 488 if !propagateConst(t.Elem()) { 489 //t.Const = true // TODO 490 } 491 return true 492 case types.ArrayType: 493 return propagateConst(t.Elem()) 494 case types.IntType: 495 //t.Const = true 496 return true 497 } 498 return false 499 } 500 501 func (g *translator) ZeroValue(t types.Type) Expr { 502 if t == nil { 503 panic("nil type") 504 } 505 switch t.Kind().Major() { 506 case types.Ptr, types.Func: 507 return g.Nil() 508 case types.Int, types.Float: 509 return cUintLit(0, 10) 510 case types.Struct, types.Array: 511 return &CCompLitExpr{Type: t} 512 default: 513 panic(t) 514 } 515 }