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