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 }