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