modernc.org/ccgo/v3@v3.16.14/lib/init.go (about)

     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  }