compile_exports.mx raw
1 package main
2
3 import (
4 "runtime"
5 "unsafe"
6
7 . "git.smesh.lol/moxie/pkg/types"
8 )
9
10
11 //export moxie_compile_to_ir
12 func moxie_compile_to_ir(srcPtr unsafe.Pointer, srcLen int32, namePtr unsafe.Pointer, nameLen int32, triplePtr unsafe.Pointer, tripleLen int32) (n int32) {
13 runtime.InitCShared()
14 initCompileCtx()
15 src := unsafe.Slice((*byte)(srcPtr), srcLen)
16 name := string(unsafe.Slice((*byte)(namePtr), nameLen))
17 triple := string(unsafe.Slice((*byte)(triplePtr), tripleLen))
18
19 ir := CompileToIR(src, name, triple)
20
21 idx := int32(len(cctx.irResults))
22 push(cctx.irResults, ir)
23 return idx
24 }
25
26 //export moxie_compile_ir_len
27 func moxie_compile_ir_len(h int32) (n int32) {
28 runtime.InitCShared()
29 if h < 0 || int32(h) >= len(cctx.irResults) {
30 return 0
31 }
32 return int32(len(cctx.irResults[h]))
33 }
34
35 //export moxie_compile_ir_copy
36 func moxie_compile_ir_copy(h int32, dst unsafe.Pointer, capVal int32) (n int32) {
37 runtime.InitCShared()
38 if h < 0 || int32(h) >= len(cctx.irResults) {
39 return 0
40 }
41 ir := cctx.irResults[h]
42 n = int32(len(ir))
43 if n > capVal {
44 n = capVal
45 }
46 if n > 0 {
47 buf := unsafe.Slice((*byte)(dst), n)
48 copy(buf, ir[:n])
49 }
50 return n
51 }
52
53 //export moxie_compile_ir_free
54 func moxie_compile_ir_free(h int32) {
55 runtime.InitCShared()
56 if h >= 0 && int32(h) < len(cctx.irResults) {
57 cctx.irResults[h] = ""
58 }
59 }
60
61 //export moxie_register_package
62 func moxie_register_package(pathPtr unsafe.Pointer, pathLen int32, namePtr unsafe.Pointer, nameLen int32) {
63 runtime.InitCShared()
64 path := string(unsafe.Slice((*byte)(pathPtr), pathLen))
65 name := string(unsafe.Slice((*byte)(namePtr), nameLen))
66 cctx.universe.Registry[path] = NewTCPackage(path, name)
67 }
68
69 //export moxie_register_func
70 func moxie_register_func(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, sigPtr unsafe.Pointer, sigLen int32) {
71 runtime.InitCShared()
72 pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
73 name := string(unsafe.Slice((*byte)(namePtr), nameLen))
74 sigDesc := string(unsafe.Slice((*byte)(sigPtr), sigLen))
75 pkg := cctx.universe.Registry[pkgPath]
76 if pkg == nil {
77 return
78 }
79 old := cctx.parseTypePkgScope
80 cctx.parseTypePkgScope = pkg.Scope
81 sig := parseSignatureDesc(sigDesc)
82 cctx.parseTypePkgScope = old
83 pkg.Scope.Insert(NewTCFunc(pkg, name, sig))
84 }
85
86 //export moxie_register_var
87 func moxie_register_var(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, typDescPtr unsafe.Pointer, typDescLen int32) {
88 runtime.InitCShared()
89 pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
90 name := string(unsafe.Slice((*byte)(namePtr), nameLen))
91 typDesc := string(unsafe.Slice((*byte)(typDescPtr), typDescLen))
92 pkg := cctx.universe.Registry[pkgPath]
93 if pkg == nil {
94 return
95 }
96 typ := parseTypeDesc(typDesc)
97 pkg.Scope.Insert(NewTCVar(pkg, name, typ))
98 }
99
100 //export moxie_register_const
101 func moxie_register_const(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, typDescPtr unsafe.Pointer, typDescLen int32, val int64) {
102 runtime.InitCShared()
103 pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
104 name := string(unsafe.Slice((*byte)(namePtr), nameLen))
105 typDesc := string(unsafe.Slice((*byte)(typDescPtr), typDescLen))
106 pkg := cctx.universe.Registry[pkgPath]
107 if pkg == nil {
108 return
109 }
110 typ := parseTypeDesc(typDesc)
111 pkg.Scope.Insert(NewTCConst(pkg, name, typ, &ConstInt{V:val}))
112 }
113
114 //export moxie_register_const_string
115 func moxie_register_const_string(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, valPtr unsafe.Pointer, valLen int32) {
116 runtime.InitCShared()
117 pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
118 name := string(unsafe.Slice((*byte)(namePtr), nameLen))
119 val := string(unsafe.Slice((*byte)(valPtr), valLen))
120 pkg := cctx.universe.Registry[pkgPath]
121 if pkg == nil {
122 return
123 }
124 pkg.Scope.Insert(NewTCConst(pkg, name, Typ[UntypedString], &ConstStr{S:val}))
125 }
126
127 //export moxie_register_iface
128 func moxie_register_iface(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, methodsPtr unsafe.Pointer, methodsLen int32) {
129 runtime.InitCShared()
130 pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
131 name := string(unsafe.Slice((*byte)(namePtr), nameLen))
132 methodsDesc := string(unsafe.Slice((*byte)(methodsPtr), methodsLen))
133 pkg := cctx.universe.Registry[pkgPath]
134 if pkg == nil {
135 return
136 }
137 var methods []*IfaceMethod
138 if methodsDesc != "" {
139 parts := splitSemicolon(methodsDesc)
140 for _, p := range parts {
141 eqIdx := -1
142 for i := 0; i < len(p); i++ {
143 if p[i] == '=' {
144 eqIdx = i
145 break
146 }
147 }
148 if eqIdx < 0 {
149 continue
150 }
151 mname := p[:eqIdx]
152 msig := parseSignatureDesc(p[eqIdx+1:])
153 push(methods, NewTCIfaceMethod(mname, msig))
154 }
155 }
156 iface := NewTCInterface(methods, nil)
157 iface.Complete()
158 tn := NewTypeName(pkg, name, iface)
159 pkg.Scope.Insert(tn)
160 }
161
162 //export moxie_register_type
163 func moxie_register_type(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, underlyingPtr unsafe.Pointer, underlyingLen int32) {
164 runtime.InitCShared()
165 pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
166 name := string(unsafe.Slice((*byte)(namePtr), nameLen))
167 pkg := cctx.universe.Registry[pkgPath]
168 if pkg == nil {
169 return
170 }
171 var underlying Type
172 if underlyingLen > 0 {
173 desc := string(unsafe.Slice((*byte)(underlyingPtr), underlyingLen))
174 underlying = parseTypeDesc(desc)
175 } else {
176 underlying = NewTCStruct(nil, nil)
177 }
178 tn := NewTypeName(pkg, name, nil)
179 NewNamed(tn, underlying)
180 pkg.Scope.Insert(tn)
181 }
182
183 //export moxie_register_method
184 func moxie_register_method(pkgPathPtr unsafe.Pointer, pkgPathLen int32, typeNamePtr unsafe.Pointer, typeNameLen int32, methodNamePtr unsafe.Pointer, methodNameLen int32, sigPtr unsafe.Pointer, sigLen int32, ptrRecv int32) {
185 runtime.InitCShared()
186 pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
187 typeName := string(unsafe.Slice((*byte)(typeNamePtr), typeNameLen))
188 methodName := string(unsafe.Slice((*byte)(methodNamePtr), methodNameLen))
189 sigDesc := string(unsafe.Slice((*byte)(sigPtr), sigLen))
190 pkg := cctx.universe.Registry[pkgPath]
191 if pkg == nil {
192 return
193 }
194 obj := pkg.Scope.Lookup(typeName)
195 if obj == nil {
196 return
197 }
198 tn, ok := obj.(*TypeName)
199 if !ok {
200 return
201 }
202 named, ok := tn.Typ.(*Named)
203 if !ok {
204 return
205 }
206 old := cctx.parseTypePkgScope
207 cctx.parseTypePkgScope = pkg.Scope
208 sig := parseSignatureDesc(sigDesc)
209 cctx.parseTypePkgScope = old
210 var recvType Type = named
211 if ptrRecv != 0 {
212 recvType = NewPointer(named)
213 }
214 sig = NewSignature(NewTCVar(pkg, "", recvType), sig.Params, sig.Results, sig.Variadic)
215 fn := NewTCFunc(pkg, methodName, sig)
216 if ptrRecv != 0 {
217 fn.PtrRecv = true
218 }
219 named.AddMethod(fn)
220 }
221
222 func splitSemicolon(s string) (ss []string) {
223 var parts []string
224 start := 0
225 for i := 0; i < len(s); i++ {
226 if s[i] == ';' {
227 push(parts, s[start:i])
228 start = i + 1
229 }
230 }
231 push(parts, s[start:])
232 return parts
233 }
234
235 //export moxie_clear_imports
236 func moxie_clear_imports() {
237 runtime.InitCShared()
238 for k := range cctx.universe.Registry {
239 delete(cctx.universe.Registry, k)
240 }
241 }
242
243
244 func parseTypeDesc(desc string) (t Type) {
245 switch desc {
246 case "bool":
247 return Typ[Bool]
248 case "int8":
249 return Typ[Int8]
250 case "int16":
251 return Typ[Int16]
252 case "int32", "int32":
253 return Typ[Int32]
254 case "int64":
255 return Typ[Int64]
256 case "uint8", "byte":
257 return Typ[Uint8]
258 case "uint16":
259 return Typ[Uint16]
260 case "uint32", "uint32":
261 return Typ[Uint32]
262 case "uint64":
263 return Typ[Uint64]
264 case "float32":
265 return Typ[Float32]
266 case "float64":
267 return Typ[Float64]
268 case "string":
269 return Typ[TCString]
270 case "ptr":
271 return Typ[UnsafePointer]
272 case "error":
273 errorSig := NewSignature(nil, nil, NewTuple(NewTCVar(nil, "", Typ[TCString])), false)
274 errorIface := NewTCInterface([]*IfaceMethod{NewTCIfaceMethod("Error", errorSig), NewTCIfaceMethod("String", errorSig)}, nil)
275 errorIface.Complete()
276 return errorIface
277 case "struct", "struct{}":
278 return NewTCStruct(nil, nil)
279 case "interface{}":
280 emptyIface := NewTCInterface(nil, nil)
281 emptyIface.Complete()
282 return emptyIface
283 }
284 // TypeParam marker: $P -> reconstruct a TypeParam so substTypeParams can
285 // substitute it during monomorphization.
286 if len(desc) > 1 && desc[0] == '$' {
287 tpName := desc[1:]
288 tn := NewTypeName(nil, tpName, nil)
289 return NewTypeParam(tn, nil)
290 }
291 if len(desc) > 7 && desc[:7] == "struct{" && desc[len(desc)-1] == '}' {
292 inner := desc[7 : len(desc)-1]
293 if inner == "" {
294 return NewTCStruct(nil, nil)
295 }
296 fields := splitCommaBalanced(inner)
297 vars := []*TCVar{:0:len(fields)}
298 tags := []string{:0:len(fields)}
299 for _, f := range fields {
300 ci := -1
301 for k := 0; k < len(f); k++ {
302 if f[k] == ':' {
303 ci = k
304 break
305 }
306 }
307 if ci < 0 {
308 continue
309 }
310 fname := f[:ci]
311 anon := false
312 if len(fname) > 0 && fname[0] == '@' {
313 fname = fname[1:]
314 anon = true
315 }
316 ftype := parseTypeDesc(f[ci+1:])
317 v := NewTCVar(nil, fname, ftype)
318 v.Anonymous = anon
319 push(vars, v)
320 push(tags, "")
321 }
322 return NewTCStruct(vars, tags)
323 }
324 if len(desc) > 2 && desc[0] == '[' && desc[1] == ']' {
325 elem := parseTypeDesc(desc[2:])
326 if b, ok := elem.(*Basic); ok && b.Kind == Uint8 {
327 return Typ[TCString]
328 }
329 return NewSlice(elem)
330 }
331 if len(desc) > 2 && desc[0] == '[' && desc[1] != ']' {
332 closeBrack := int32(-1)
333 for k := int32(1); k < int32(len(desc)); k++ {
334 if desc[k] == ']' {
335 closeBrack = k
336 break
337 }
338 }
339 if closeBrack > 1 {
340 n := ParseIntLocal(desc[1:closeBrack])
341 elem := parseTypeDesc(desc[closeBrack+1:])
342 return NewArray(elem, n)
343 }
344 }
345 if len(desc) > 1 && desc[0] == '*' {
346 return NewPointer(parseTypeDesc(desc[1:]))
347 }
348 if len(desc) > 5 && desc[:5] == "func(" && desc[len(desc)-1] == ')' {
349 return parseSignatureDesc(desc[5 : len(desc)-1])
350 }
351 if len(desc) > 5 && desc[:5] == "chan:" {
352 return NewTCChan(TCSendRecv, parseTypeDesc(desc[5:]))
353 }
354 if len(desc) > 7 && desc[:7] == "chan<-:" {
355 return NewTCChan(TCSendOnly, parseTypeDesc(desc[7:]))
356 }
357 if len(desc) > 7 && desc[:7] == "<-chan:" {
358 return NewTCChan(TCRecvOnly, parseTypeDesc(desc[7:]))
359 }
360 if len(desc) > 4 && desc[:4] == "map[" {
361 depth := int32(0)
362 closeBrack := int32(-1)
363 for k := int32(4); k < int32(len(desc)); k++ {
364 if desc[k] == '[' {
365 depth++
366 } else if desc[k] == ']' {
367 if depth == 0 {
368 closeBrack = k
369 break
370 }
371 depth--
372 }
373 }
374 if closeBrack > 4 {
375 key := parseTypeDesc(desc[4:closeBrack])
376 val := parseTypeDesc(desc[closeBrack+1:])
377 return NewTCMap(key, val)
378 }
379 }
380 dotIdx := int32(-1)
381 for i := int32(len(desc)) - 1; i >= 0; i-- {
382 if desc[i] == '.' {
383 dotIdx = i
384 break
385 }
386 }
387 if dotIdx > 0 {
388 pkgPath := desc[:dotIdx]
389 typeName := desc[dotIdx+1:]
390 if extPkg, ok := cctx.universe.Registry[pkgPath]; ok {
391 obj := extPkg.Scope.Lookup(typeName)
392 if tn, _ := obj.(*TypeName); tn != nil && tn.Typ != nil {
393 return tn.Typ
394 }
395 }
396 }
397 if cctx.parseTypePkgScope != nil {
398 obj := cctx.parseTypePkgScope.Lookup(desc)
399 if tn, ok := obj.(*TypeName); ok && tn.Typ != nil {
400 return tn.Typ
401 }
402 }
403 // Universe-scope named types (uintptr, byte, rune) appear in mxh descs by
404 // name; without this lookup they degrade to int32 and receiver widths in
405 // cached packages diverge from the source-compiled layout.
406 if Universe != nil {
407 obj := Universe.Lookup(desc)
408 if tn, ok := obj.(*TypeName); ok && tn.Typ != nil {
409 return tn.Typ
410 }
411 }
412 return Typ[Int32]
413 }
414
415 func parseSignatureDesc(desc string) (s *Signature) {
416 // format: "param1,param2->result1,result2" or "param1,param2" (void) or "->result" (no params)
417 // The arrow and commas are matched at depth 0 only: nested func/struct
418 // descs carry their own arrows and commas inside brackets.
419 arrow := -1
420 depth := 0
421 for i := 0; i < len(desc)-1; i++ {
422 switch desc[i] {
423 case '{', '[', '(':
424 depth++
425 case '}', ']', ')':
426 depth--
427 case '-':
428 if depth == 0 && desc[i+1] == '>' {
429 arrow = i
430 }
431 }
432 if arrow >= 0 {
433 break
434 }
435 }
436 var paramStr, resultStr string
437 if arrow >= 0 {
438 paramStr = desc[:arrow]
439 resultStr = desc[arrow+2:]
440 } else {
441 paramStr = desc
442 }
443 variadic := false
444 var params []*TCVar
445 if paramStr != "" {
446 parts := splitCommaBalanced(paramStr)
447 params = []*TCVar{:0:len(parts)}
448 for pi, p := range parts {
449 if pi == len(parts)-1 && len(p) > 3 && p[:3] == "..." {
450 variadic = true
451 push(params, NewTCVar(nil, "", NewSlice(parseTypeDesc(p[3:]))))
452 continue
453 }
454 push(params, NewTCVar(nil, "", parseTypeDesc(p)))
455 }
456 }
457 var results []*TCVar
458 if resultStr != "" {
459 parts := splitCommaBalanced(resultStr)
460 results = []*TCVar{:0:len(parts)}
461 for _, r := range parts {
462 push(results, NewTCVar(nil, "", parseTypeDesc(r)))
463 }
464 }
465 var pTuple, rTuple *Tuple
466 if len(params) > 0 {
467 pTuple = NewTuple(params...)
468 }
469 if len(results) > 0 {
470 rTuple = NewTuple(results...)
471 }
472 return NewSignature(nil, pTuple, rTuple, variadic)
473 }
474
475 func splitCommaBalanced(s string) (ss []string) {
476 // Count commas at depth 0.
477 n := int32(1)
478 depth := int32(0)
479 for i := int32(0); i < len(s); i++ {
480 switch s[i] {
481 case '{', '[', '(':
482 depth++
483 case '}', ']', ')':
484 depth--
485 case ',':
486 if depth == 0 {
487 n++
488 }
489 }
490 }
491 parts := []string{:0:n}
492 start := int32(0)
493 depth = 0
494 for i := int32(0); i < len(s); i++ {
495 switch s[i] {
496 case '{', '[', '(':
497 depth++
498 case '}', ']', ')':
499 depth--
500 case ',':
501 if depth == 0 {
502 push(parts, s[start:i])
503 start = i + 1
504 }
505 }
506 }
507 push(parts, s[start:])
508 return parts
509 }
510
511 func splitComma(s string) (ss []string) {
512 n := int32(1)
513 for i := int32(0); i < len(s); i++ {
514 if s[i] == ',' {
515 n++
516 }
517 }
518 parts := []string{:0:n}
519 start := int32(0)
520 for i := int32(0); i < len(s); i++ {
521 if s[i] == ',' {
522 push(parts, s[start:i])
523 start = i + 1
524 }
525 }
526 push(parts, s[start:])
527 return parts
528 }
529
530