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 }