init.go (15599B)
1 // Copyright 2020 The CCGO Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ccgo // import "modernc.org/ccgo/v3/lib" 6 7 import ( 8 "fmt" 9 "sort" 10 "strings" 11 12 "modernc.org/cc/v3" 13 ) 14 15 func isAggregateTypeOrUnion(t cc.Type) bool { 16 switch t.Kind() { 17 case cc.Struct, cc.Union, cc.Array: 18 return true 19 } 20 21 return false 22 } 23 24 // 6.7.8 Initialization 25 func (p *project) initializer(f *function, n *cc.Initializer, t cc.Type, sc cc.StorageClass, tld *tld) { 26 lm := map[*cc.Initializer][]cc.StringID{} 27 tm := map[*cc.Initializer][]cc.StringID{} 28 s := p.initializerFlatten(n, lm, tm) 29 sort.Slice(s, func(i, j int) bool { 30 a := s[i] 31 b := s[j] 32 if a.Offset < b.Offset { 33 return true 34 } 35 36 if a.Offset > b.Offset { 37 return false 38 } 39 40 if a.Field == nil || b.Field == nil || !a.Field.IsBitField() || !b.Field.IsBitField() { 41 panic(todo("%v: internal error: off %#x, %v: off %#x, t %v", a.Position(), a.Offset, b.Position(), b.Offset, t)) 42 } 43 44 return a.Field.BitFieldOffset() < b.Field.BitFieldOffset() 45 }) 46 p.initializerInner("", 0, f, s, t, sc, tld, nil, lm, tm) 47 } 48 49 func (p *project) initializerInner(tag string, off uintptr, f *function, s []*cc.Initializer, t cc.Type, sc cc.StorageClass, tld *tld, patchField cc.Field, lm, tm map[*cc.Initializer][]cc.StringID) { 50 // 11: The initializer for a scalar shall be a single expression, optionally 51 // enclosed in braces. The initial value of the object is that of the 52 // expression (after conversion); the same type constraints and conversions as 53 // for simple assignment apply, taking the type of the scalar to be the 54 // unqualified version of its declared type. 55 if t.IsScalarType() && len(s) == 1 { 56 p.w("%s%s", tidyComment("", s[0]), tag) 57 switch { 58 case tld != nil && t.Kind() == cc.Ptr && s[0].AssignmentExpression.Operand.Value() == nil: 59 tld.patches = append(tld.patches, initPatch{t, s[0], patchField}) 60 p.w(" 0 ") 61 default: 62 p.assignmentExpression(f, s[0].AssignmentExpression, t, exprValue, 0) 63 } 64 return 65 } 66 67 // 12: The rest of this subclause deals with initializers for objects that have 68 // aggregate or union type. 69 70 k := t.Kind() 71 72 // 13: The initializer for a structure or union object that has automatic 73 // storage duration shall be either an initializer list as described below, or 74 // a single expression that has compatible structure or union type. In the 75 // latter case, the initial value of the object, including unnamed members, is 76 // that of the expression. 77 if sc == cc.Automatic && len(s) == 1 { 78 switch k { 79 case cc.Struct, cc.Union: 80 if compatibleStructOrUnion(t, s[0].AssignmentExpression.Operand.Type()) { 81 p.w("%s%s", tidyComment("", s[0]), tag) 82 p.assignmentExpression(f, s[0].AssignmentExpression, t, exprValue, 0) 83 return 84 } 85 } 86 } 87 88 if k == cc.Array && len(s) == 1 { 89 et := t.Elem() 90 switch { 91 case isCharType(et): 92 // 14: An array of character type may be initialized by a character string 93 // literal, optionally enclosed in braces. Successive characters of the 94 // character string literal (including the terminating null character if there 95 // is room or if the array is of unknown size) initialize the elements of the 96 // array. 97 if x, ok := s[0].AssignmentExpression.Operand.Value().(cc.StringValue); ok { 98 p.w("%s%s", tidyComment("", s[0]), tag) 99 str := cc.StringID(x).String() 100 slen := uintptr(len(str)) + 1 101 alen := t.Len() 102 switch { 103 case alen < slen-1: 104 p.w("*(*%s)(unsafe.Pointer(%s))", p.typ(s[0], t), p.stringLiteralString(str[:alen])) 105 case alen < slen: 106 p.w("*(*%s)(unsafe.Pointer(%s))", p.typ(s[0], t), p.stringLiteralString(str)) 107 default: // alen >= slen 108 p.w("*(*%s)(unsafe.Pointer(%s))", p.typ(s[0], t), p.stringLiteralString(str+strings.Repeat("\x00", int(alen-slen)))) 109 } 110 return 111 } 112 case p.isWCharType(et): 113 // 15: An array with element type compatible with wchar_t may be initialized by 114 // a wide string literal, optionally enclosed in braces. Successive wide 115 // characters of the wide string literal (including the terminating null wide 116 // character if there is room or if the array is of unknown size) initialize 117 // the elements of the array. 118 if x, ok := s[0].AssignmentExpression.Operand.Value().(cc.WideStringValue); ok { 119 p.w("%s%s", tidyComment("", s[0]), tag) 120 str := []rune(cc.StringID(x).String()) 121 slen := uintptr(len(str)) + 1 122 alen := t.Len() 123 switch { 124 case alen < slen-1: 125 panic(todo("", p.pos(s[0]))) 126 case alen < slen: 127 p.w("*(*%s)(unsafe.Pointer(%s))", p.typ(s[0], t), p.wideStringLiteral(x, 0)) 128 default: // alen >= slen 129 p.w("*(*%s)(unsafe.Pointer(%s))", p.typ(s[0], t), p.wideStringLiteral(x, int(alen-slen))) 130 } 131 return 132 } 133 } 134 } 135 136 // 16: Otherwise, the initializer for an object that has aggregate or union 137 // type shall be a brace-enclosed list of initializers for the elements or 138 // named members. 139 switch k { 140 case cc.Array: 141 p.initializerArray(tag, off, f, s, t, sc, tld, lm, tm) 142 case cc.Struct: 143 p.initializerStruct(tag, off, f, s, t, sc, tld, lm, tm) 144 case cc.Union: 145 p.initializerUnion(tag, off, f, s, t, sc, tld, lm, tm) 146 default: 147 panic(todo("%v: internal error: %v alias %v %v", s[0].Position(), t, t.Alias(), len(s))) 148 } 149 } 150 151 func (p *project) initializerArray(tag string, off uintptr, f *function, s []*cc.Initializer, t cc.Type, sc cc.StorageClass, tld *tld, lm, tm map[*cc.Initializer][]cc.StringID) { 152 if len(s) == 0 { 153 p.w("%s%s{}", tag, p.typ(nil, t)) 154 return 155 } 156 157 et := t.Elem() 158 esz := et.Size() 159 s0 := s[0] 160 p.w("%s%s%s{", initComment(s0, lm), tag, p.typ(s0, t)) 161 var a [][]*cc.Initializer 162 for len(s) != 0 { 163 s2, parts, _ := p.initializerArrayElement(off, s, esz) 164 s = s2 165 a = append(a, parts) 166 } 167 mustIndex := uintptr(len(a)) != t.Len() 168 var parts []*cc.Initializer 169 for _, parts = range a { 170 var comma *cc.Token 171 comma = parts[len(parts)-1].TrailingComma() 172 elemOff := (parts[0].Offset - off) / esz * esz 173 tag = "" 174 if mustIndex { 175 tag = fmt.Sprintf("%d:", elemOff/esz) 176 } 177 p.initializerInner(tag, off+elemOff, f, parts, et, sc, tld, nil, lm, tm) 178 p.preCommaSep(comma) 179 p.w(",") 180 } 181 p.w("%s}", initComment(parts[len(parts)-1], tm)) 182 } 183 184 func initComment(n *cc.Initializer, m map[*cc.Initializer][]cc.StringID) string { 185 a := m[n] 186 if len(a) == 0 { 187 return "" 188 } 189 190 m[n] = a[1:] 191 return tidyCommentString(a[0].String()) 192 } 193 194 func (p *project) initializerArrayElement(off uintptr, s []*cc.Initializer, elemSize uintptr) (r []*cc.Initializer, parts []*cc.Initializer, isZero bool) { 195 r = s 196 isZero = true 197 valueOff := s[0].Offset - off 198 elemOff := valueOff - valueOff%elemSize 199 nextOff := elemOff + elemSize 200 for len(s) != 0 { 201 if v := s[0]; v.Offset-off < nextOff { 202 s = s[1:] 203 parts = append(parts, v) 204 if !v.AssignmentExpression.Operand.IsZero() { 205 isZero = false 206 } 207 continue 208 } 209 210 break 211 } 212 return r[len(parts):], parts, isZero 213 } 214 215 func (p *project) initializerStruct(tag string, off uintptr, f *function, s []*cc.Initializer, t cc.Type, sc cc.StorageClass, tld *tld, lm, tm map[*cc.Initializer][]cc.StringID) { 216 if len(s) == 0 { 217 p.w("%s%s{}", tag, p.typ(nil, t)) 218 return 219 } 220 221 if t.HasFlexibleMember() { 222 p.err(s[0], "flexible array members not supported") 223 return 224 } 225 226 p.w("%s%s%s{", initComment(s[0], lm), tag, p.typ(s[0], t)) 227 var parts []*cc.Initializer 228 var isZero bool 229 var fld cc.Field 230 for len(s) != 0 { 231 var comma *cc.Token 232 s, fld, parts, isZero = p.structInitializerParts(off, s, t) 233 if isZero { 234 continue 235 } 236 237 if fld.Type().IsIncomplete() { 238 panic(todo("")) 239 } 240 241 comma = parts[len(parts)-1].TrailingComma() 242 tag = fmt.Sprintf("%s:", p.fieldName2(parts[0], fld)) 243 ft := fld.Type() 244 switch { 245 case fld.IsBitField(): 246 bft := p.bitFileType(parts[0], fld.BitFieldBlockWidth()) 247 off0 := fld.Offset() 248 first := true 249 for _, v := range parts { 250 if v.AssignmentExpression.Operand.IsZero() { 251 continue 252 } 253 254 if !first { 255 p.w("|") 256 } 257 first = false 258 bitFld := v.Field 259 p.w("%s%s", tidyComment("", v.AssignmentExpression), tag) 260 tag = "" 261 p.assignmentExpression(f, v.AssignmentExpression, bft, exprValue, 0) 262 p.w("&%#x", uint64(1)<<uint64(bitFld.BitFieldWidth())-1) 263 if o := bitFld.BitFieldOffset() + 8*int((bitFld.Offset()-off0)); o != 0 { 264 p.w("<<%d", o) 265 } 266 } 267 default: 268 p.initializerInner(tag, off+fld.Offset(), f, parts, ft, sc, tld, fld, lm, tm) 269 } 270 p.preCommaSep(comma) 271 p.w(",") 272 } 273 p.w("%s}", initComment(parts[len(parts)-1], tm)) 274 } 275 276 func (p *project) preCommaSep(comma *cc.Token) { 277 if comma == nil { 278 return 279 } 280 281 p.w("%s", strings.TrimSpace(comma.Sep.String())) 282 } 283 284 func (p *project) structInitializerParts(off uintptr, s []*cc.Initializer, t cc.Type) (r []*cc.Initializer, fld cc.Field, parts []*cc.Initializer, isZero bool) { 285 if len(s) == 0 { 286 return nil, nil, nil, true 287 } 288 289 part := s[0] 290 isZero = part.AssignmentExpression.Operand.IsZero() 291 parts = append(parts, part) 292 s = s[1:] 293 fld, _, fNext := p.containingStructField(part, off, t) 294 for len(s) != 0 { 295 part = s[0] 296 vOff := part.Offset 297 if vOff >= fNext { 298 break 299 } 300 301 isZero = isZero && part.AssignmentExpression.Operand.IsZero() 302 parts = append(parts, part) 303 s = s[1:] 304 } 305 return s, fld, parts, isZero 306 } 307 308 func (p *project) containingStructField(part *cc.Initializer, off uintptr, t cc.Type) (f cc.Field, fOff, fNext uintptr) { 309 nf := t.NumField() 310 vOff := part.Offset 311 for i := []int{0}; i[0] < nf; i[0]++ { 312 f = t.FieldByIndex(i) 313 if f.IsBitField() && f.Name() == 0 { // Anonymous bit fields cannot be initialized. 314 continue 315 } 316 317 fOff = off + f.Offset() 318 switch { 319 case f.IsBitField(): 320 fNext = fOff + uintptr(f.BitFieldBlockWidth())>>3 321 default: 322 fNext = fOff + f.Type().Size() 323 } 324 if vOff >= fOff && vOff < fNext { 325 return f, fOff, fNext 326 } 327 } 328 329 panic(todo("%v: internal error", p.pos(part))) 330 } 331 332 func (p *project) initializerUnion(tag string, off uintptr, f *function, s []*cc.Initializer, t cc.Type, sc cc.StorageClass, tld *tld, lm, tm map[*cc.Initializer][]cc.StringID) { 333 if len(s) == 0 { 334 p.w("%s%s{}", tag, p.typ(nil, t)) 335 return 336 } 337 338 if t.HasFlexibleMember() { 339 p.err(s[0], "flexible array members not supported") 340 return 341 } 342 343 parts, isZero := p.initializerUnionField(off, s, t) 344 if len(parts) == 0 || isZero { 345 p.w("%s%s%s{", initComment(s[0], lm), tag, p.typ(s[0], t)) 346 p.w("%s}", initComment(parts[len(parts)-1], tm)) 347 return 348 } 349 350 p.w("%sfunc() (r %s) {", tag, p.typ(parts[0], t)) 351 for _, part := range parts { 352 var ft cc.Type 353 fld := part.Field 354 if fld != nil && fld.IsBitField() { 355 } 356 357 if ft == nil { 358 ft = part.Type() 359 } 360 if ft.Kind() == cc.Array { 361 et := ft.Elem() 362 switch { 363 case isCharType(et): 364 switch x := part.AssignmentExpression.Operand.Value().(type) { 365 case cc.StringValue: 366 str := cc.StringID(x).String() 367 slen := uintptr(len(str)) + 1 368 alen := ft.Len() 369 switch { 370 case alen < slen-1: 371 p.w("copy(((*[%d]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&r))+%d)))[:], (*[%d]byte)(unsafe.Pointer(%s))[:])\n", alen, part.Offset-off, alen, p.stringLiteralString(str[:alen])) 372 case alen < slen: 373 p.w("copy(((*[%d]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&r))+%d)))[:], (*[%d]byte)(unsafe.Pointer(%s))[:])\n", alen, part.Offset-off, alen, p.stringLiteralString(str)) 374 default: // alen >= slen 375 p.w("copy(((*[%d]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&r))+%d)))[:], (*[%d]byte)(unsafe.Pointer(%s))[:])\n", alen, part.Offset-off, alen, p.stringLiteralString(str+strings.Repeat("\x00", int(alen-slen)))) 376 } 377 continue 378 default: 379 panic(todo("%v: %v <- %T", p.pos(part), et, x)) 380 } 381 case p.isWCharType(et): 382 panic(todo("")) 383 } 384 ft = et 385 } 386 switch { 387 case fld != nil && fld.IsBitField(): 388 bft := p.bitFileType(part, fld.BitFieldBlockWidth()) 389 p.w("*(*%s)(unsafe.Pointer(uintptr(unsafe.Pointer(&r))+%d)) |= ", p.typ(part, bft), part.Offset-off) 390 p.assignmentExpression(f, part.AssignmentExpression, bft, exprValue, 0) 391 p.w("&%#x", uint64(1)<<uint64(fld.BitFieldWidth())-1) 392 if o := fld.BitFieldOffset(); o != 0 { 393 p.w("<<%d", o) 394 } 395 default: 396 p.w("*(*%s)(unsafe.Pointer(uintptr(unsafe.Pointer(&r))+%d)) = ", p.typ(part, ft), part.Offset-off) 397 p.assignmentExpression(f, part.AssignmentExpression, ft, exprValue, 0) 398 } 399 p.w("\n") 400 } 401 p.w("return r\n") 402 p.w("}()") 403 } 404 405 func (p *project) initializerUnionField(off uintptr, s []*cc.Initializer, t cc.Type) (parts []*cc.Initializer, isZero bool) { 406 isZero = true 407 nextOff := off + t.Size() 408 for len(s) != 0 { 409 if v := s[0]; v.Offset < nextOff { 410 s = s[1:] 411 parts = append(parts, v) 412 isZero = isZero && v.AssignmentExpression.Operand.IsZero() 413 continue 414 } 415 416 break 417 } 418 return parts, isZero 419 } 420 421 func compatibleStructOrUnion(t1, t2 cc.Type) bool { 422 switch t1.Kind() { 423 case cc.Struct: 424 if t2.Kind() != cc.Struct { 425 return false 426 } 427 case cc.Union: 428 if t2.Kind() != cc.Union { 429 return false 430 } 431 default: 432 return false 433 } 434 435 if tag := t1.Tag(); tag != 0 && t2.Tag() != tag { 436 return false 437 } 438 439 nf := t1.NumField() 440 if t2.NumField() != nf { 441 return false 442 } 443 444 for i := []int{0}; i[0] < nf; i[0]++ { 445 f1 := t1.FieldByIndex(i) 446 f2 := t2.FieldByIndex(i) 447 nm := f1.Name() 448 if f2.Name() != nm { 449 return false 450 } 451 452 ft1 := f1.Type() 453 ft2 := f2.Type() 454 if ft1.Size() != ft2.Size() || 455 f1.IsBitField() != f2.IsBitField() || 456 f1.BitFieldOffset() != f2.BitFieldOffset() || 457 f1.BitFieldWidth() != f2.BitFieldWidth() { 458 return false 459 } 460 461 if !compatibleType(ft1, ft2) { 462 return false 463 } 464 } 465 return true 466 } 467 468 func compatibleType(t1, t2 cc.Type) bool { 469 if t1.Kind() != t2.Kind() { 470 return false 471 } 472 473 switch t1.Kind() { 474 case cc.Array: 475 if t1.Len() != t2.Len() || !compatibleType(t1.Elem(), t2.Elem()) { 476 return false 477 } 478 case cc.Struct, cc.Union: 479 if !compatibleStructOrUnion(t1, t2) { 480 return false 481 } 482 } 483 return true 484 } 485 486 func (p *project) bitFileType(n cc.Node, bits int) cc.Type { 487 switch bits { 488 case 8: 489 return p.task.cfg.ABI.Type(cc.UChar) 490 case 16: 491 return p.task.cfg.ABI.Type(cc.UShort) 492 case 32: 493 return p.task.cfg.ABI.Type(cc.UInt) 494 case 64: 495 return p.task.cfg.ABI.Type(cc.ULongLong) 496 default: 497 panic(todo("%v: internal error: %v", n.Position(), bits)) 498 } 499 } 500 501 func (p *project) isWCharType(t cc.Type) bool { 502 if t.IsAliasType() { 503 if id := t.AliasDeclarator().Name(); id == idWcharT || 504 p.task.goos == "windows" && id == idWinWchar { 505 return true 506 } 507 } 508 509 return false 510 } 511 512 func isCharType(t cc.Type) bool { 513 switch t.Kind() { 514 case cc.Char, cc.SChar, cc.UChar: 515 return true 516 } 517 518 return false 519 } 520 521 func (p *project) initializerFlatten(n *cc.Initializer, lm, tm map[*cc.Initializer][]cc.StringID) (s []*cc.Initializer) { 522 switch n.Case { 523 case cc.InitializerExpr: // AssignmentExpression 524 return append(s, n) 525 case cc.InitializerInitList: // '{' InitializerList ',' '}' 526 first := true 527 for list := n.InitializerList; list != nil; list = list.InitializerList { 528 in := list.Initializer 529 k := in 530 if in.Case != cc.InitializerExpr { 531 k = nil 532 } 533 if first { 534 lm[k] = append(lm[k], append(lm[nil], n.Token.Sep)...) 535 if k != nil { 536 delete(lm, nil) 537 } 538 first = false 539 } 540 if list.InitializerList == nil { 541 tm[k] = append([]cc.StringID{n.Token3.Sep}, append(tm[nil], tm[k]...)...) 542 tm[k] = append(tm[k], append(tm[nil], n.Token3.Sep)...) 543 if k != nil { 544 delete(tm, nil) 545 } 546 } 547 s = append(s, p.initializerFlatten(in, lm, tm)...) 548 } 549 return s 550 default: 551 panic(todo("%v: internal error: %v", n.Position(), n.Case)) 552 } 553 }