Compare commits

...

15 Commits

Author SHA1 Message Date
Marc Vertes
24b5375636 interp: fix memory handling of global values
In some cases, the global character of a value was lost, leading to
undefined behaviour. Now a node level field of -1 means that the value
is global, and that it should be accessed from the root data frame.

Fixes #993.
2021-01-05 17:28:03 +01:00
Marc Vertes
a83f492309 interp: add support for binary composite slice
Fixes #987.
2020-12-15 18:20:04 +01:00
mpl
02c30482cc interp: enable type assertion from empty interface into slice
Fixes #985
2020-12-15 17:28:04 +01:00
Marc Vertes
9e1da978b0 interp: fix handling interface types in wrapped functions
The interpreter interface type was replaced by a reflect.Value in
objects passed or return to function wrappers, losing the ability
to retrieve methods.

The valueInterface is now preserved, and correctly accessed if
wrapped multiple times.

Fixes #977.
2020-12-15 16:14:05 +01:00
mpl
662838fd80 interp: fix and refactor typeAssertStatus in
typeAssertStatus deals with the 3rd form of type assertion ("_, ok"), for
when one does not care about the result of the assertion itself.
Some cases for it, which are already fixed for the two other forms of
type assertions, had not been fixed for this form yet.

Therefore, this change fixes such cases for this form, while integrating
typeAssertStatus to the same code path as for the two other forms.
2020-12-07 15:58:04 +01:00
Marc Vertes
92d65c22f0 interp: remove incorrect type check on array object
The type check was generating false negatives. A correct test to check the
adressable status of an array is more complex to implement, and will
be done later.

Fixes #973.
2020-12-02 17:26:03 +01:00
mpl
2db4579b6f interp: fix short-form type assertions
The long-form (with comma-ok) ones were already fixed but the short-form
ones were not because they were in a completely different code path.

This PR also refactors the code so that both short-form and long-form
are now merged in the same function.

N.B: even though most (all?) cases seem to now be supported, one of them
still yields a result that does not satisfy reflect's Implements method
yet. It does not prevent the resulting assertion to be usable though.

N.B2: the code path for the third-form (_, ok) hasn't been fixed and/or
refactored yet.

Fixes #919
2020-12-02 14:46:03 +01:00
mpl
101633c380 interp: support two more type assertion cases
Fixes #967
2020-12-01 15:50:04 +01:00
mpl
1e0f6ece6e interp: support more type assertion cases
Fixes #955
2020-11-30 18:00:04 +01:00
Marc Vertes
662d2a6afe interp: fix parsing of assign to dereferenced pointer
Fixes #969.
2020-11-30 17:46:05 +01:00
Marc Vertes
b25ee3f809 interp: fix method lookup on aliased types
In aliased type declarations, when the target type was imported from
an external package rather than declared locally, the aliased type was
overwritten by target, loosing ability to lookup methods on the aliased
type. Aliasing on imported types is now properly detected and handled.

Fixes #971.
2020-11-30 15:44:04 +01:00
Marc Vertes
81e1e5f206 interp: handle getting address of interpreter interface value
Fixes #963.
2020-11-30 11:48:04 +01:00
Marc Vertes
81d8339132 test: chdir to package directory prior to launch tests
Because this is what `go test` does, and some packages depend on that,
for example `github.com/jjcollinge/servicefabric`.
2020-11-25 16:10:04 +01:00
Marc Vertes
d494f9e420 interp: support calling goto from sub-scope
As opposed to other symbols, goto labels must be searched in included
scopes, not upper ones. Implement scope.lookdown to perform this,
to allow calls to goto to be embedded in included scopes where label
is defined.

Fixes #953.
2020-11-19 12:48:03 +01:00
Marc Vertes
6da1107c39 fix: do not confuse function call with type conversion
Use node action to better discriminate between function call and type
conversion which have the same pattern at AST level.

Fixes #960.
2020-11-18 14:56:05 +01:00
21 changed files with 925 additions and 211 deletions

29
_test/addr2.go Normal file
View File

@@ -0,0 +1,29 @@
package main
import (
"encoding/xml"
"fmt"
)
type Email struct {
Where string `xml:"where,attr"`
Addr string
}
func f(s string, r interface{}) error {
return xml.Unmarshal([]byte(s), &r)
}
func main() {
data := `
<Email where='work'>
<Addr>bob@work.com</Addr>
</Email>
`
v := Email{}
err := f(data, &v)
fmt.Println(err, v)
}
// Ouput:
// <nil> {work bob@work.com}

125
_test/assert0.go Normal file
View File

@@ -0,0 +1,125 @@
package main
import (
"fmt"
"reflect"
"time"
)
type MyWriter interface {
Write(p []byte) (i int, err error)
}
type TestStruct struct{}
func (t TestStruct) Write(p []byte) (n int, err error) {
return len(p), nil
}
func usesWriter(w MyWriter) {
n, _ := w.Write([]byte("hello world"))
fmt.Println(n)
}
type MyStringer interface {
String() string
}
func usesStringer(s MyStringer) {
fmt.Println(s.String())
}
func main() {
aType := reflect.TypeOf((*MyWriter)(nil)).Elem()
var t interface{}
t = TestStruct{}
var tw MyWriter
var ok bool
tw, ok = t.(MyWriter)
if !ok {
fmt.Println("TestStruct does not implement MyWriter")
} else {
fmt.Println("TestStruct implements MyWriter")
usesWriter(tw)
}
n, _ := t.(MyWriter).Write([]byte("hello world"))
fmt.Println(n)
bType := reflect.TypeOf(TestStruct{})
fmt.Println(bType.Implements(aType))
// not redundant with the above, because it goes through a slightly different code path.
if _, ok := t.(MyWriter); !ok {
fmt.Println("TestStruct does not implement MyWriter")
return
} else {
fmt.Println("TestStruct implements MyWriter")
}
t = 42
foo, ok := t.(MyWriter)
if !ok {
fmt.Println("42 does not implement MyWriter")
} else {
fmt.Println("42 implements MyWriter")
}
_ = foo
if _, ok := t.(MyWriter); !ok {
fmt.Println("42 does not implement MyWriter")
} else {
fmt.Println("42 implements MyWriter")
}
var tt interface{}
tt = time.Nanosecond
var myD MyStringer
myD, ok = tt.(MyStringer)
if !ok {
fmt.Println("time.Nanosecond does not implement MyStringer")
} else {
fmt.Println("time.Nanosecond implements MyStringer")
usesStringer(myD)
}
fmt.Println(tt.(MyStringer).String())
cType := reflect.TypeOf((*MyStringer)(nil)).Elem()
dType := reflect.TypeOf(time.Nanosecond)
fmt.Println(dType.Implements(cType))
if _, ok := tt.(MyStringer); !ok {
fmt.Println("time.Nanosecond does not implement MyStringer")
} else {
fmt.Println("time.Nanosecond implements MyStringer")
}
tt = 42
bar, ok := tt.(MyStringer)
if !ok {
fmt.Println("42 does not implement MyStringer")
} else {
fmt.Println("42 implements MyStringer")
}
_ = bar
if _, ok := tt.(MyStringer); !ok {
fmt.Println("42 does not implement MyStringer")
} else {
fmt.Println("42 implements MyStringer")
}
}
// Output:
// TestStruct implements MyWriter
// 11
// 11
// true
// TestStruct implements MyWriter
// 42 does not implement MyWriter
// 42 does not implement MyWriter
// time.Nanosecond implements MyStringer
// 1ns
// 1ns
// true
// time.Nanosecond implements MyStringer
// 42 does not implement MyStringer
// 42 does not implement MyStringer

85
_test/assert1.go Normal file
View File

@@ -0,0 +1,85 @@
package main
import (
"fmt"
"reflect"
"time"
)
type TestStruct struct{}
func (t TestStruct) String() string {
return "hello world"
}
func main() {
aType := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
var t interface{}
t = time.Nanosecond
s, ok := t.(fmt.Stringer)
if !ok {
fmt.Println("time.Nanosecond does not implement fmt.Stringer")
return
}
fmt.Println(s.String())
fmt.Println(t.(fmt.Stringer).String())
bType := reflect.TypeOf(time.Nanosecond)
fmt.Println(bType.Implements(aType))
// not redundant with the above, because it goes through a slightly different code path.
if _, ok := t.(fmt.Stringer); !ok {
fmt.Println("time.Nanosecond does not implement fmt.Stringer")
return
} else {
fmt.Println("time.Nanosecond implements fmt.Stringer")
}
t = 42
foo, ok := t.(fmt.Stringer)
if !ok {
fmt.Println("42 does not implement fmt.Stringer")
} else {
fmt.Println("42 implements fmt.Stringer")
return
}
_ = foo
if _, ok := t.(fmt.Stringer); !ok {
fmt.Println("42 does not implement fmt.Stringer")
} else {
fmt.Println("42 implements fmt.Stringer")
return
}
var tt interface{}
tt = TestStruct{}
ss, ok := tt.(fmt.Stringer)
if !ok {
fmt.Println("TestStuct does not implement fmt.Stringer")
return
}
fmt.Println(ss.String())
fmt.Println(tt.(fmt.Stringer).String())
// TODO(mpl): uncomment when fixed
// cType := reflect.TypeOf(TestStruct{})
// fmt.Println(cType.Implements(aType))
if _, ok := tt.(fmt.Stringer); !ok {
fmt.Println("TestStuct does not implement fmt.Stringer")
return
} else {
fmt.Println("TestStuct implements fmt.Stringer")
}
}
// Output:
// 1ns
// 1ns
// true
// time.Nanosecond implements fmt.Stringer
// 42 does not implement fmt.Stringer
// 42 does not implement fmt.Stringer
// hello world
// hello world
// TestStuct implements fmt.Stringer

50
_test/composite15.go Normal file
View File

@@ -0,0 +1,50 @@
package main
import (
"fmt"
)
func interfaceAsInts() {
var a interface{}
b := 2
c := 3
a = []int{b, c}
d, ok := a.([]int)
if !ok {
println("nope")
return
}
for _, v := range d {
fmt.Println(v)
}
}
func interfaceAsInterfaces() {
var a, b, c interface{}
b = 2
c = 3
a = []interface{}{b, c}
d, ok := a.([]interface{})
if !ok {
println("nope")
return
}
for _, v := range d {
fmt.Println(v)
}
}
func main() {
interfaceAsInts()
interfaceAsInterfaces()
}
// Output:
// 2
// 3
// 2
// 3

16
_test/composite16.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"fmt"
"net/url"
)
func main() {
body := url.Values{
"Action": {"none"},
}
fmt.Println(body)
}
// Output:
// map[Action:[none]]

17
_test/fun26.go Normal file
View File

@@ -0,0 +1,17 @@
package main
type F func() (int, error)
func f1() (int, error) { return 3, nil }
func f2(a string, f F) {
c, _ := f()
println(a, c)
}
func main() {
f2("hello", F(f1))
}
// Output:
// hello 3

12
_test/goto1.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
if true {
goto here
}
here:
println("ok")
}
// Output:
// ok

32
_test/interface47.go Normal file
View File

@@ -0,0 +1,32 @@
package main
type Doer interface {
Do() error
}
type T struct {
Name string
}
func (t *T) Do() error { println("in do"); return nil }
func f() (Doer, error) { return &T{"truc"}, nil }
type Ev struct {
doer func() (Doer, error)
}
func (e *Ev) do() {
d, _ := e.doer()
d.Do()
}
func main() {
e := &Ev{f}
println(e != nil)
e.do()
}
// Output:
// true
// in do

15
_test/issue-993.go Normal file
View File

@@ -0,0 +1,15 @@
package main
var m map[string]int64
func initVar() {
m = make(map[string]int64)
}
func main() {
initVar()
println(len(m))
}
// Output:
// 0

15
_test/time15.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "time"
type TimeValue time.Time
func (v *TimeValue) decode() { println("in decode") }
func main() {
var tv TimeValue
tv.decode()
}
// Output:
// in decode

15
_test/var15.go Normal file
View File

@@ -0,0 +1,15 @@
package main
var a int = 2
func inca() {
a = a + 1
}
func main() {
inca()
println(a)
}
// Output:
// 3

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"go/build"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
@@ -102,6 +103,18 @@ func test(arg []string) (err error) {
testing.Init()
os.Args = tf
flag.Parse()
path += string(filepath.Separator)
var dir string
switch strings.Split(path, string(filepath.Separator))[0] {
case ".", "..", string(filepath.Separator):
dir = path
default:
dir = filepath.Join(build.Default.GOPATH, "src", path)
}
if err = os.Chdir(dir); err != nil {
return err
}
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
i.Use(stdlib.Symbols)

View File

@@ -75,6 +75,9 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case assignStmt, defineStmt:
a := n.anc
i := childPos(n) - a.nright
if i < 0 {
break
}
if len(a.child) > a.nright+a.nleft {
i--
}
@@ -194,7 +197,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case breakStmt, continueStmt, gotoStmt:
if len(n.child) > 0 {
// Handle labeled statements
// Handle labeled statements.
label := n.child[0].ident
if sym, _, ok := sc.lookup(label); ok {
if sym.kind != labelSym {
@@ -211,25 +214,23 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case labeledStmt:
label := n.child[0].ident
if sym, _, ok := sc.lookup(label); ok {
if sym.kind != labelSym {
err = n.child[0].cfgErrorf("label %s not defined", label)
break
}
// TODO(marc): labels must be stored outside of symbols to avoid collisions
// Used labels are searched in current and sub scopes, not upper ones.
if sym, ok := sc.lookdown(label); ok {
sym.node = n
n.sym = sym
} else {
n.sym = &symbol{kind: labelSym, node: n, index: -1}
sc.sym[label] = n.sym
}
sc.sym[label] = n.sym
case caseClause:
sc = sc.pushBloc()
if sn := n.anc.anc; sn.kind == typeSwitch && sn.child[1].action == aAssign {
// Type switch clause with a var defined in switch guard
// Type switch clause with a var defined in switch guard.
var typ *itype
if len(n.child) == 2 {
// 1 type in clause: define the var with this type in the case clause scope
// 1 type in clause: define the var with this type in the case clause scope.
switch {
case n.child[0].ident == nilIdent:
typ = sc.getType("interface{}")
@@ -239,7 +240,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
typ, err = nodeType(interp, sc, n.child[0])
}
} else {
// define the var with the type in the switch guard expression
// Define the var with the type in the switch guard expression.
typ = sn.child[1].child[1].child[0].typ
}
if err != nil {
@@ -287,8 +288,12 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
// Get type from ancestor (implicit type)
if n.anc.kind == keyValueExpr && n == n.anc.child[0] {
n.typ = n.anc.typ.key
} else if n.anc.typ != nil {
n.typ = n.anc.typ.val
} else if atyp := n.anc.typ; atyp != nil {
if atyp.cat == valueT {
n.typ = &itype{cat: valueT, rtype: atyp.rtype.Elem()}
} else {
n.typ = atyp.val
}
}
if n.typ == nil {
err = n.cfgErrorf("undefined type")
@@ -417,9 +422,10 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
return false
}
if n.child[1].kind == identExpr {
switch n.child[1].kind {
case identExpr, selectorExpr:
n.typ = &itype{cat: aliasT, val: typ, name: typeName}
} else {
default:
n.typ = typ
n.typ.name = typeName
}
@@ -605,7 +611,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
if n.anc.kind == constDecl {
n.gen = nop
n.findex = -1
n.findex = notInFrame
if sym, _, ok := sc.lookup(dest.ident); ok {
sym.kind = constSym
}
@@ -646,7 +652,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
if n.child[0].ident == "_" {
lc.gen = typeAssertStatus
} else {
lc.gen = typeAssert2
lc.gen = typeAssertLong
}
n.gen = nop
case unaryExpr:
@@ -711,7 +717,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
// This operation involved constants, and the result is already computed
// by constOp and available in n.rval. Nothing else to do at execution.
n.gen = nop
n.findex = -1
n.findex = notInFrame
case n.anc.kind == assignStmt && n.anc.action == aAssign:
// To avoid a copy in frame, if the result is to be assigned, store it directly
// at the frame location of destination.
@@ -852,7 +858,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
switch {
case n.typ.cat == builtinT:
n.findex = -1
n.findex = notInFrame
n.val = nil
case n.anc.kind == returnStmt:
// Store result directly to frame output location, to avoid a frame copy.
@@ -894,7 +900,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
n.rval = c1.rval
case c1.rval.IsValid() && isConstType(c0.typ):
n.gen = nop
n.findex = -1
n.findex = notInFrame
n.typ = c0.typ
if c, ok := c1.rval.Interface().(constant.Value); ok {
i, _ := constant.Int64Val(constant.ToInt(c))
@@ -957,7 +963,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
}
} else {
n.findex = -1
n.findex = notInFrame
}
}
@@ -1038,7 +1044,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case fileStmt:
wireChild(n, varDecl)
sc = sc.pop()
n.findex = -1
n.findex = notInFrame
case forStmt0: // for {}
body := n.child[0]
@@ -1496,6 +1502,9 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
// Resolve source package symbol
if sym, ok := interp.srcPkg[pkg][name]; ok {
n.findex = sym.index
if sym.global {
n.level = globalFrame
}
n.val = sym.node
n.gen = nop
n.action = aGetSym
@@ -1510,7 +1519,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
if n.child[0].isType(sc) {
// Handle method as a function with receiver in 1st argument
n.val = m
n.findex = -1
n.findex = notInFrame
n.gen = nop
n.typ = &itype{}
*n.typ = *m.typ
@@ -1823,7 +1832,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
switch {
case n.rval.IsValid():
n.gen = nop
n.findex = -1
n.findex = notInFrame
case n.anc.kind == assignStmt && n.anc.action == aAssign:
dest := n.anc.child[childPos(n)-n.anc.nright]
n.typ = dest.typ
@@ -1850,6 +1859,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
if sc.global {
// Global object allocation is already performed in GTA.
index = sc.sym[c.ident].index
c.level = globalFrame
} else {
index = sc.add(n.typ)
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
@@ -1876,8 +1886,15 @@ func compDefineX(sc *scope, n *node) error {
if err != nil {
return err
}
for funtype.cat == valueT && funtype.val != nil {
// Retrieve original interpreter type from a wrapped function.
// Struct fields of function types are always wrapped in valueT to ensure
// their possible use in runtime. In that case, the val field retains the
// original interpreter type, which is used now.
funtype = funtype.val
}
if funtype.cat == valueT {
// Handle functions imported from runtime
// Handle functions imported from runtime.
for i := 0; i < funtype.rtype.NumOut(); i++ {
types = append(types, &itype{cat: valueT, rtype: funtype.rtype.Out(i)})
}
@@ -1901,7 +1918,7 @@ func compDefineX(sc *scope, n *node) error {
if n.child[0].ident == "_" {
n.child[l].gen = typeAssertStatus
} else {
n.child[l].gen = typeAssert2
n.child[l].gen = typeAssertLong
}
types = append(types, n.child[l].child[1].typ, sc.getType("bool"))
n.gen = nop
@@ -2333,7 +2350,7 @@ func isCall(n *node) bool {
}
func isBinCall(n *node) bool {
return n.kind == callExpr && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
}
func mustReturnValue(n *node) bool {
@@ -2349,7 +2366,7 @@ func mustReturnValue(n *node) bool {
}
func isRegularCall(n *node) bool {
return n.kind == callExpr && n.child[0].typ.cat == funcT
return isCall(n) && n.child[0].typ.cat == funcT
}
func variadicPos(n *node) int {
@@ -2471,6 +2488,8 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
gen = compositeBinMap
case reflect.Ptr:
gen = compositeGenerator(n, typ, n.typ.val.rtype)
case reflect.Slice:
gen = compositeBinSlice
default:
log.Panic(n.cfgErrorf("compositeGenerator not implemented for type kind: %s", k))
}

View File

@@ -115,6 +115,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ, node: n}
continue
}
c.level = globalFrame
// redeclaration error
if sym.typ.node != nil && sym.typ.node.anc != nil {
@@ -253,10 +254,11 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
return false
}
if n.child[1].kind == identExpr {
switch n.child[1].kind {
case identExpr, selectorExpr:
n.typ = &itype{cat: aliasT, val: typ, name: typeName, path: importPath, field: typ.field, incomplete: typ.incomplete, scope: sc, node: n.child[0]}
copy(n.typ.method, typ.method)
} else {
default:
n.typ = typ
n.typ.name = typeName
n.typ.path = importPath

View File

@@ -65,7 +65,8 @@ type frame struct {
// Located at start of struct to ensure proper aligment.
id uint64
anc *frame // ancestor frame (global space)
root *frame // global space
anc *frame // ancestor frame (caller space)
data []reflect.Value // values
mutex sync.RWMutex
@@ -80,8 +81,11 @@ func newFrame(anc *frame, len int, id uint64) *frame {
data: make([]reflect.Value, len),
id: id,
}
if anc != nil {
if anc == nil {
f.root = f
} else {
f.done = anc.done
f.root = anc.root
}
return f
}
@@ -93,6 +97,7 @@ func (f *frame) clone() *frame {
defer f.mutex.RUnlock()
return &frame{
anc: f.anc,
root: f.root,
data: f.data,
deferred: f.deferred,
recovered: f.recovered,
@@ -239,7 +244,7 @@ type Options struct {
func New(options Options) *Interpreter {
i := Interpreter{
opt: opt{context: build.Default},
frame: &frame{data: []reflect.Value{}},
frame: newFrame(nil, 0, 0),
fset: token.NewFileSet(),
universe: initUniverse(),
scopes: map[string]*scope{},

View File

@@ -110,6 +110,7 @@ func TestEvalAssign(t *testing.T) {
{src: "f := int64(3.2)", err: "1:39: cannot convert expression of type float64 to type int64"},
{src: "g := 1; g <<= 8", res: "256"},
{src: "h := 1; h >>= 8", res: "0"},
{src: "i := 1; j := &i; (*j) = 2", res: "2"},
})
}
@@ -444,19 +445,15 @@ func TestEvalSliceExpression(t *testing.T) {
{src: `a := []int{0,1,2}[:]`, res: "[0 1 2]"},
{src: `a := []int{0,1,2,3}[1:3:4]`, res: "[1 2]"},
{src: `a := []int{0,1,2,3}[:3:4]`, res: "[0 1 2]"},
{src: `ar := [3]int{0,1,2}
a := ar[1:3]`, res: "[1 2]"},
{src: `ar := [3]int{0,1,2}; a := ar[1:3]`, res: "[1 2]"},
{src: `a := (&[3]int{0,1,2})[1:3]`, res: "[1 2]"},
{src: `a := (&[3]int{0,1,2})[1:3]`, res: "[1 2]"},
{src: `s := "hello"[1:3]`, res: "el"},
{src: `str := "hello"
s := str[1:3]`, res: "el"},
{src: `str := "hello"; s := str[1:3]`, res: "el"},
{src: `a := int(1)[0:1]`, err: "1:33: cannot slice type int"},
{src: `a := ([3]int{0,1,2})[1:3]`, err: "1:33: cannot slice type [3]int"},
{src: `a := (&[]int{0,1,2,3})[1:3]`, err: "1:33: cannot slice type *[]int"},
{src: `a := "hello"[1:3:4]`, err: "1:45: invalid operation: 3-index slice of string"},
{src: `ar := [3]int{0,1,2}
a := ar[:4]`, err: "2:16: index int is out of bounds"},
{src: `ar := [3]int{0,1,2}; a := ar[:4]`, err: "1:58: index int is out of bounds"},
{src: `a := []int{0,1,2,3}[1::4]`, err: "1:49: 2nd index required in 3-index slice"},
{src: `a := []int{0,1,2,3}[1:3:]`, err: "1:51: 3rd index required in 3-index slice"},
{src: `a := []int{0,1,2}[3:1]`, err: "invalid index values, must be low <= high <= max"},

View File

@@ -3,10 +3,12 @@ package interp
//go:generate go run ../internal/cmd/genop/genop.go
import (
"errors"
"fmt"
"go/constant"
"log"
"reflect"
"regexp"
"sync"
"unsafe"
)
@@ -67,11 +69,22 @@ var builtin = [...]bltnGenerator{
aStar: deref,
aSub: sub,
aSubAssign: subAssign,
aTypeAssert: typeAssert,
aTypeAssert: typeAssertShort,
aXor: xor,
aXorAssign: xorAssign,
}
var receiverStripperRxp *regexp.Regexp
func init() {
re := `func\(((.*?(, |\)))(.*))`
var err error
receiverStripperRxp, err = regexp.Compile(re)
if err != nil {
panic(err)
}
}
type valueInterface struct {
node *node
value reflect.Value
@@ -178,118 +191,47 @@ func runCfg(n *node, f *frame) {
}
}
func typeAssertStatus(n *node) {
c0, c1 := n.child[0], n.child[1] // cO contains the input value, c1 the type to assert
value := genValue(c0) // input value
value1 := genValue(n.anc.child[1]) // returned status
rtype := c1.typ.rtype // type to assert
next := getExec(n.tnext)
switch {
case isInterfaceSrc(c1.typ):
typ := c1.typ
n.exec = func(f *frame) bltn {
v, ok := value(f).Interface().(valueInterface)
value1(f).SetBool(ok && v.node.typ.implements(typ))
return next
}
case isInterface(c1.typ):
n.exec = func(f *frame) bltn {
v := value(f)
ok := v.IsValid() && canAssertTypes(v.Elem().Type(), rtype)
value1(f).SetBool(ok)
return next
}
case c0.typ.cat == valueT || c0.typ.cat == errorT:
n.exec = func(f *frame) bltn {
v := value(f)
ok := v.IsValid() && canAssertTypes(v.Elem().Type(), rtype)
value1(f).SetBool(ok)
return next
}
default:
n.exec = func(f *frame) bltn {
v, ok := value(f).Interface().(valueInterface)
ok = ok && v.value.IsValid() && canAssertTypes(v.value.Type(), rtype)
value1(f).SetBool(ok)
return next
}
func stripReceiverFromArgs(signature string) (string, error) {
fields := receiverStripperRxp.FindStringSubmatch(signature)
if len(fields) < 5 {
return "", errors.New("error while matching method signature")
}
if fields[3] == ")" {
return fmt.Sprintf("func()%s", fields[4]), nil
}
return fmt.Sprintf("func(%s", fields[4]), nil
}
func typeAssert(n *node) {
func typeAssertShort(n *node) {
typeAssert(n, true, false)
}
func typeAssertLong(n *node) {
typeAssert(n, true, true)
}
func typeAssertStatus(n *node) {
typeAssert(n, false, true)
}
func typeAssert(n *node, withResult, withOk bool) {
c0, c1 := n.child[0], n.child[1]
value := genValue(c0) // input value
value0 := genValue(n) // returned result
next := getExec(n.tnext)
var value0, value1 func(*frame) reflect.Value
setStatus := false
switch {
case isInterfaceSrc(c1.typ):
typ := n.child[1].typ
typID := n.child[1].typ.id()
n.exec = func(f *frame) bltn {
v := value(f)
vi, ok := v.Interface().(valueInterface)
if !ok {
panic(n.cfgErrorf("interface conversion: nil is not %v", typID))
}
if !vi.node.typ.implements(typ) {
panic(n.cfgErrorf("interface conversion: %v is not %v", vi.node.typ.id(), typID))
}
value0(f).Set(v)
return next
}
case isInterface(c1.typ):
n.exec = func(f *frame) bltn {
v := value(f).Elem()
typ := value0(f).Type()
if !v.IsValid() {
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", typ.String()))
}
if !canAssertTypes(v.Type(), typ) {
method := firstMissingMethod(v.Type(), typ)
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", v.Type().String(), typ.String(), method))
}
value0(f).Set(v)
return next
}
case c0.typ.cat == valueT || c0.typ.cat == errorT:
n.exec = func(f *frame) bltn {
v := value(f).Elem()
typ := value0(f).Type()
if !v.IsValid() {
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", typ.String()))
}
if !canAssertTypes(v.Type(), typ) {
method := firstMissingMethod(v.Type(), typ)
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", v.Type().String(), typ.String(), method))
}
value0(f).Set(v)
return next
}
default:
n.exec = func(f *frame) bltn {
v := value(f).Interface().(valueInterface)
typ := value0(f).Type()
if !v.value.IsValid() {
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", typ.String()))
}
if !canAssertTypes(v.value.Type(), typ) {
panic(fmt.Sprintf("interface conversion: interface {} is %s, not %s", v.value.Type().String(), typ.String()))
}
value0(f).Set(v.value)
return next
}
case withResult && withOk:
value0 = genValue(n.anc.child[0]) // returned result
value1 = genValue(n.anc.child[1]) // returned status
setStatus = n.anc.child[1].ident != "_" // do not assign status to "_"
case withResult && !withOk:
value0 = genValue(n) // returned result
case !withResult && withOk:
value1 = genValue(n.anc.child[1]) // returned status
setStatus = n.anc.child[1].ident != "_" // do not assign status to "_"
}
}
func typeAssert2(n *node) {
c0, c1 := n.child[0], n.child[1]
value := genValue(c0) // input value
value0 := genValue(n.anc.child[0]) // returned result
value1 := genValue(n.anc.child[1]) // returned status
setStatus := n.anc.child[1].ident != "_" // do not assign status to "_"
typ := c1.typ // type to assert or convert to
typ := c1.typ // type to assert or convert to
typID := typ.id()
rtype := typ.rtype // type to assert
next := getExec(n.tnext)
@@ -297,50 +239,198 @@ func typeAssert2(n *node) {
switch {
case isInterfaceSrc(typ):
n.exec = func(f *frame) bltn {
v, ok := value(f).Interface().(valueInterface)
if ok && v.node.typ.id() == typID {
value0(f).Set(value(f))
} else {
ok = false
}
valf := value(f)
v, ok := valf.Interface().(valueInterface)
if setStatus {
value1(f).SetBool(ok)
defer func() {
value1(f).SetBool(ok)
}()
}
if !ok {
if !withOk {
panic(n.cfgErrorf("interface conversion: nil is not %v", typID))
}
return next
}
if v.node.typ.id() == typID {
if withResult {
value0(f).Set(valf)
}
return next
}
m0 := v.node.typ.methods()
m1 := typ.methods()
if len(m0) < len(m1) {
ok = false
if !withOk {
panic(n.cfgErrorf("interface conversion: %v is not %v", v.node.typ.id(), typID))
}
return next
}
for k, meth1 := range m1 {
var meth0 string
meth0, ok = m0[k]
if !ok {
return next
}
// As far as we know this equality check can fail because they are two ways to
// represent the signature of a method: one where the receiver appears before the
// func keyword, and one where it is just a func signature, and the receiver is
// seen as the first argument. That's why if that equality fails, we try harder to
// compare them afterwards. Hopefully that is the only reason this equality can fail.
if meth0 == meth1 {
continue
}
tm := lookupFieldOrMethod(v.node.typ, k)
if tm == nil {
ok = false
return next
}
var err error
meth0, err = stripReceiverFromArgs(meth0)
if err != nil {
ok = false
return next
}
if meth0 != meth1 {
ok = false
return next
}
}
if withResult {
value0(f).Set(valf)
}
return next
}
case isInterface(typ):
n.exec = func(f *frame) bltn {
v := value(f).Elem()
ok := v.IsValid() && canAssertTypes(v.Type(), rtype)
if ok {
value0(f).Set(v)
}
var leftType reflect.Type
v := value(f)
val, ok := v.Interface().(valueInterface)
if setStatus {
value1(f).SetBool(ok)
defer func() {
value1(f).SetBool(ok)
}()
}
if ok && val.node.typ.cat != valueT {
m0 := val.node.typ.methods()
m1 := typ.methods()
if len(m0) < len(m1) {
ok = false
return next
}
for k, meth1 := range m1 {
var meth0 string
meth0, ok = m0[k]
if !ok {
return next
}
if meth0 != meth1 {
ok = false
return next
}
}
if withResult {
value0(f).Set(genInterfaceWrapper(val.node, rtype)(f))
}
ok = true
return next
}
if ok {
v = val.value
leftType = val.node.typ.rtype
} else {
v = v.Elem()
leftType = v.Type()
ok = true
}
ok = v.IsValid()
if !ok {
if !withOk {
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
}
return next
}
ok = canAssertTypes(leftType, rtype)
if !ok {
if !withOk {
method := firstMissingMethod(leftType, rtype)
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", leftType.String(), rtype.String(), method))
}
return next
}
if withResult {
value0(f).Set(v)
}
return next
}
case n.child[0].typ.cat == valueT || n.child[0].typ.cat == errorT:
n.exec = func(f *frame) bltn {
v := value(f).Elem()
ok := v.IsValid() && canAssertTypes(v.Type(), rtype)
if ok {
value0(f).Set(v)
}
ok := v.IsValid()
if setStatus {
value1(f).SetBool(ok)
defer func() {
value1(f).SetBool(ok)
}()
}
if !ok {
if !withOk {
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
}
return next
}
v = valueInterfaceValue(v)
ok = canAssertTypes(v.Type(), rtype)
if !ok {
if !withOk {
method := firstMissingMethod(v.Type(), rtype)
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", v.Type().String(), rtype.String(), method))
}
return next
}
if withResult {
value0(f).Set(v)
}
return next
}
default:
n.exec = func(f *frame) bltn {
v, ok := value(f).Interface().(valueInterface)
ok = ok && v.value.IsValid() && canAssertTypes(v.value.Type(), rtype)
if ok {
value0(f).Set(v.value)
}
if setStatus {
value1(f).SetBool(ok)
defer func() {
value1(f).SetBool(ok)
}()
}
if !ok || !v.value.IsValid() {
ok = false
if !withOk {
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
}
return next
}
styp := v.value.Type()
// TODO(mpl): probably also maps and others. and might have to recurse too.
if styp.String() == "[]interp.valueInterface" {
styp = v.node.typ.rtype
}
ok = canAssertTypes(styp, rtype)
if !ok {
if !withOk {
panic(fmt.Sprintf("interface conversion: interface {} is %s, not %s", v.value.Type().String(), rtype.String()))
}
return next
}
if withResult {
value0(f).Set(v.value)
}
return next
}
@@ -588,12 +678,23 @@ func not(n *node) {
func addr(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
dest(f).Set(value(f).Addr())
return next
c0 := n.child[0]
value := genValue(c0)
switch c0.typ.cat {
case interfaceT:
i := n.findex
l := n.level
n.exec = func(f *frame) bltn {
v := value(f).Interface().(valueInterface).value
getFrame(f, l).data[i] = reflect.ValueOf(v.Interface())
return next
}
default:
n.exec = func(f *frame) bltn {
dest(f).Set(value(f).Addr())
return next
}
}
}
@@ -763,6 +864,9 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
if src.Type().Kind() != dest.Type().Kind() {
dest.Set(src.Addr())
} else {
if wrappedSrc, ok := src.Interface().(valueInterface); ok {
src = wrappedSrc.value
}
dest.Set(src)
}
d = d[numRet+1:]
@@ -791,11 +895,6 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
if v, ok := r.Interface().(*node); ok {
result[i] = genFunctionWrapper(v)(f)
}
if def.typ.ret[i].cat == interfaceT {
x := result[i].Interface().(valueInterface).value
result[i] = reflect.New(reflect.TypeOf((*interface{})(nil)).Elem()).Elem()
result[i].Set(x)
}
}
return result
})
@@ -868,7 +967,10 @@ func call(n *node) {
var method bool
value := genValue(n.child[0])
var values []func(*frame) reflect.Value
if n.child[0].recv != nil {
recvIndexLater := false
switch {
case n.child[0].recv != nil:
// Compute method receiver value.
if isRecursiveType(n.child[0].recv.node.typ, n.child[0].recv.node.typ.rtype) {
values = append(values, genValueRecvInterfacePtr(n.child[0]))
@@ -876,11 +978,17 @@ func call(n *node) {
values = append(values, genValueRecv(n.child[0]))
}
method = true
} else if n.child[0].action == aMethod {
case len(n.child[0].child) > 0 && n.child[0].child[0].typ != nil && n.child[0].child[0].typ.cat == interfaceT:
recvIndexLater = true
values = append(values, genValueBinRecv(n.child[0], &receiver{node: n.child[0].child[0]}))
value = genValueBinMethodOnInterface(n, value)
method = true
case n.child[0].action == aMethod:
// Add a place holder for interface method receiver.
values = append(values, nil)
method = true
}
numRet := len(n.child[0].typ.ret)
variadic := variadicPos(n)
child := n.child[1:]
@@ -990,6 +1098,7 @@ func call(n *node) {
n.exec = func(f *frame) bltn {
var def *node
var ok bool
bf := value(f)
if def, ok = bf.Interface().(*node); ok {
bf = def.rval
@@ -1059,16 +1168,16 @@ func call(n *node) {
var src reflect.Value
if v == nil {
src = def.recv.val
if len(def.recv.index) > 0 {
if src.Kind() == reflect.Ptr {
src = src.Elem().FieldByIndex(def.recv.index)
} else {
src = src.FieldByIndex(def.recv.index)
}
}
} else {
src = v(f)
}
if recvIndexLater && def.recv != nil && len(def.recv.index) > 0 {
if src.Kind() == reflect.Ptr {
src = src.Elem().FieldByIndex(def.recv.index)
} else {
src = src.FieldByIndex(def.recv.index)
}
}
// Accommodate to receiver type
d := dest[0]
if ks, kd := src.Kind(), d.Kind(); ks != kd {
@@ -1112,6 +1221,8 @@ func call(n *node) {
func getFrame(f *frame, l int) *frame {
switch l {
case globalFrame:
return f.root
case 0:
return f
case 1:
@@ -1165,13 +1276,13 @@ func callBin(n *node) {
numOut := c.child[0].typ.rtype.NumOut()
for j := 0; j < numOut; j++ {
ind := c.findex + j
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
values = append(values, func(f *frame) reflect.Value { return valueInterfaceValue(f.data[ind]) })
}
case isRegularCall(c):
// Handle nested function calls: pass returned values as arguments
for j := range c.child[0].typ.ret {
ind := c.findex + j
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
values = append(values, func(f *frame) reflect.Value { return valueInterfaceValue(f.data[ind]) })
}
default:
if c.kind == basicLit || c.rval.IsValid() {
@@ -1265,7 +1376,11 @@ func callBin(n *node) {
for i := range rvalues {
c := n.anc.child[i]
if c.ident != "_" {
rvalues[i] = genValue(c)
if c.typ.cat == interfaceT {
rvalues[i] = genValueInterfaceValue(c)
} else {
rvalues[i] = genValue(c)
}
}
}
n.exec = func(f *frame) bltn {
@@ -1608,6 +1723,22 @@ func getMethodByName(n *node) {
n.exec = func(f *frame) bltn {
val := value0(f).Interface().(valueInterface)
for {
v, ok := val.value.Interface().(valueInterface)
if !ok {
break
}
val = v
}
typ := val.node.typ
if typ.node == nil && typ.cat == valueT {
// happens with a var of empty interface type, that has value of concrete type
// from runtime, being asserted to "user-defined" interface.
if _, ok := typ.rtype.MethodByName(name); !ok {
panic(fmt.Sprintf("method %s not found", name))
}
return next
}
m, li := val.node.typ.lookupMethod(name)
fr := f.clone()
nod := *m
@@ -2031,7 +2162,11 @@ func arrayLit(n *node) {
for i, v := range values {
a.Index(index[i]).Set(v(f))
}
value(f).Set(a)
dest := value(f)
if _, ok := dest.Interface().(valueInterface); ok {
a = reflect.ValueOf(valueInterface{n, a})
}
dest.Set(a)
return next
}
}
@@ -2098,6 +2233,51 @@ func compositeBinMap(n *node) {
}
}
func compositeBinSlice(n *node) {
value := valueGenerator(n, n.findex)
next := getExec(n.tnext)
child := n.child
if n.nleft == 1 {
child = n.child[1:]
}
values := make([]func(*frame) reflect.Value, len(child))
index := make([]int, len(child))
rtype := n.typ.rtype.Elem()
var max, prev int
for i, c := range child {
if c.kind == keyValueExpr {
convertLiteralValue(c.child[1], rtype)
values[i] = genValue(c.child[1])
index[i] = int(vInt(c.child[0].rval))
} else {
convertLiteralValue(c, rtype)
values[i] = genValue(c)
index[i] = prev
}
prev = index[i] + 1
if prev > max {
max = prev
}
}
typ := n.typ.frameType()
n.exec = func(f *frame) bltn {
var a reflect.Value
if n.typ.sizedef {
a, _ = n.typ.zero()
} else {
a = reflect.MakeSlice(typ, max, max)
}
for i, v := range values {
a.Index(index[i]).Set(v(f))
}
value(f).Set(a)
return next
}
}
// doCompositeBinStruct creates and populates a struct object from a binary type.
func doCompositeBinStruct(n *node, hasType bool) {
next := getExec(n.tnext)

View File

@@ -72,26 +72,28 @@ type symbol struct {
// execution to the index in frame, created exactly from the types layout.
//
type scope struct {
anc *scope // Ancestor upper scope
anc *scope // ancestor upper scope
child []*scope // included scopes
def *node // function definition node this scope belongs to, or nil
loop *node // loop exit node for break statement
loopRestart *node // loop restart node for continue statement
pkgID string // unique id of package in which scope is defined
types []reflect.Type // Frame layout, may be shared by same level scopes
level int // Frame level: number of frame indirections to access var during execution
sym map[string]*symbol // Map of symbols defined in this current scope
types []reflect.Type // frame layout, may be shared by same level scopes
level int // frame level: number of frame indirections to access var during execution
sym map[string]*symbol // map of symbols defined in this current scope
global bool // true if scope refers to global space (single frame for universe and package level scopes)
iota int // iota value in this scope
}
// push creates a new scope and chain it to the current one.
// push creates a new child scope and chain it to the current one.
func (s *scope) push(indirect bool) *scope {
sc := scope{anc: s, level: s.level, sym: map[string]*symbol{}}
sc := &scope{anc: s, level: s.level, sym: map[string]*symbol{}}
s.child = append(s.child, sc)
if indirect {
sc.types = []reflect.Type{}
sc.level = s.level + 1
} else {
// propagate size, types, def and global as scopes at same level share the same frame
// Propagate size, types, def and global as scopes at same level share the same frame.
sc.types = s.types
sc.def = s.def
sc.global = s.global
@@ -99,7 +101,7 @@ func (s *scope) push(indirect bool) *scope {
}
// inherit loop state and pkgID from ancestor
sc.loop, sc.loopRestart, sc.pkgID = s.loop, s.loopRestart, s.pkgID
return &sc
return sc
}
func (s *scope) pushBloc() *scope { return s.push(false) }
@@ -107,7 +109,7 @@ func (s *scope) pushFunc() *scope { return s.push(true) }
func (s *scope) pop() *scope {
if s.level == s.anc.level {
// propagate size and types, as scopes at same level share the same frame
// Propagate size and types, as scopes at same level share the same frame.
s.anc.types = s.types
}
return s.anc
@@ -128,6 +130,9 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) {
level := s.level
for {
if sym, ok := s.sym[ident]; ok {
if sym.global {
return sym, globalFrame, true
}
return sym, level - s.level, true
}
if s.anc == nil {
@@ -138,6 +143,20 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) {
return nil, 0, false
}
// lookdown searches for a symbol in the current scope and included ones, recursively.
// It returns the first found symbol and true, or nil and false.
func (s *scope) lookdown(ident string) (*symbol, bool) {
if sym, ok := s.sym[ident]; ok {
return sym, true
}
for _, c := range s.child {
if sym, ok := c.lookdown(ident); ok {
return sym, true
}
}
return nil, false
}
func (s *scope) rangeChanType(n *node) *itype {
if sym, _, found := s.lookup(n.child[1].ident); found {
if t := sym.typ; len(n.child) == 3 && t != nil && (t.cat == chanT || t.cat == chanRecvT) {

View File

@@ -557,7 +557,8 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
if v, ok := pkg[name]; ok {
t.cat = valueT
t.rtype = v.Type()
if isBinType(v) { // a bin type is encoded as a pointer on nil value
if isBinType(v) {
// A bin type is encoded as a pointer on a typed nil value.
t.rtype = t.rtype.Elem()
}
} else {
@@ -736,7 +737,7 @@ func (t *itype) finalize() (*itype, error) {
}
// ReferTo returns true if the type contains a reference to a
// full type name. It allows to asses a type recursive status.
// full type name. It allows to assess a type recursive status.
func (t *itype) referTo(name string, seen map[*itype]bool) bool {
if t.path+"/"+t.name == name {
return true

View File

@@ -499,9 +499,7 @@ func (check typecheck) sliceExpr(n *node) error {
case reflect.Array:
valid = true
l = t.Len()
if c.kind != indexExpr && c.kind != selectorExpr && (c.sym == nil || c.sym.kind != varSym) {
return c.cfgErrorf("cannot slice type %s", c.typ.id())
}
// TODO(marc): check addressable status of array object (i.e. composite arrays are not).
case reflect.Slice:
valid = true
case reflect.Ptr:

View File

@@ -5,8 +5,15 @@ import (
"reflect"
)
const (
notInFrame = -1 // value of node.findex for literal values (not in frame)
globalFrame = -1 // value of node.level for global symbols
)
func valueGenerator(n *node, i int) func(*frame) reflect.Value {
switch n.level {
case globalFrame:
return func(f *frame) reflect.Value { return valueOf(f.root.data, i) }
case 0:
return func(f *frame) reflect.Value { return valueOf(f.data, i) }
case 1:
@@ -33,6 +40,30 @@ func valueOf(data []reflect.Value, i int) reflect.Value {
return reflect.Value{}
}
func genValueBinMethodOnInterface(n *node, defaultGen func(*frame) reflect.Value) func(*frame) reflect.Value {
if n == nil || n.child == nil || n.child[0] == nil ||
n.child[0].child == nil || n.child[0].child[0] == nil {
return defaultGen
}
if n.child[0].child[1] == nil || n.child[0].child[1].ident == "" {
return defaultGen
}
value0 := genValue(n.child[0].child[0])
return func(f *frame) reflect.Value {
val, ok := value0(f).Interface().(valueInterface)
if !ok {
return defaultGen(f)
}
typ := val.node.typ
if typ.node != nil || typ.cat != valueT {
return defaultGen(f)
}
meth, _ := typ.rtype.MethodByName(n.child[0].child[1].ident)
return meth.Func
}
}
func genValueRecvIndirect(n *node) func(*frame) reflect.Value {
v := genValueRecv(n)
return func(f *frame) reflect.Value { return v(f).Elem() }
@@ -55,6 +86,35 @@ func genValueRecv(n *node) func(*frame) reflect.Value {
}
}
func genValueBinRecv(n *node, recv *receiver) func(*frame) reflect.Value {
value := genValue(n)
binValue := genValue(recv.node)
v := func(f *frame) reflect.Value {
if def, ok := value(f).Interface().(*node); ok {
if def != nil && def.recv != nil && def.recv.val.IsValid() {
return def.recv.val
}
}
ival, _ := binValue(f).Interface().(valueInterface)
return ival.value
}
fi := recv.index
if len(fi) == 0 {
return v
}
return func(f *frame) reflect.Value {
r := v(f)
if r.Kind() == reflect.Ptr {
r = r.Elem()
}
return r.FieldByIndex(fi)
}
}
func genValueRecvInterfacePtr(n *node) func(*frame) reflect.Value {
v := genValue(n.recv.node)
fi := n.recv.index
@@ -118,18 +178,16 @@ func genValue(n *node) func(*frame) reflect.Value {
return func(f *frame) reflect.Value { return v }
}
if n.sym != nil {
if n.sym.index < 0 {
i := n.sym.index
if i < 0 {
return genValue(n.sym.node)
}
i := n.sym.index
if n.sym.global {
return func(f *frame) reflect.Value {
return n.interp.frame.data[i]
}
return func(f *frame) reflect.Value { return f.root.data[i] }
}
return valueGenerator(n, i)
}
if n.findex < 0 {
if n.findex == notInFrame {
var v reflect.Value
if w, ok := n.val.(reflect.Value); ok {
v = w
@@ -244,6 +302,17 @@ func genValueOutput(n *node, t reflect.Type) func(*frame) reflect.Value {
return value
}
func valueInterfaceValue(v reflect.Value) reflect.Value {
for {
vv, ok := v.Interface().(valueInterface)
if !ok {
break
}
v = vv.value
}
return v
}
func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
value := genValue(n)
@@ -254,7 +323,7 @@ func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
v.Set(zeroInterfaceValue())
v = value(f)
}
return v.Interface().(valueInterface).value
return valueInterfaceValue(v)
}
}