source: code/trunk/vendor/modernc.org/ccgo/v3/lib/init.go@ 824

Last change on this file since 824 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 15.2 KB
Line 
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
5package ccgo // import "modernc.org/ccgo/v3/lib"
6
7import (
8 "fmt"
9 "sort"
10 "strings"
11
12 "modernc.org/cc/v3"
13)
14
15func 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
25func (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
49func (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
151func (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
184func 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
194func (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
215func (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
276func (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
284func (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
308func (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
332func (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
405func (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
421func 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
468func 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
486func (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
501func (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
512func 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
521func (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}
Note: See TracBrowser for help on using the repository browser.