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 * address.go 12 * 13 * Created on Apr 05, 2017 14 * Author Massimiliano Ghilardi 15 */ 16 17package fast 18 19import ( 20 "go/ast" 21 r "reflect" 22 "unsafe" 23 24 "github.com/cosmos72/gomacro/base/output" 25 xr "github.com/cosmos72/gomacro/xreflect" 26) 27 28:import ( 29 "go/ast" 30 r "reflect" 31) 32 33:const ( 34 // conventional values 35 AnyDepth = -1 36 FileDepth = -2 37 TopDepth = -3 38) 39 40:func faddress(upn int, typ ast.Node) ast.Node { 41 // the return type of Eval() and EvalType() varies. better check early. 42 var t r.Type = EvalType(typ) 43 var decls, addresstaken, bind, rettype ast.Node 44 45 if upn == 0 { 46 decls = ~'{{ }} 47 } else if upn > 0 { 48 decls = ~'env 49 for i := 0; i < upn; i++ { 50 decls = ~"{~,decls. Outer} 51 } 52 decls = ~"{{ 53 env = ~,decls 54 }} 55 } else if upn == FileDepth { 56 decls = ~'{{env = env.FileEnv}} 57 } else if upn == TopDepth { 58 decls = ~'{{env = env.FileEnv.Outer}} 59 } else { 60 decls = ~'{ 61 env = env.Outer.Outer.Outer 62 for i := 3; i < upn; i++ { 63 env = env.Outer 64 } 65 } 66 } 67 if t == nil { 68 // env.Vals[index] actually contains the variable's address 69 // no need to set special flags like env.IntAddressTaken. 70 // that's needed instead when taking the address of env.Ints[index] 71 bind = ~'{env .Vals[index].Addr()} 72 rettype = ~'{r.Value} 73 return ~"{ 74 ret = func(env *Env) (~,rettype) { 75 ~,@decls 76 return ~,bind 77 } 78 } 79 } 80 81 addresstaken = ~"{{env.IntAddressTaken = true}} 82 rettype = ~"{* ~,typ} 83 if t.Kind() == r.Uint64 { 84 bind = ~'{&env.Ints[index]} 85 } else { 86 bind = ~"{(*~,typ)(unsafe.Pointer(&env.Ints[index]))} 87 } 88 89 return ~"{ 90 if intbinds { 91 ret = func(env *Env) (~,rettype) { 92 ~,@decls 93 ~,addresstaken 94 return ~,bind 95 } 96 } else { 97 ret = func(env *Env) (~,rettype) { 98 ~,@decls 99 return env.Vals[index].Addr().Interface().(~,rettype) 100 } 101 } 102 } 103} 104 105:macro address(depth ast.Node, typ ast.Node) ast.Node { 106 // the return type of Eval() and EvalType() varies. better check early. 107 var upn int = Eval(depth).(int) 108 109 return faddress(upn, typ) 110} 111 112:macro addresses(depth ast.Node) ast.Node { 113 return ~"{ 114 switch k { 115 case r.Bool: address; ~,depth; bool 116 case r.Int: address; ~,depth; int 117 case r.Int8: address; ~,depth; int8 118 case r.Int16: address; ~,depth; int16 119 case r.Int32: address; ~,depth; int32 120 case r.Int64: address; ~,depth; int64 121 case r.Uint: address; ~,depth; uint 122 case r.Uint8: address; ~,depth; uint8 123 case r.Uint16: address; ~,depth; uint16 124 case r.Uint32: address; ~,depth; uint32 125 case r.Uint64: address; ~,depth; uint64 126 case r.Uintptr: address; ~,depth; uintptr 127 case r.Float32: address; ~,depth; float32 128 case r.Float64: address; ~,depth; float64 129 case r.Complex64: address; ~,depth; complex64 130 case r.Complex128: address; ~,depth; complex128 131 default: address; ~,depth; nil 132 } 133 } 134} 135 136func (c *Comp) AddressOf(node *ast.UnaryExpr) *Expr { 137 return c.addressOf(node.X, nil) 138} 139 140func (c *Comp) addressOf(expr ast.Expr, t xr.Type) *Expr { 141 for { 142 switch e := expr.(type) { 143 case *ast.ParenExpr: 144 expr = e.X 145 continue 146 case *ast.StarExpr: 147 // optimize & * x -> x, but check that x is a pointer 148 if t != nil { 149 t = t.Elem() 150 } 151 ret := c.Expr1(e.X, t) 152 if ret.Type.Kind() != r.Ptr { 153 c.Errorf("unary operation * on non-pointer <%v>: %v", ret.Type, e) 154 } 155 } 156 break 157 } 158 place := c.placeOrAddress(expr, PlaceAddress, t) 159 // c.Debugf("AddressOf: place %v has type %v, taking its address", expr, place.Type) 160 if place.IsVar() { 161 va := place.Var // make a copy of place.Var, do not alter the original's type 162 return va.Address(c.Depth) 163 } else if place.Addr == nil { 164 c.Errorf("cannot take the address of %v <%v>", expr, place.Type) 165 return nil 166 } else { 167 // placeOrAddress returns the dereferenced type... fix it 168 t := c.Universe.PtrTo(place.Type) 169 return exprX1(t, place.Addr) 170 } 171} 172 173func (c *Comp) AddressOfVar(name string) *Expr { 174 sym := c.Resolve(name) 175 va := sym.AsVar(PlaceAddress) 176 return va.Address(c.Depth) 177} 178 179func (va *Var) Address(maxdepth int) *Expr { 180 upn := va.Upn 181 k := va.Type.Kind() 182 index := va.Desc.Index() 183 if index == NoIndex { 184 output.Errorf("cannot take the address of %s: _", va.Desc.Class()) 185 return nil 186 } 187 var ret I 188 intbinds := va.Desc.Class() == IntBind 189 switch upn { 190 case 0: addresses; 0 191 case 1: addresses; 1 192 case 2: addresses; 2 193 default: addresses; -1 194 case maxdepth-1: addresses; -2 195 case maxdepth: addresses; -3 196 } 197 u := va.Type.Universe() 198 return &Expr{Lit: Lit{Type: u.PtrTo(va.Type)}, Fun: ret} 199} 200