package ssa import ( "git.smesh.lol/moxie/pkg/token" "git.smesh.lol/moxie/pkg/syntax" "git.smesh.lol/moxie/pkg/types" ) func (fb *ssaFuncBuilder) resolveMethodCallWithRecv(sel *syntax.SelectorExpr, recv SSAValue) (*SSAFunction, SSAValue) { typ := recv.SSAType() if typ == nil { return nil, nil } isPtr := false if p, ok := types.SafeUnderlying(typ).(*types.Pointer); ok { typ = p.Elem() isPtr = true } if _, ok := types.SafeUnderlying(typ).(*types.TCInterface); ok { if named2, ok2 := typ.(*types.Named); !ok2 || named2.NumMethods() == 0 { return nil, nil } } // Handle Basic types (builtin stringers in runtime package) if basic, ok2 := typ.(*types.Basic); ok2 { typeName := basic.Name() mangledName := typeName | "." | sel.Sel.Value extSym := "runtime." | mangledName for i := int32(0); i < basic.NumMethods(); i++ { m := basic.Method(i) if m.Name() == sel.Sel.Value { fn := fb.fn.Pkg.ExternalFunc(mangledName, m.Signature()) fn.externalSymbol = extSym return fn, recv } } resolved := types.UntypedToTyped(basic) if resolved == nil { return nil, nil } switch rt := resolved.(type) { case *types.Basic: typeName = rt.Name() mangledName = typeName | "." | sel.Sel.Value extSym = "runtime." | mangledName for i := int32(0); i < rt.NumMethods(); i++ { m := rt.Method(i) if m.Name() == sel.Sel.Value { fn := fb.fn.Pkg.ExternalFunc(mangledName, m.Signature()) fn.externalSymbol = extSym return fn, recv } } return nil, nil case *types.Named: typ = rt default: return nil, nil } } named, ok := typ.(*types.Named) if !ok { return nil, nil } typeName := "" if named.Obj() != nil { typeName = named.Obj().Name() } if typeName == "" { return nil, nil } resolveNamed := named if resolveNamed.NumMethods() == 0 { cur := types.Type(resolveNamed) for depth := 0; depth < 10; depth++ { n, ok2 := cur.(*types.Named) if !ok2 || n == nil { break } if n.NumMethods() > 0 { resolveNamed = n if n.Obj() != nil && n.Obj().Name() != "" { typeName = n.Obj().Name() } break } raw := n.RawUnderlying() if raw == nil || raw == cur { break } cur = raw } } namedPkgPath := "" if resolveNamed.Obj() != nil && resolveNamed.Obj().Pkg() != nil { namedPkgPath = resolveNamed.Obj().Pkg().Path() } samePackage := namedPkgPath != "" && namedPkgPath == fb.fn.Pkg.Pkg.Path() mangledName := typeName | "." | sel.Sel.Value if !samePackage && namedPkgPath != "" { mangledName = namedPkgPath | "." | typeName | "." | sel.Sel.Value } var fn *SSAFunction if samePackage { fn, _ = fb.fn.Pkg.Members[mangledName].(*SSAFunction) } if fn == nil { for i := 0; i < resolveNamed.NumMethods(); i++ { m := resolveNamed.Method(i) if m.Name() == sel.Sel.Value { fn = fb.fn.Pkg.ExternalFunc(mangledName, m.Signature()) if !samePackage && namedPkgPath != "" { fn.externalSymbol = namedPkgPath | "." | typeName | "." | sel.Sel.Value } else if resolveNamed.Obj() != nil && resolveNamed.Obj().Pkg() == nil { fn.externalSymbol = "runtime." | mangledName } break } } } if fn != nil { if fn.Signature != nil && fn.Signature.Recv() != nil { recvParam := fn.Signature.Recv() _, recvIsPtr := types.SafeUnderlying(recvParam.Type()).(*types.Pointer) if recvIsPtr && !isPtr { if nameExpr, ok2 := sel.X.(*syntax.Name); ok2 { obj := fb.lookupObject(nameExpr.Value) if obj != nil { if alloc, ok3 := fb.vars[obj]; ok3 { recv = alloc } } } else if innerSel, ok2 := sel.X.(*syntax.SelectorExpr); ok2 { addr := fb.buildSelectorAddr(innerSel) if addr != nil { recv = addr } } } } return fn, recv } st, ok2 := types.SafeUnderlying(named).(*types.TCStruct) if !ok2 { return nil, nil } if !isPtr { fixed := false if innerSel, ok3 := sel.X.(*syntax.SelectorExpr); ok3 { addr := fb.buildSelectorAddr(innerSel) if addr != nil { recv = addr fixed = true } } if !fixed { if nameExpr, ok3 := sel.X.(*syntax.Name); ok3 { obj := fb.lookupObject(nameExpr.Value) if obj != nil { if a, ok4 := fb.vars[obj]; ok4 { recv = a fixed = true } } } } if !fixed { alloc := &SSAAlloc{} alloc.typ = types.NewPointer(recv.SSAType()) alloc.name = fb.nextName() fb.emit(alloc) storeOp := &SSAStore{Addr: alloc, Val: recv} fb.emit(storeOp) recv = alloc } } for i := 0; i < st.NumFields(); i++ { f := st.Field(i) if !f.Anonymous() { continue } embedType := f.Type() embedNamed, ok3 := embedType.(*types.Named) if !ok3 { if p, ok4 := embedType.(*types.Pointer); ok4 { embedNamed, ok3 = p.Elem().(*types.Named) } } if !ok3 || embedNamed.Obj() == nil { continue } embedName := embedNamed.Obj().Name() embedSamePkg := embedNamed.Obj().Pkg() != nil && embedNamed.Obj().Pkg().Path() == fb.fn.Pkg.Pkg.Path() embedMangledName := embedName | "." | sel.Sel.Value if !embedSamePkg && embedNamed.Obj().Pkg() != nil { embedMangledName = embedNamed.Obj().Pkg().Path() | "." | embedName | "." | sel.Sel.Value } var embedFn *SSAFunction if embedSamePkg { embedFn, _ = fb.fn.Pkg.Members[embedMangledName].(*SSAFunction) } if embedFn == nil { for mi := int32(0); mi < int32(embedNamed.NumMethods()); mi++ { m := embedNamed.Method(int32(mi)) if m.Name() == sel.Sel.Value { embedFn = fb.fn.Pkg.ExternalFunc(embedMangledName, m.Signature()) if !embedSamePkg && embedNamed.Obj().Pkg() != nil { embedFn.externalSymbol = embedNamed.Obj().Pkg().Path() | "." | embedName | "." | sel.Sel.Value } break } } } if embedFn != nil { fieldAddr := &SSAFieldAddr{ X: recv, Field: i, } fieldAddr.typ = types.NewPointer(embedType) fieldAddr.name = fb.nextName() fb.emit(fieldAddr) var embedRecv SSAValue = fieldAddr if _, isEmbedPtr := types.SafeUnderlying(embedType).(*types.Pointer); isEmbedPtr { ld := &SSAUnOp{Op: OpMul, X: fieldAddr} ld.typ = embedType ld.name = fb.nextName() fb.emit(ld) embedRecv = ld } return embedFn, embedRecv } embedSt, ok5 := types.SafeUnderlying(embedNamed).(*types.TCStruct) if !ok5 { continue } for j := 0; j < embedSt.NumFields(); j++ { f2 := embedSt.Field(j) if !f2.Anonymous() { continue } embed2Type := f2.Type() embed2Named, ok6 := embed2Type.(*types.Named) if !ok6 { if p2, ok7 := embed2Type.(*types.Pointer); ok7 { embed2Named, ok6 = p2.Elem().(*types.Named) } } if !ok6 || embed2Named.Obj() == nil { continue } embed2Name := embed2Named.Obj().Name() embed2SamePkg := embed2Named.Obj().Pkg() != nil && embed2Named.Obj().Pkg().Path() == fb.fn.Pkg.Pkg.Path() embed2Mangled := embed2Name | "." | sel.Sel.Value if !embed2SamePkg && embed2Named.Obj().Pkg() != nil { embed2Mangled = embed2Named.Obj().Pkg().Path() | "." | embed2Name | "." | sel.Sel.Value } var embed2Fn *SSAFunction if embed2SamePkg { embed2Fn, _ = fb.fn.Pkg.Members[embed2Mangled].(*SSAFunction) } if embed2Fn == nil { for mi := int32(0); mi < int32(embed2Named.NumMethods()); mi++ { m := embed2Named.Method(int32(mi)) if m.Name() == sel.Sel.Value { embed2Fn = fb.fn.Pkg.ExternalFunc(embed2Mangled, m.Signature()) if !embed2SamePkg && embed2Named.Obj().Pkg() != nil { embed2Fn.externalSymbol = embed2Named.Obj().Pkg().Path() | "." | embed2Name | "." | sel.Sel.Value } break } } } if embed2Fn == nil { continue } fieldAddr1 := &SSAFieldAddr{X: recv, Field: i} fieldAddr1.typ = types.NewPointer(embedType) fieldAddr1.name = fb.nextName() fb.emit(fieldAddr1) var cur1 SSAValue = fieldAddr1 curEmbedType := embedType if p5, ok8 := types.SafeUnderlying(embedType).(*types.Pointer); ok8 { ld := &SSAUnOp{Op: OpMul, X: fieldAddr1} ld.typ = embedType ld.name = fb.nextName() fb.emit(ld) cur1 = ld curEmbedType = p5.Elem() } _ = curEmbedType fieldAddr2 := &SSAFieldAddr{X: cur1, Field: j} fieldAddr2.typ = types.NewPointer(embed2Type) fieldAddr2.name = fb.nextName() fb.emit(fieldAddr2) var embedRecv2 SSAValue = fieldAddr2 if _, isEmbedPtr2 := types.SafeUnderlying(embed2Type).(*types.Pointer); isEmbedPtr2 { ld2 := &SSAUnOp{Op: OpMul, X: fieldAddr2} ld2.typ = embed2Type ld2.name = fb.nextName() fb.emit(ld2) embedRecv2 = ld2 } return embed2Fn, embedRecv2 } } return nil, nil } func (fb *ssaFuncBuilder) buildIfaceMethodCall(sel *syntax.SelectorExpr, recv SSAValue, argExprs []syntax.Expr) (s SSAValue) { typ := recv.SSAType() if p, ok := types.SafeUnderlying(typ).(*types.Pointer); ok { typ = p.Elem() } iface, ok := types.SafeUnderlying(typ).(*types.TCInterface) if !ok { return nil } var methodSig *types.Signature for i := 0; i < iface.NumMethods(); i++ { m := iface.Method(i) if m == nil { continue } if m.Name() == sel.Sel.Value { methodSig = m.Sig() break } } var retType types.Type if methodSig != nil && methodSig.Results() != nil { if methodSig.Results().Len() == 1 { retType = methodSig.Results().At(0).Type() } else if methodSig.Results().Len() > 1 { retType = methodSig.Results() } } args := fb.buildArgs(argExprs) inv := &SSAInvoke{ X: recv, MethodName: sel.Sel.Value, IfaceType: iface, Args: args, } inv.typ = retType inv.name = fb.nextName() fb.emit(inv) if retType == nil { return nil } return inv } func (fb *ssaFuncBuilder) resolveType(e syntax.Expr) (t types.Type) { if fb.info != nil { if tv, ok := fb.info.Types[e]; ok && tv.Type != nil { return tv.Type } } t = fb.resolveTypeAST(e) return t } func (fb *ssaFuncBuilder) resolveTypeAST(e syntax.Expr) (t types.Type) { if e == nil { return nil } switch e := e.(type) { case *syntax.Name: if fb.typeSubst != nil { if t, ok := fb.typeSubst[e.Value]; ok { return t } } if t, ok := fb.localTypes[e.Value]; ok { return t } obj := fb.lookupObject(e.Value) if tn, ok := obj.(*types.TypeName); ok { if tp, ok2 := tn.Type().(*types.TypeParam); ok2 && fb.typeSubst != nil { if t, ok3 := fb.typeSubst[tp.String()]; ok3 { return t } } return tn.Type() } case *syntax.Operation: if e.Y == nil && e.Op == token.Mul { base := fb.resolveTypeAST(e.X) if base != nil { return types.NewPointer(base) } } case *syntax.SliceType: elem := fb.resolveTypeAST(e.Elem) if elem != nil { if b, ok := elem.(*types.Basic); ok && b.Kind() == types.Uint8 { return types.Typ[types.TCString] } return types.NewSlice(elem) } case *syntax.ArrayType: elem := fb.resolveTypeAST(e.Elem) if elem != nil { n := int64(-1) if lit, ok := e.Len.(*syntax.BasicLit); ok { n = ssaParseInt64(lit.Value) } else if e.Len != nil { cv := tcEvalConstExpr(e.Len, fb.fn.Pkg.Pkg.Scope(), -1) if cv == nil && fb.localConsts != nil { cv = fb.evalLocalConstExpr(e.Len) } if ci, ok := cv.(*types.ConstInt); ok { n = ci.v } } return types.NewArray(elem, n) } case *syntax.MapType: key := fb.resolveTypeAST(e.Key) val := fb.resolveTypeAST(e.Value) if key != nil && val != nil { return types.NewTCMap(key, val) } case *syntax.StructType: return fb.resolveStructTypeAST(e) case *syntax.ChanType: elem := fb.resolveTypeAST(e.Elem) if elem != nil { dir := types.TCSendRecv if e.Dir == syntax.SendOnly { dir = types.TCSendOnly } else if e.Dir == syntax.RecvOnly { dir = types.TCRecvOnly } return types.NewTCChan(dir, elem) } case *syntax.InterfaceType: return fb.resolveInterfaceTypeAST(e) case *syntax.FuncType: return fb.resolveFuncTypeAST(e) case *syntax.SelectorExpr: if x, ok := e.X.(*syntax.Name); ok { obj := fb.lookupObject(x.Value) if pn, ok2 := obj.(*types.PkgName); ok2 && pn.Imported() != nil { sel := pn.Imported().Scope().Lookup(e.Sel.Value) if tn, ok3 := sel.(*types.TypeName); ok3 { return tn.Type() } } } case *syntax.ParenExpr: return fb.resolveTypeAST(e.X) } return nil } func (fb *ssaFuncBuilder) resolveInterfaceTypeAST(e *syntax.InterfaceType) (t *types.TCInterface) { var methods []*types.IfaceMethod for _, f := range e.MethodList { if f.Name == nil { continue } ft, ok := f.Type.(*syntax.FuncType) if !ok { continue } sig := fb.resolveFuncTypeAST(ft) if sig != nil { methods = append(methods, types.NewTCIfaceMethod(f.Name.Value, sig)) } } iface := types.NewTCInterface(methods, nil) iface.Complete() return iface } func (fb *ssaFuncBuilder) resolveStructTypeAST(e *syntax.StructType) (t *types.TCStruct) { var fields []*types.TCVar var tags []string for i, f := range e.FieldList { typ := fb.resolveTypeAST(f.Type) if typ == nil { return nil } name := "" if f.Name != nil { name = f.Name.Value } fields = append(fields, types.NewTCField(fb.fn.Pkg.Pkg, name, typ, f.Name == nil)) tag := "" if i < len(e.TagList) && e.TagList[i] != nil { tag = e.TagList[i].Value } tags = append(tags, tag) } return types.NewTCStruct(fields, tags) } func (fb *ssaFuncBuilder) resolveFuncTypeAST(ft *syntax.FuncType) (s *types.Signature) { if ft == nil { return nil } var params []*types.TCVar for _, p := range ft.ParamList { typ := fb.resolveTypeAST(p.Type) pname := "" if p.Name != nil { pname = p.Name.Value } params = append(params, types.NewTCVar(nil, pname, typ)) } var results []*types.TCVar for _, r := range ft.ResultList { typ := fb.resolveTypeAST(r.Type) rname := "" if r.Name != nil { rname = r.Name.Value } results = append(results, types.NewTCVar(nil, rname, typ)) } var pTuple, rTuple *types.Tuple if len(params) > 0 { pTuple = types.NewTuple(params...) } if len(results) > 0 { rTuple = types.NewTuple(results...) } return types.NewSignature(nil, pTuple, rTuple, false) } func (fb *ssaFuncBuilder) fieldIndex(t types.Type, name string) (n int32) { if t == nil { return -1 } if pt, ok := types.SafeUnderlying(t).(*types.Pointer); ok { t = pt.Elem() } if st, ok := types.SafeUnderlying(t).(*types.TCStruct); ok { for i := 0; i < st.NumFields(); i++ { if st.Field(i).Name() == name { return i } } } return -1 } func (fb *ssaFuncBuilder) findEmbeddedField(t types.Type, name string) (embedIdx int32, innerIdx int32) { path := fb.findEmbeddedFieldPath(t, name) if len(path) < 2 { return -1, -1 } return path[0], path[1] } func (fb *ssaFuncBuilder) findEmbeddedFieldPath(t types.Type, name string) (ss []int32) { if t == nil { return nil } if pt, ok := types.SafeUnderlying(t).(*types.Pointer); ok { t = pt.Elem() } st, ok := types.SafeUnderlying(t).(*types.TCStruct) if !ok { return nil } for i := 0; i < st.NumFields(); i++ { f := st.Field(i) if !f.Anonymous() { continue } embedType := f.Type() if pt, ok := types.SafeUnderlying(embedType).(*types.Pointer); ok { embedType = pt.Elem() } inner := fb.fieldIndex(embedType, name) if inner >= 0 { return []int32{i, inner} } sub := fb.findEmbeddedFieldPath(embedType, name) if len(sub) > 0 { return append([]int32{i}, sub...) } } return nil } func (fb *ssaFuncBuilder) fieldType(t Type, idx int32) (tv types.Type) { if t == nil { return nil } if pt, ok := types.SafeUnderlying(t).(*types.Pointer); ok { t = pt.Elem() } if st, ok := types.SafeUnderlying(t).(*types.TCStruct); ok { if idx >= 0 && idx < st.NumFields() { return st.Field(idx).Type() } } return nil } func ssaExtractConst(v SSAValue) (s *SSAConst) { if c, ok := v.(*SSAConst); ok { return c } if conv, ok := v.(*SSAConvert); ok { if c, ok2 := conv.X.(*SSAConst); ok2 { return &SSAConst{typ: conv.SSAType(), val: c.val} } } return nil } func ssaExprNames(e syntax.Expr) (ns []*syntax.Name) { if e == nil { return nil } if n, ok := e.(*syntax.Name); ok { return []*syntax.Name{n} } if l, ok := e.(*syntax.ListExpr); ok { var names []*syntax.Name for _, el := range l.ElemList { if n, ok := el.(*syntax.Name); ok { names = append(names, n) } } return names } return nil } func localizeConstVal(val ConstVal, typ types.Type) (c types.ConstVal) { if val == nil { return nil } return val }