github.com/gotranspile/cxgo@v0.3.7/libs/registry.go (about)

     1  package libs
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/gotranspile/cxgo/types"
     9  )
    10  
    11  type Library struct {
    12  	created bool
    13  
    14  	Name string
    15  	// Header is a content of a C header file for the library.
    16  	// It will be protected by a ifndef guard automatically.
    17  	Header string
    18  	// Types overrides type definitions parsed from the library's header with a custom ones.
    19  	Types map[string]types.Type
    20  
    21  	Idents map[string]*types.Ident
    22  
    23  	Imports map[string]string
    24  
    25  	ForceMacros map[string]bool
    26  }
    27  
    28  func (l *Library) GetType(name string) types.Type {
    29  	t, ok := l.Types[name]
    30  	if !ok {
    31  		panic(errors.New("cannot find type: " + name))
    32  	}
    33  	return t
    34  }
    35  
    36  // Declare adds a new ident to the library. It also writes a corresponding C definition to the header.
    37  func (l *Library) Declare(ids ...*types.Ident) {
    38  	for _, id := range ids {
    39  		l.declare(id)
    40  	}
    41  }
    42  
    43  func (l *Library) declare(id *types.Ident) {
    44  	if l.Idents == nil {
    45  		l.Idents = make(map[string]*types.Ident)
    46  	}
    47  	l.Idents[id.Name] = id
    48  	switch tp := id.CType(nil).(type) {
    49  	case *types.FuncType:
    50  		l.Header += fmt.Sprintf("%s %s(", cTypeStr(tp.Return()), id.Name)
    51  		for i, a := range tp.Args() {
    52  			if i != 0 {
    53  				l.Header += ", "
    54  			}
    55  			l.Header += cTypeStr(a.Type())
    56  		}
    57  		if tp.Variadic() {
    58  			if tp.ArgN() > 0 {
    59  				l.Header += ", "
    60  			}
    61  			l.Header += "..."
    62  		}
    63  		l.Header += ");\n"
    64  	default:
    65  		l.Header += fmt.Sprintf("%s %s;\n", cTypeStr(tp), id.Name)
    66  	}
    67  }
    68  
    69  func cTypeStr(t types.Type) string {
    70  	switch t := t.(type) {
    71  	case nil:
    72  		return "void"
    73  	case types.Named:
    74  		return t.Name().Name
    75  	case types.PtrType:
    76  		if t.Elem() == nil {
    77  			return "void*"
    78  		} else if el, ok := t.Elem().(types.Named); ok && el.Name().GoName == "byte" {
    79  			return "char*"
    80  		}
    81  		return cTypeStr(t.Elem()) + "*"
    82  	case types.IntType:
    83  		s := ""
    84  		if t.Signed() {
    85  			s = "signed "
    86  		} else {
    87  			s = "unsigned "
    88  		}
    89  		s += fmt.Sprintf("__int%d", t.Sizeof()*8)
    90  		return s
    91  	case types.FloatType:
    92  		switch t.Sizeof() {
    93  		case 4:
    94  			return "float"
    95  		case 8:
    96  			return "double"
    97  		default:
    98  			return fmt.Sprintf("_cxgo_float%d", t.Sizeof()*8)
    99  		}
   100  	case types.BoolType:
   101  		return "_Bool"
   102  	default:
   103  		if t.Kind() == types.Unknown {
   104  			return types.GoPrefix + "any"
   105  		}
   106  		panic(fmt.Errorf("TODO: %T", t))
   107  	}
   108  }
   109  
   110  var libs = make(map[string]LibraryFunc)
   111  
   112  type LibraryFunc func(c *Env) *Library
   113  
   114  // RegisterLibrary registers an override for a C library.
   115  func RegisterLibrary(name string, fnc LibraryFunc) {
   116  	if name == "" {
   117  		panic("empty name")
   118  	}
   119  	if fnc == nil {
   120  		panic("no constructor")
   121  	}
   122  	if _, ok := libs[name]; ok {
   123  		panic("already registered")
   124  	}
   125  	libs[name] = fnc
   126  }
   127  
   128  const IncludePath = "/_cxgo_overrides"
   129  
   130  var defPathReplacer = strings.NewReplacer(
   131  	"/", "_",
   132  	".", "_",
   133  )
   134  
   135  // GetLibrary finds or initializes the library, given a C include filename.
   136  func (c *Env) GetLibrary(name string) (*Library, bool) {
   137  	if l, ok := c.libs[name]; ok {
   138  		return l, true
   139  	}
   140  	fnc, ok := libs[name]
   141  	if !ok {
   142  		return nil, false
   143  	}
   144  	l := fnc(c)
   145  	l.created = true
   146  	l.Name = name
   147  	//for name, typ := range l.Types {
   148  	//	named, ok := typ.(types.Named)
   149  	//	if !ok {
   150  	//		continue
   151  	//	}
   152  	//	if _, ok := l.Idents[name]; !ok {
   153  	//		if l.Idents == nil {
   154  	//			l.Idents = make(map[string]*types.Ident)
   155  	//		}
   156  	//		l.Idents[name] = named.Name()
   157  	//	}
   158  	//}
   159  	c.libs[name] = l
   160  	for k, v := range l.Imports {
   161  		c.imports[k] = v
   162  	}
   163  	for k, v := range l.ForceMacros {
   164  		c.macros[k] = v
   165  	}
   166  
   167  	ifdef := "_cxgo_" + strings.ToUpper(defPathReplacer.Replace(name))
   168  	l.Header = fmt.Sprintf(`
   169  #ifndef %s
   170  #define %s
   171  
   172  %s
   173  
   174  #endif // %s
   175  `,
   176  		ifdef,
   177  		ifdef,
   178  		l.Header,
   179  		ifdef,
   180  	)
   181  	return l, true
   182  }
   183  
   184  // GetLibraryType is a helper for GetLibrary followed by GetType.
   185  func (c *Env) GetLibraryType(lib, typ string) types.Type {
   186  	l, ok := c.GetLibrary(lib)
   187  	if !ok {
   188  		panic("cannot find library: " + lib)
   189  	}
   190  	return l.GetType(typ)
   191  }
   192  
   193  func (c *Env) NewLibrary(path string) (*Library, bool) {
   194  	if !strings.HasPrefix(path, IncludePath+"/") {
   195  		return nil, false // only override ones in our fake lookup path
   196  	}
   197  	name := strings.TrimPrefix(path, IncludePath+"/")
   198  	return c.GetLibrary(name)
   199  }
   200  
   201  func (c *Env) TypeByName(name string) (types.Type, bool) {
   202  	for _, l := range c.libs {
   203  		if t, ok := l.Types[name]; ok {
   204  			return t, true
   205  		}
   206  	}
   207  	return nil, false
   208  }
   209  
   210  func (c *Env) LibIdentByName(name string) (*Library, *types.Ident, bool) {
   211  	for _, l := range c.libs {
   212  		if id, ok := l.Idents[name]; ok {
   213  			return l, id, true
   214  		}
   215  	}
   216  	return nil, nil, false
   217  }
   218  
   219  func (c *Env) IdentByName(name string) (*types.Ident, bool) {
   220  	_, id, ok := c.LibIdentByName(name)
   221  	return id, ok
   222  }
   223  
   224  func (c *Env) ForceMacro(name string) bool {
   225  	ok := c.macros[name]
   226  	return ok
   227  }