gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }