github.com/gotranspile/cxgo@v0.3.8-0.20240118201721-29871598a6a2/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 (
   111  	libs   = make(map[string]LibraryFunc)
   112  	libsrc = make(map[string]string)
   113  )
   114  
   115  type LibraryFunc func(c *Env) *Library
   116  
   117  // RegisterLibrary registers an override for a C library.
   118  //
   119  // If the library provides no custom cxgo identifiers or types, RegisterLibrarySrc can be used instead.
   120  func RegisterLibrary(name string, fnc LibraryFunc) {
   121  	if name == "" {
   122  		panic("empty name")
   123  	}
   124  	if fnc == nil {
   125  		panic("no constructor")
   126  	}
   127  	if _, ok := libs[name]; ok {
   128  		panic("already registered")
   129  	}
   130  	libs[name] = fnc
   131  }
   132  
   133  // RegisterLibrarySrc registers an override for a C library source.
   134  //
   135  // For registering custom cxgo identifiers or types see RegisterLibrary.
   136  func RegisterLibrarySrc(name string, hdr string) {
   137  	if name == "" {
   138  		panic("empty name")
   139  	}
   140  	if _, ok := libsrc[name]; ok {
   141  		panic("already registered")
   142  	}
   143  	libsrc[name] = hdr
   144  }
   145  
   146  const IncludePath = "/_cxgo_overrides"
   147  
   148  var defPathReplacer = strings.NewReplacer(
   149  	"/", "_",
   150  	".", "_",
   151  )
   152  
   153  // GetLibrary finds or initializes the library, given a C include filename.
   154  func (c *Env) GetLibrary(name string) (*Library, bool) {
   155  	if v, ok := c.Map[name]; ok {
   156  		name = v
   157  	}
   158  	if c.NoLibs && name != BuiltinH {
   159  		return nil, false
   160  	}
   161  	l, ok := c.libs[name]
   162  	if ok {
   163  		return l, true
   164  	}
   165  	// First consult a general library registry.
   166  	// If not found, but we have a library source - register library anyway.
   167  	if fnc, ok := libs[name]; ok {
   168  		l = fnc(c)
   169  	} else if _, ok := libsrc[name]; ok {
   170  		l = new(Library)
   171  	} else {
   172  		return nil, false
   173  	}
   174  	l.created = true
   175  	l.Name = name
   176  	//for name, typ := range l.Types {
   177  	//	named, ok := typ.(types.Named)
   178  	//	if !ok {
   179  	//		continue
   180  	//	}
   181  	//	if _, ok := l.Idents[name]; !ok {
   182  	//		if l.Idents == nil {
   183  	//			l.Idents = make(map[string]*types.Ident)
   184  	//		}
   185  	//		l.Idents[name] = named.Name()
   186  	//	}
   187  	//}
   188  	c.libs[name] = l
   189  	for k, v := range l.Imports {
   190  		c.imports[k] = v
   191  	}
   192  	for k, v := range l.ForceMacros {
   193  		c.macros[k] = v
   194  	}
   195  	ifdef := "_cxgo_" + strings.ToUpper(defPathReplacer.Replace(name))
   196  	var hdr strings.Builder
   197  	hdr.WriteString("#ifndef " + ifdef + "\n")
   198  	hdr.WriteString("#define " + ifdef + "\n")
   199  	if name != BuiltinH {
   200  		hdr.WriteString("#include <" + BuiltinH + ">\n")
   201  	}
   202  	hdr.WriteString("\n")
   203  	if src := libsrc[name]; src != "" {
   204  		hdr.WriteString(src)
   205  		hdr.WriteString("\n")
   206  	}
   207  	if l.Header != "" {
   208  		hdr.WriteString(l.Header)
   209  		hdr.WriteString("\n")
   210  	}
   211  	hdr.WriteString("\n#endif // " + ifdef + "\n")
   212  	l.Header = hdr.String()
   213  	return l, true
   214  }
   215  
   216  // GetLibraryType is a helper for GetLibrary followed by GetType.
   217  func (c *Env) GetLibraryType(lib, typ string) types.Type {
   218  	l, ok := c.GetLibrary(lib)
   219  	if !ok {
   220  		panic("cannot find library: " + lib)
   221  	}
   222  	return l.GetType(typ)
   223  }
   224  
   225  func (c *Env) NewLibrary(path string) (*Library, bool) {
   226  	if !strings.HasPrefix(path, IncludePath+"/") {
   227  		return nil, false // only override ones in our fake lookup path
   228  	}
   229  	name := strings.TrimPrefix(path, IncludePath+"/")
   230  	return c.GetLibrary(name)
   231  }
   232  
   233  func (c *Env) TypeByName(name string) (types.Type, bool) {
   234  	if v, ok := c.Map[name]; ok {
   235  		name = v
   236  	}
   237  	if c.NoLibs && name != BuiltinH {
   238  		return nil, false
   239  	}
   240  	for _, l := range c.libs {
   241  		if t, ok := l.Types[name]; ok {
   242  			return t, true
   243  		}
   244  	}
   245  	return nil, false
   246  }
   247  
   248  func (c *Env) LibIdentByName(name string) (*Library, *types.Ident, bool) {
   249  	if v, ok := c.Map[name]; ok {
   250  		name = v
   251  	}
   252  	if c.NoLibs && name != BuiltinH {
   253  		return nil, nil, false
   254  	}
   255  	for _, l := range c.libs {
   256  		if id, ok := l.Idents[name]; ok {
   257  			return l, id, true
   258  		}
   259  	}
   260  	return nil, nil, false
   261  }
   262  
   263  func (c *Env) IdentByName(name string) (*types.Ident, bool) {
   264  	_, id, ok := c.LibIdentByName(name)
   265  	return id, ok
   266  }
   267  
   268  func (c *Env) ForceMacro(name string) bool {
   269  	ok := c.macros[name]
   270  	return ok
   271  }