package main import ( "runtime" "unsafe" . "git.smesh.lol/moxie/pkg/types" ) //export moxie_compile_to_ir func moxie_compile_to_ir(srcPtr unsafe.Pointer, srcLen int32, namePtr unsafe.Pointer, nameLen int32, triplePtr unsafe.Pointer, tripleLen int32) (n int32) { runtime.InitCShared() initCompileCtx() src := unsafe.Slice((*byte)(srcPtr), srcLen) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) triple := string(unsafe.Slice((*byte)(triplePtr), tripleLen)) ir := CompileToIR(src, name, triple) idx := int32(len(cctx.irResults)) push(cctx.irResults, ir) return idx } //export moxie_compile_ir_len func moxie_compile_ir_len(h int32) (n int32) { runtime.InitCShared() if h < 0 || int32(h) >= len(cctx.irResults) { return 0 } return int32(len(cctx.irResults[h])) } //export moxie_compile_ir_copy func moxie_compile_ir_copy(h int32, dst unsafe.Pointer, capVal int32) (n int32) { runtime.InitCShared() if h < 0 || int32(h) >= len(cctx.irResults) { return 0 } ir := cctx.irResults[h] n = int32(len(ir)) if n > capVal { n = capVal } if n > 0 { buf := unsafe.Slice((*byte)(dst), n) copy(buf, ir[:n]) } return n } //export moxie_compile_ir_free func moxie_compile_ir_free(h int32) { runtime.InitCShared() if h >= 0 && int32(h) < len(cctx.irResults) { cctx.irResults[h] = "" } } //export moxie_register_package func moxie_register_package(pathPtr unsafe.Pointer, pathLen int32, namePtr unsafe.Pointer, nameLen int32) { runtime.InitCShared() path := string(unsafe.Slice((*byte)(pathPtr), pathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) cctx.universe.Registry[path] = NewTCPackage(path, name) } //export moxie_register_func func moxie_register_func(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, sigPtr unsafe.Pointer, sigLen int32) { runtime.InitCShared() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) sigDesc := string(unsafe.Slice((*byte)(sigPtr), sigLen)) pkg := cctx.universe.Registry[pkgPath] if pkg == nil { return } old := cctx.parseTypePkgScope cctx.parseTypePkgScope = pkg.Scope sig := parseSignatureDesc(sigDesc) cctx.parseTypePkgScope = old pkg.Scope.Insert(NewTCFunc(pkg, name, sig)) } //export moxie_register_var func moxie_register_var(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, typDescPtr unsafe.Pointer, typDescLen int32) { runtime.InitCShared() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) typDesc := string(unsafe.Slice((*byte)(typDescPtr), typDescLen)) pkg := cctx.universe.Registry[pkgPath] if pkg == nil { return } typ := parseTypeDesc(typDesc) pkg.Scope.Insert(NewTCVar(pkg, name, typ)) } //export moxie_register_const func moxie_register_const(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, typDescPtr unsafe.Pointer, typDescLen int32, val int64) { runtime.InitCShared() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) typDesc := string(unsafe.Slice((*byte)(typDescPtr), typDescLen)) pkg := cctx.universe.Registry[pkgPath] if pkg == nil { return } typ := parseTypeDesc(typDesc) pkg.Scope.Insert(NewTCConst(pkg, name, typ, &ConstInt{V:val})) } //export moxie_register_const_string func moxie_register_const_string(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, valPtr unsafe.Pointer, valLen int32) { runtime.InitCShared() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) val := string(unsafe.Slice((*byte)(valPtr), valLen)) pkg := cctx.universe.Registry[pkgPath] if pkg == nil { return } pkg.Scope.Insert(NewTCConst(pkg, name, Typ[UntypedString], &ConstStr{S:val})) } //export moxie_register_iface func moxie_register_iface(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, methodsPtr unsafe.Pointer, methodsLen int32) { runtime.InitCShared() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) methodsDesc := string(unsafe.Slice((*byte)(methodsPtr), methodsLen)) pkg := cctx.universe.Registry[pkgPath] if pkg == nil { return } var methods []*IfaceMethod if methodsDesc != "" { parts := splitSemicolon(methodsDesc) for _, p := range parts { eqIdx := -1 for i := 0; i < len(p); i++ { if p[i] == '=' { eqIdx = i break } } if eqIdx < 0 { continue } mname := p[:eqIdx] msig := parseSignatureDesc(p[eqIdx+1:]) push(methods, NewTCIfaceMethod(mname, msig)) } } iface := NewTCInterface(methods, nil) iface.Complete() tn := NewTypeName(pkg, name, iface) pkg.Scope.Insert(tn) } //export moxie_register_type func moxie_register_type(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, underlyingPtr unsafe.Pointer, underlyingLen int32) { runtime.InitCShared() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) pkg := cctx.universe.Registry[pkgPath] if pkg == nil { return } var underlying Type if underlyingLen > 0 { desc := string(unsafe.Slice((*byte)(underlyingPtr), underlyingLen)) underlying = parseTypeDesc(desc) } else { underlying = NewTCStruct(nil, nil) } tn := NewTypeName(pkg, name, nil) NewNamed(tn, underlying) pkg.Scope.Insert(tn) } //export moxie_register_method 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) { runtime.InitCShared() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) typeName := string(unsafe.Slice((*byte)(typeNamePtr), typeNameLen)) methodName := string(unsafe.Slice((*byte)(methodNamePtr), methodNameLen)) sigDesc := string(unsafe.Slice((*byte)(sigPtr), sigLen)) pkg := cctx.universe.Registry[pkgPath] if pkg == nil { return } obj := pkg.Scope.Lookup(typeName) if obj == nil { return } tn, ok := obj.(*TypeName) if !ok { return } named, ok := tn.Typ.(*Named) if !ok { return } old := cctx.parseTypePkgScope cctx.parseTypePkgScope = pkg.Scope sig := parseSignatureDesc(sigDesc) cctx.parseTypePkgScope = old var recvType Type = named if ptrRecv != 0 { recvType = NewPointer(named) } sig = NewSignature(NewTCVar(pkg, "", recvType), sig.Params, sig.Results, sig.Variadic) fn := NewTCFunc(pkg, methodName, sig) if ptrRecv != 0 { fn.PtrRecv = true } named.AddMethod(fn) } func splitSemicolon(s string) (ss []string) { var parts []string start := 0 for i := 0; i < len(s); i++ { if s[i] == ';' { push(parts, s[start:i]) start = i + 1 } } push(parts, s[start:]) return parts } //export moxie_clear_imports func moxie_clear_imports() { runtime.InitCShared() for k := range cctx.universe.Registry { delete(cctx.universe.Registry, k) } } func parseTypeDesc(desc string) (t Type) { switch desc { case "bool": return Typ[Bool] case "int8": return Typ[Int8] case "int16": return Typ[Int16] case "int32", "int32": return Typ[Int32] case "int64": return Typ[Int64] case "uint8", "byte": return Typ[Uint8] case "uint16": return Typ[Uint16] case "uint32", "uint32": return Typ[Uint32] case "uint64": return Typ[Uint64] case "float32": return Typ[Float32] case "float64": return Typ[Float64] case "string": return Typ[TCString] case "ptr": return Typ[UnsafePointer] case "error": errorSig := NewSignature(nil, nil, NewTuple(NewTCVar(nil, "", Typ[TCString])), false) errorIface := NewTCInterface([]*IfaceMethod{NewTCIfaceMethod("Error", errorSig), NewTCIfaceMethod("String", errorSig)}, nil) errorIface.Complete() return errorIface case "struct", "struct{}": return NewTCStruct(nil, nil) case "interface{}": emptyIface := NewTCInterface(nil, nil) emptyIface.Complete() return emptyIface } // TypeParam marker: $P -> reconstruct a TypeParam so substTypeParams can // substitute it during monomorphization. if len(desc) > 1 && desc[0] == '$' { tpName := desc[1:] tn := NewTypeName(nil, tpName, nil) return NewTypeParam(tn, nil) } if len(desc) > 7 && desc[:7] == "struct{" && desc[len(desc)-1] == '}' { inner := desc[7 : len(desc)-1] if inner == "" { return NewTCStruct(nil, nil) } fields := splitCommaBalanced(inner) vars := []*TCVar{:0:len(fields)} tags := []string{:0:len(fields)} for _, f := range fields { ci := -1 for k := 0; k < len(f); k++ { if f[k] == ':' { ci = k break } } if ci < 0 { continue } fname := f[:ci] anon := false if len(fname) > 0 && fname[0] == '@' { fname = fname[1:] anon = true } ftype := parseTypeDesc(f[ci+1:]) v := NewTCVar(nil, fname, ftype) v.Anonymous = anon push(vars, v) push(tags, "") } return NewTCStruct(vars, tags) } if len(desc) > 2 && desc[0] == '[' && desc[1] == ']' { elem := parseTypeDesc(desc[2:]) if b, ok := elem.(*Basic); ok && b.Kind == Uint8 { return Typ[TCString] } return NewSlice(elem) } if len(desc) > 2 && desc[0] == '[' && desc[1] != ']' { closeBrack := int32(-1) for k := int32(1); k < int32(len(desc)); k++ { if desc[k] == ']' { closeBrack = k break } } if closeBrack > 1 { n := ParseIntLocal(desc[1:closeBrack]) elem := parseTypeDesc(desc[closeBrack+1:]) return NewArray(elem, n) } } if len(desc) > 1 && desc[0] == '*' { return NewPointer(parseTypeDesc(desc[1:])) } if len(desc) > 5 && desc[:5] == "func(" && desc[len(desc)-1] == ')' { return parseSignatureDesc(desc[5 : len(desc)-1]) } if len(desc) > 5 && desc[:5] == "chan:" { return NewTCChan(TCSendRecv, parseTypeDesc(desc[5:])) } if len(desc) > 7 && desc[:7] == "chan<-:" { return NewTCChan(TCSendOnly, parseTypeDesc(desc[7:])) } if len(desc) > 7 && desc[:7] == "<-chan:" { return NewTCChan(TCRecvOnly, parseTypeDesc(desc[7:])) } if len(desc) > 4 && desc[:4] == "map[" { depth := int32(0) closeBrack := int32(-1) for k := int32(4); k < int32(len(desc)); k++ { if desc[k] == '[' { depth++ } else if desc[k] == ']' { if depth == 0 { closeBrack = k break } depth-- } } if closeBrack > 4 { key := parseTypeDesc(desc[4:closeBrack]) val := parseTypeDesc(desc[closeBrack+1:]) return NewTCMap(key, val) } } dotIdx := int32(-1) for i := int32(len(desc)) - 1; i >= 0; i-- { if desc[i] == '.' { dotIdx = i break } } if dotIdx > 0 { pkgPath := desc[:dotIdx] typeName := desc[dotIdx+1:] if extPkg, ok := cctx.universe.Registry[pkgPath]; ok { obj := extPkg.Scope.Lookup(typeName) if tn, _ := obj.(*TypeName); tn != nil && tn.Typ != nil { return tn.Typ } } } if cctx.parseTypePkgScope != nil { obj := cctx.parseTypePkgScope.Lookup(desc) if tn, ok := obj.(*TypeName); ok && tn.Typ != nil { return tn.Typ } } // Universe-scope named types (uintptr, byte, rune) appear in mxh descs by // name; without this lookup they degrade to int32 and receiver widths in // cached packages diverge from the source-compiled layout. if Universe != nil { obj := Universe.Lookup(desc) if tn, ok := obj.(*TypeName); ok && tn.Typ != nil { return tn.Typ } } return Typ[Int32] } func parseSignatureDesc(desc string) (s *Signature) { // format: "param1,param2->result1,result2" or "param1,param2" (void) or "->result" (no params) // The arrow and commas are matched at depth 0 only: nested func/struct // descs carry their own arrows and commas inside brackets. arrow := -1 depth := 0 for i := 0; i < len(desc)-1; i++ { switch desc[i] { case '{', '[', '(': depth++ case '}', ']', ')': depth-- case '-': if depth == 0 && desc[i+1] == '>' { arrow = i } } if arrow >= 0 { break } } var paramStr, resultStr string if arrow >= 0 { paramStr = desc[:arrow] resultStr = desc[arrow+2:] } else { paramStr = desc } variadic := false var params []*TCVar if paramStr != "" { parts := splitCommaBalanced(paramStr) params = []*TCVar{:0:len(parts)} for pi, p := range parts { if pi == len(parts)-1 && len(p) > 3 && p[:3] == "..." { variadic = true push(params, NewTCVar(nil, "", NewSlice(parseTypeDesc(p[3:])))) continue } push(params, NewTCVar(nil, "", parseTypeDesc(p))) } } var results []*TCVar if resultStr != "" { parts := splitCommaBalanced(resultStr) results = []*TCVar{:0:len(parts)} for _, r := range parts { push(results, NewTCVar(nil, "", parseTypeDesc(r))) } } var pTuple, rTuple *Tuple if len(params) > 0 { pTuple = NewTuple(params...) } if len(results) > 0 { rTuple = NewTuple(results...) } return NewSignature(nil, pTuple, rTuple, variadic) } func splitCommaBalanced(s string) (ss []string) { // Count commas at depth 0. n := int32(1) depth := int32(0) for i := int32(0); i < len(s); i++ { switch s[i] { case '{', '[', '(': depth++ case '}', ']', ')': depth-- case ',': if depth == 0 { n++ } } } parts := []string{:0:n} start := int32(0) depth = 0 for i := int32(0); i < len(s); i++ { switch s[i] { case '{', '[', '(': depth++ case '}', ']', ')': depth-- case ',': if depth == 0 { push(parts, s[start:i]) start = i + 1 } } } push(parts, s[start:]) return parts } func splitComma(s string) (ss []string) { n := int32(1) for i := int32(0); i < len(s); i++ { if s[i] == ',' { n++ } } parts := []string{:0:n} start := int32(0) for i := int32(0); i < len(s); i++ { if s[i] == ',' { push(parts, s[start:i]) start = i + 1 } } push(parts, s[start:]) return parts }