1/* 2 * gomacro - A Go interpreter with Lisp-like macros 3 * 4 * Copyright (C) 2017-2019 Massimiliano Ghilardi 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * 11 * interface.go 12 * 13 * Created on: Mar 29, 2017 14 * Author: Massimiliano Ghilardi 15 */ 16 17package fast 18 19import ( 20 "fmt" 21 "go/ast" 22 r "reflect" 23 24 "github.com/cosmos72/gomacro/base/reflect" 25 26 "github.com/cosmos72/gomacro/base" 27 xr "github.com/cosmos72/gomacro/xreflect" 28) 29 30// compile an interface definition 31func (c *Comp) TypeInterface(node *ast.InterfaceType) xr.Type { 32 if node.Methods == nil || len(node.Methods.List) == 0 { 33 return c.TypeOfInterface() 34 } 35 types, names := c.TypeFields(node.Methods) 36 37 // parser returns embedded interfaces as unnamed fields 38 var methodnames []string 39 var methodtypes, embeddedtypes []xr.Type 40 for i, typ := range types { 41 if i < len(names) && len(names[i]) != 0 { 42 methodnames = append(methodnames, names[i]) 43 methodtypes = append(methodtypes, typ) 44 } else { 45 embeddedtypes = append(embeddedtypes, typ) 46 } 47 } 48 universe := c.Universe 49 pkg := universe.LoadPackage(c.FileComp().Path) 50 return universe.InterfaceOf(pkg, methodnames, methodtypes, embeddedtypes) 51} 52 53// InterfaceProxy returns the proxy struct that implements a compiled interface 54func (c *Comp) InterfaceProxy(t xr.Type) r.Type { 55 ret := c.interf2proxy[t.ReflectType()] 56 if ret == nil { 57 c.Errorf("internal error: proxy not found for %s type <%v>", t.Kind(), t) 58 } 59 return ret 60} 61 62// converterToProxy compiles a conversion from 'tin' into a proxy struct that implements the interface type 'tout' 63// and returns a function that performs such conversion 64func (c *Comp) converterToProxy(tin xr.Type, tout xr.Type) func(val r.Value) r.Value { 65 rtout := tout.ReflectType() // a compiled interface 66 rtproxy := c.InterfaceProxy(tout) // one of our proxies that pre-implement the compiled interface 67 68 vtable := r.New(rtproxy).Elem() 69 n := rtout.NumMethod() 70 for i := 0; i < n; i++ { 71 mtdout := rtout.Method(i) 72 mtdin, count := tin.MethodByName(mtdout.Name, mtdout.PkgPath) 73 if count == 0 { 74 c.Errorf("cannot convert type <%v> to interface <%v>: missing method %s %s", tin, rtout, mtdout.PkgPath, mtdout.Name) 75 } else if count > 1 { 76 c.Errorf("type <%v> has %d wrapper methods %s %s all at the same depth=%d - cannot convert to interface <%v>", 77 tin, count, mtdout.PkgPath, mtdout.Name, len(mtdin.FieldIndex), tout) 78 } 79 e := c.compileMethodAsFunc(tin, mtdin) 80 setProxyField(vtable.Field(i+1), r.ValueOf(e.Value)) 81 } 82 extractor := c.extractor(tin) 83 if extractor == nil { 84 return func(val r.Value) r.Value { 85 vaddr := r.New(rtproxy) 86 vproxy := vaddr.Elem() 87 vproxy.Set(vtable) 88 vproxy.Field(0).Set(r.ValueOf(xr.MakeInterfaceHeader(val, tin))) 89 return convert(vaddr, rtout) 90 } 91 } 92 // extract object from tin proxy or emulated interface (if any), 93 // and wrap it in tout proxy 94 return func(val r.Value) r.Value { 95 v, t := extractor(val) 96 vaddr := r.New(rtproxy) 97 vproxy := vaddr.Elem() 98 vproxy.Set(vtable) 99 vproxy.Field(0).Set(r.ValueOf(xr.MakeInterfaceHeader(v, t))) 100 return convert(vaddr, rtout) 101 } 102} 103 104func setProxyField(place r.Value, mtd r.Value) { 105 rtin := mtd.Type() 106 rtout := place.Type() 107 if rtin == rtout { 108 place.Set(mtd) 109 } else if rtin.ConvertibleTo(rtout) { 110 place.Set(mtd.Convert(rtout)) 111 } else { 112 place.Set(r.MakeFunc(rtout, func(args []r.Value) []r.Value { 113 args[0] = args[0].Interface().(xr.InterfaceHeader).Value() 114 return mtd.Call(args) 115 })) 116 } 117} 118 119// extract a value from a proxy struct (one of the imports.* structs) that implements an interface 120// this is the inverse of the function returned by Comp.converterToProxy() above 121func (g *CompGlobals) extractFromProxy(v r.Value) (r.Value, xr.Type) { 122 // base.Debugf("type assertion: value = %v <%v>", v, base.Type(v)) 123 124 // v.Kind() is allowed also on invalid r.Value, and it returns r.Invalid 125 if v.Kind() == r.Interface { 126 v = v.Elem() // extract concrete type 127 } 128 if !v.IsValid() || v == base.None { 129 // cannot extract concrete type 130 return v, nil 131 } 132 rt := v.Type() 133 var xt xr.Type 134 // base.Debugf("type assertion: concrete value = %v <%v>", i, t) 135 if rt != nil && rt.Kind() == r.Ptr && g.proxy2interf[rt.Elem()] != nil { 136 v = v.Elem().Field(0) 137 if j, ok := reflect.Interface(v).(xr.InterfaceHeader); ok { 138 // base.Debugf("type assertion: unwrapped value = %v <%T>", j, j) 139 v = j.Value() 140 xt = j.Type() 141 } else { 142 // base.Debugf("type assertion: failed to unwrap value = %v <%T>", i, i) 143 if v.Kind() == r.Interface { 144 v = v.Elem() // extract concrete type 145 } 146 } 147 } 148 return v, xt 149} 150 151// converterToProxy compiles a conversion from 'tin' into the emulated interface type 'tout' 152// and returns a function that performs such conversion 153func (c *Comp) converterToEmulatedInterface(tin, tout xr.Type) func(val r.Value) r.Value { 154 if !tin.Implements(tout) { 155 c.Errorf("cannot convert from <%v> to <%v>", tin, tout) 156 } 157 n := tout.NumMethod() 158 obj2methodFuncs := make([]func(r.Value) r.Value, n) 159 160 tsrc := tin 161 if tin.Kind() == r.Ptr { 162 // xr.Type.MethodByName wants T, not *T, even for methods with pointer receiver 163 tsrc = tin.Elem() 164 } 165 debug := c.Options&base.OptDebugMethod != 0 166 for i := 0; i < n; i++ { 167 mtdout := tout.Method(i) 168 mtdin, count := tsrc.MethodByName(mtdout.Name, c.PackagePath) // pkgpath is ignored for exported names 169 170 if count == 0 { 171 c.Errorf("cannot convert from <%v> to <%v>: missing method %s %s", tin, tout, mtdout.Name, mtdout.Type) 172 } else if count > 1 { 173 c.Errorf("cannot convert from <%v> to <%v>: multiple methods match %s %s", tin, tout, mtdout.Name, mtdout.Type) 174 } 175 if !mtdin.Type.AssignableTo(mtdout.Type) { 176 c.Errorf("cannot convert from <%v> to <%v>: mismatched method %s: expecting %v, found %v", 177 tin, tout, mtdout.Name, mtdout.Type, mtdin.Type) 178 } 179 obj2methodFuncs[i] = c.compileObjGetMethod(tin, mtdin) 180 if debug { 181 c.Debugf("compiled method conversion from %v.%s <%v> (concrete method %d) to %v.%s <%v> (interface method %d)", 182 tin, mtdin.Name, mtdin.Type, mtdin.Index, tout, mtdout.Name, mtdout.Type, mtdout.Index) 183 } 184 } 185 rtout := tout.ReflectType() 186 187 extractor := c.extractor(tin) 188 if extractor == nil { 189 return func(obj r.Value) r.Value { 190 return xr.ToEmulatedInterface(rtout, obj, tin, obj2methodFuncs) 191 } 192 } 193 // extract object from tin proxy or emulated interface (if any), 194 // and wrap it in tout emulated interface 195 return func(obj r.Value) r.Value { 196 v, t := extractor(obj) 197 return xr.ToEmulatedInterface(rtout, v, t, obj2methodFuncs) 198 } 199} 200 201// return a function that extracts value wrapped in a proxy or emulated interface 202// returns nil if no extraction is needed 203func (g *CompGlobals) extractor(tin xr.Type) func(r.Value) (r.Value, xr.Type) { 204 if tin.Kind() != r.Interface { 205 return nil 206 } else if xr.IsEmulatedInterface(tin) { 207 return xr.FromEmulatedInterface 208 } else { 209 return g.extractFromProxy 210 } 211} 212 213// return the error "\n\treason: t does not implement tinterf: missing method <method>" 214func interfaceMissingMethod(t, tinterf xr.Type) string { 215 var s string 216 if tinterf.Kind() == r.Interface { 217 s = fmt.Sprintf("\n\treason: %v does not implement %v", t, tinterf) 218 missingmtd := xr.MissingMethod(t, tinterf) 219 if missingmtd != nil { 220 s = fmt.Sprintf("%s: missing method %s", s, missingmtd.String()) 221 } 222 } 223 return s 224} 225