Compare commits

...

11 Commits

Author SHA1 Message Date
Marc Vertes
8fa00f826c interp: fix map assignment from arithmetic operations
The logic to trigger assigment optimizations has been refactored for
clarity, and to exclude assignments to map entries.

Fixes #981.
2021-01-18 19:04:05 +01:00
Marc Vertes
a64fe5b210 interp: fix detection of type recursivity
If a struct contains several fields of the same temporary incomplete
type, it could be detected incorrectly as a recursive struct. Pass
a copy of defined types map to avoid this issue.

Fixes #1007.
2021-01-15 12:14:04 +01:00
Marc Vertes
5c59dc425f interp: fix operators working on integer constants
Always attempt to obtain an integer constant value for operators
expecting so. It allows to use '/' in integer constant defintions,
instead of default big.Rat.

Fixes #1005
2021-01-14 17:26:06 +01:00
Marc Vertes
8ad14d8ea4 interp: handle aliased string used as a slice
Fixes #1002.
2021-01-14 16:48:06 +01:00
Marc Vertes
8a1f9ef44e interp: parse circular interface definitions
An undefined type detection function has been added to better diagnose
incomplete type definitions. Implicit type names in interface or struct
declarations are now better handled. The incomplete status is not
fowarded to aliased type declarations to handle circular definitions.

Fixes #999 and #995. Improves #260 (goes farther, but still fails).
2021-01-14 15:46:04 +01:00
Marc Vertes
5cd1e11379 chore: rename github to tap, following goreleaser deprecation notice
The release of v0.9.9 failed, due to
https://goreleaser.com/deprecations/#brewsgithub.
2021-01-06 09:14:04 +01:00
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
23 changed files with 602 additions and 132 deletions

View File

@@ -47,7 +47,7 @@ archives:
- LICENSE
brews:
- github:
- tap:
owner: traefik
name: homebrew-tap
commit_author:

View File

@@ -48,6 +48,14 @@ func main() {
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 {
@@ -57,6 +65,12 @@ func main() {
}
_ = 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
@@ -72,6 +86,12 @@ func main() {
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 {
@@ -81,6 +101,11 @@ func main() {
}
_ = bar
if _, ok := tt.(MyStringer); !ok {
fmt.Println("42 does not implement MyStringer")
} else {
fmt.Println("42 implements MyStringer")
}
}
// Output:
@@ -88,9 +113,13 @@ func main() {
// 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

View File

@@ -27,6 +27,13 @@ func main() {
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)
@@ -34,9 +41,17 @@ func main() {
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)
@@ -49,12 +64,22 @@ func main() {
// 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]]

15
_test/const22.go Normal file
View File

@@ -0,0 +1,15 @@
package main
const (
numDec uint8 = (1 << iota) / 2
numHex
numOct
numFloat
)
func main() {
println(13 & (numHex | numOct))
}
// Output:
// 1

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

17
_test/interface48.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "fmt"
type I1 interface{ A }
type A = I2
type I2 interface{ F() I1 }
func main() {
var i I1
fmt.Println(i)
}
// Output:
// <nil>

45
_test/interface49.go Normal file
View File

@@ -0,0 +1,45 @@
package main
type Descriptor interface {
ParentFile() FileDescriptor
}
type FileDescriptor interface {
Enums() EnumDescriptors
Services() ServiceDescriptors
}
type EnumDescriptors interface {
Get(i int) EnumDescriptor
}
type EnumDescriptor interface {
Values() EnumValueDescriptors
}
type EnumValueDescriptors interface {
Get(i int) EnumValueDescriptor
}
type EnumValueDescriptor interface {
Descriptor
}
type ServiceDescriptors interface {
Get(i int) ServiceDescriptor
}
type ServiceDescriptor interface {
Descriptor
isServiceDescriptor
}
type isServiceDescriptor interface{ ProtoType(ServiceDescriptor) }
func main() {
var d Descriptor
println(d == nil)
}
// Output:
// true

40
_test/issue-1007.go Normal file
View File

@@ -0,0 +1,40 @@
package main
type TypeA struct {
B TypeB
}
type TypeB struct {
C1 *TypeC
C2 *TypeC
}
type TypeC struct {
Val string
D *TypeD
D2 *TypeD
}
type TypeD struct {
Name string
}
func build() *TypeA {
return &TypeA{
B: TypeB{
C2: &TypeC{Val: "22"},
},
}
}
func Bar(s string) string {
a := build()
return s + "-" + a.B.C2.Val
}
func main() {
println(Bar("test"))
}
// Output:
// test-22

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

@@ -0,0 +1,15 @@
package main
import "fmt"
func main() {
dp := make(map[int]int)
dp[0] = 1
for i := 1; i < 10; i++ {
dp[i] = dp[i-1] + dp[i-2]
}
fmt.Printf("%v\n", dp)
}
// Output:
// map[0:1 1:1 2:2 3:3 4:5 5:8 6:13 7:21 8:34 9:55]

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/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

@@ -209,7 +209,11 @@ func {{$name}}Const(n *node) {
v := constant.BinaryOp(vConstantValue(v0), operator, vConstantValue(v1))
n.rval.Set(reflect.ValueOf(v))
{{- else}}
{{- if $op.Int}}
v := constant.BinaryOp(constant.ToInt(vConstantValue(v0)), token.{{tokenFromName $name}}, constant.ToInt(vConstantValue(v1)))
{{- else}}
v := constant.BinaryOp(vConstantValue(v0), token.{{tokenFromName $name}}, vConstantValue(v1))
{{- end}}
n.rval.Set(reflect.ValueOf(v))
{{- end}}
{{- if $op.Str}}
@@ -940,6 +944,7 @@ type Op struct {
Complex bool // true if operator applies to complex
Shift bool // true if operator is a shift operation
Bool bool // true if operator applies to bool
Int bool // true if operator applies to int only
}
func main() {
@@ -968,17 +973,17 @@ func main() {
b := &bytes.Buffer{}
data := map[string]interface{}{
"Arithmetic": map[string]Op{
"add": {"+", true, true, true, false, false},
"sub": {"-", false, true, true, false, false},
"mul": {"*", false, true, true, false, false},
"quo": {"/", false, true, true, false, false},
"rem": {"%", false, false, false, false, false},
"shl": {"<<", false, false, false, true, false},
"shr": {">>", false, false, false, true, false},
"and": {"&", false, false, false, false, false},
"or": {"|", false, false, false, false, false},
"xor": {"^", false, false, false, false, false},
"andNot": {"&^", false, false, false, false, false},
"add": {"+", true, true, true, false, false, false},
"sub": {"-", false, true, true, false, false, false},
"mul": {"*", false, true, true, false, false, false},
"quo": {"/", false, true, true, false, false, false},
"rem": {"%", false, false, false, false, false, true},
"shl": {"<<", false, false, false, true, false, true},
"shr": {">>", false, false, false, true, false, true},
"and": {"&", false, false, false, false, false, true},
"or": {"|", false, false, false, false, false, true},
"xor": {"^", false, false, false, false, false, true},
"andNot": {"&^", false, false, false, false, false, true},
},
"IncDec": map[string]Op{
"inc": {Name: "+"},
@@ -996,7 +1001,7 @@ func main() {
"not": {Name: "!", Float: false, Bool: true},
"neg": {Name: "-", Float: true, Bool: false},
"pos": {Name: "+", Float: true, Bool: false},
"bitNot": {Name: "^", Float: false, Bool: false},
"bitNot": {Name: "^", Float: false, Bool: false, Int: true},
},
}
if err = parse.Execute(b, data); err != nil {

View File

@@ -288,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")
@@ -551,10 +555,21 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
n.findex = dest.findex
n.level = dest.level
// Propagate type
// TODO: Check that existing destination type matches source type
// Propagate type.
// TODO: Check that existing destination type matches source type.
// In the following, we attempt to optimize by skipping the assign
// operation and setting the source location directly to the destination
// location in the frame.
//
switch {
case n.action == aAssign && isCall(src) && dest.typ.cat != interfaceT && !isMapEntry(dest) && !isRecursiveField(dest):
case n.action != aAssign:
// Do not optimize assign combined with another operator.
case isMapEntry(dest):
// Setting a map entry needs an additional step, do not optimize.
// As we only write, skip the default useless getIndexMap dest action.
dest.gen = nop
case isCall(src) && dest.typ.cat != interfaceT && !isRecursiveField(dest):
// Call action may perform the assignment directly.
n.gen = nop
src.level = level
@@ -562,32 +577,25 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
if src.typ.untyped && !dest.typ.untyped {
src.typ = dest.typ
}
case n.action == aAssign && src.action == aRecv:
case src.action == aRecv:
// Assign by reading from a receiving channel.
n.gen = nop
src.findex = dest.findex // Set recv address to LHS
src.findex = dest.findex // Set recv address to LHS.
dest.typ = src.typ
case n.action == aAssign && src.action == aCompositeLit && !isMapEntry(dest):
case src.action == aCompositeLit:
if dest.typ.cat == valueT && dest.typ.rtype.Kind() == reflect.Interface {
// Skip optimisation for assigned binary interface or map entry
// which require and additional operation to set the value
// Skip optimisation for assigned interface.
break
}
if dest.action == aGetIndex {
// optimization does not work when assigning to a struct field. Maybe we're not
// setting the right frame index or something, and we would end up not writing at
// the right place. So disabling it for now.
// Optimization does not work when assigning to a struct field.
break
}
// Skip the assign operation entirely, the source frame index is set
// to destination index, avoiding extra memory alloc and duplication.
n.gen = nop
src.findex = dest.findex
src.level = level
case n.action == aAssign && len(n.child) < 4 && !src.rval.IsValid() && isArithmeticAction(src):
case len(n.child) < 4 && !src.rval.IsValid() && isArithmeticAction(src):
// Optimize single assignments from some arithmetic operations.
// Skip the assign operation entirely, the source frame index is set
// to destination index, avoiding extra memory alloc and duplication.
src.typ = dest.typ
src.findex = dest.findex
src.level = level
@@ -596,18 +604,17 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
// Assign to nil.
src.rval = reflect.New(dest.typ.TypeOf()).Elem()
}
n.typ = dest.typ
if sym != nil {
sym.typ = n.typ
sym.recv = src.recv
}
n.level = level
if isMapEntry(dest) {
dest.gen = nop // skip getIndexMap
}
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
}
@@ -648,7 +655,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:
@@ -713,7 +720,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.
@@ -736,7 +743,13 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
wireChild(n)
t := n.child[0].typ
switch t.cat {
case aliasT, ptrT:
case aliasT:
if isString(t.val.TypeOf()) {
n.typ = sc.getType("byte")
break
}
fallthrough
case ptrT:
n.typ = t.val
if t.val.cat == valueT {
n.typ = &itype{cat: valueT, rtype: t.val.rtype.Elem()}
@@ -854,7 +867,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.
@@ -896,7 +909,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))
@@ -959,7 +972,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
}
} else {
n.findex = -1
n.findex = notInFrame
}
}
@@ -1040,7 +1053,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]
@@ -1498,6 +1511,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
@@ -1512,7 +1528,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
@@ -1825,7 +1841,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
@@ -1852,6 +1868,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}
@@ -1878,8 +1895,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)})
}
@@ -1903,7 +1927,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
@@ -2473,6 +2497,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 {
@@ -318,7 +319,50 @@ func (interp *Interpreter) gtaRetry(nodes []*node, importPath string) error {
}
if len(revisit) > 0 {
return revisit[0].cfgErrorf("constant definition loop")
n := revisit[0]
if n.kind == typeSpec {
if err := definedType(n.typ); err != nil {
return err
}
}
return n.cfgErrorf("constant definition loop")
}
return nil
}
func definedType(typ *itype) error {
if !typ.incomplete {
return nil
}
switch typ.cat {
case interfaceT, structT:
for _, f := range typ.field {
if err := definedType(f.typ); err != nil {
return err
}
}
case funcT:
for _, t := range typ.arg {
if err := definedType(t); err != nil {
return err
}
}
for _, t := range typ.ret {
if err := definedType(t); err != nil {
return err
}
}
case mapT:
if err := definedType(typ.key); err != nil {
return err
}
fallthrough
case aliasT, arrayT, chanT, chanSendT, chanRecvT, ptrT, variadicT:
if err := definedType(typ.val); err != nil {
return err
}
case nilT:
return typ.node.cfgErrorf("undefined: %s", typ.node.ident)
}
return nil
}

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

@@ -457,6 +457,7 @@ func TestEvalSliceExpression(t *testing.T) {
{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"},
{pre: func() { eval(t, i, `type Str = string; var r Str = "truc"`) }, src: `r[1]`, res: "114"},
})
}

View File

@@ -253,7 +253,7 @@ func andConst(n *node) {
n.rval = reflect.New(t).Elem()
switch {
case isConst:
v := constant.BinaryOp(vConstantValue(v0), token.AND, vConstantValue(v1))
v := constant.BinaryOp(constant.ToInt(vConstantValue(v0)), token.AND, constant.ToInt(vConstantValue(v1)))
n.rval.Set(reflect.ValueOf(v))
case isUint(t):
n.rval.SetUint(vUint(v0) & vUint(v1))
@@ -338,7 +338,7 @@ func andNotConst(n *node) {
n.rval = reflect.New(t).Elem()
switch {
case isConst:
v := constant.BinaryOp(vConstantValue(v0), token.AND_NOT, vConstantValue(v1))
v := constant.BinaryOp(constant.ToInt(vConstantValue(v0)), token.AND_NOT, constant.ToInt(vConstantValue(v1)))
n.rval.Set(reflect.ValueOf(v))
case isUint(t):
n.rval.SetUint(vUint(v0) &^ vUint(v1))
@@ -564,7 +564,7 @@ func orConst(n *node) {
n.rval = reflect.New(t).Elem()
switch {
case isConst:
v := constant.BinaryOp(vConstantValue(v0), token.OR, vConstantValue(v1))
v := constant.BinaryOp(constant.ToInt(vConstantValue(v0)), token.OR, constant.ToInt(vConstantValue(v1)))
n.rval.Set(reflect.ValueOf(v))
case isUint(t):
n.rval.SetUint(vUint(v0) | vUint(v1))
@@ -799,7 +799,7 @@ func remConst(n *node) {
n.rval = reflect.New(t).Elem()
switch {
case isConst:
v := constant.BinaryOp(vConstantValue(v0), token.REM, vConstantValue(v1))
v := constant.BinaryOp(constant.ToInt(vConstantValue(v0)), token.REM, constant.ToInt(vConstantValue(v1)))
n.rval.Set(reflect.ValueOf(v))
case isUint(t):
n.rval.SetUint(vUint(v0) % vUint(v1))
@@ -1195,7 +1195,7 @@ func xorConst(n *node) {
n.rval = reflect.New(t).Elem()
switch {
case isConst:
v := constant.BinaryOp(vConstantValue(v0), token.XOR, vConstantValue(v1))
v := constant.BinaryOp(constant.ToInt(vConstantValue(v0)), token.XOR, constant.ToInt(vConstantValue(v1)))
n.rval.Set(reflect.ValueOf(v))
case isUint(t):
n.rval.SetUint(vUint(v0) ^ vUint(v1))

View File

@@ -69,7 +69,7 @@ var builtin = [...]bltnGenerator{
aStar: deref,
aSub: sub,
aSubAssign: subAssign,
aTypeAssert: typeAssert1,
aTypeAssert: typeAssertShort,
aXor: xor,
aXorAssign: xorAssign,
}
@@ -191,45 +191,6 @@ 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 {
@@ -241,25 +202,33 @@ func stripReceiverFromArgs(signature string) (string, error) {
return fmt.Sprintf("func(%s", fields[4]), nil
}
func typeAssert1(n *node) {
typeAssert(n, false)
func typeAssertShort(n *node) {
typeAssert(n, true, false)
}
func typeAssert2(n *node) {
typeAssert(n, true)
func typeAssertLong(n *node) {
typeAssert(n, true, true)
}
func typeAssert(n *node, withOk bool) {
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
var value0, value1 func(*frame) reflect.Value
setStatus := false
if withOk {
switch {
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 "_"
} else {
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 "_"
}
typ := c1.typ // type to assert or convert to
@@ -270,7 +239,8 @@ func typeAssert(n *node, withOk bool) {
switch {
case isInterfaceSrc(typ):
n.exec = func(f *frame) bltn {
v, ok := value(f).Interface().(valueInterface)
valf := value(f)
v, ok := valf.Interface().(valueInterface)
if setStatus {
defer func() {
value1(f).SetBool(ok)
@@ -283,7 +253,9 @@ func typeAssert(n *node, withOk bool) {
return next
}
if v.node.typ.id() == typID {
value0(f).Set(value(f))
if withResult {
value0(f).Set(valf)
}
return next
}
m0 := v.node.typ.methods()
@@ -329,7 +301,9 @@ func typeAssert(n *node, withOk bool) {
}
}
value0(f).Set(value(f))
if withResult {
value0(f).Set(valf)
}
return next
}
case isInterface(typ):
@@ -362,9 +336,9 @@ func typeAssert(n *node, withOk bool) {
}
}
// TODO(mpl): make this case compliant with reflect's Implements.
v = genInterfaceWrapper(val.node, rtype)(f)
value0(f).Set(v)
if withResult {
value0(f).Set(genInterfaceWrapper(val.node, rtype)(f))
}
ok = true
return next
}
@@ -392,7 +366,9 @@ func typeAssert(n *node, withOk bool) {
}
return next
}
value0(f).Set(v)
if withResult {
value0(f).Set(v)
}
return next
}
case n.child[0].typ.cat == valueT || n.child[0].typ.cat == errorT:
@@ -410,6 +386,7 @@ func typeAssert(n *node, withOk bool) {
}
return next
}
v = valueInterfaceValue(v)
ok = canAssertTypes(v.Type(), rtype)
if !ok {
if !withOk {
@@ -418,7 +395,9 @@ func typeAssert(n *node, withOk bool) {
}
return next
}
value0(f).Set(v)
if withResult {
value0(f).Set(v)
}
return next
}
default:
@@ -436,14 +415,23 @@ func typeAssert(n *node, withOk bool) {
}
return next
}
ok = canAssertTypes(v.value.Type(), rtype)
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
}
value0(f).Set(v.value)
if withResult {
value0(f).Set(v.value)
}
return next
}
}
@@ -907,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
})
@@ -1238,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:
@@ -1291,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() {
@@ -1391,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 {
@@ -1734,6 +1723,13 @@ 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
@@ -2166,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
}
}
@@ -2233,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

@@ -130,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 {

View File

@@ -109,7 +109,7 @@ type itype struct {
cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil
val *itype // Type of value element if chanT,chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT or variadicT
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT or variadicT
recv *itype // Receiver type for funcT or nil
arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil
@@ -449,6 +449,9 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
}
t = sym.typ
if t.incomplete && t.cat == aliasT && t.val != nil && t.val.cat != nilT {
t.incomplete = false
}
if t.incomplete && t.node != n {
m := t.method
if t, err = nodeType(interp, sc, t.node); err != nil {
@@ -879,13 +882,19 @@ func isComplete(t *itype, visited map[string]bool) bool {
}
name := t.path + "/" + t.name
if visited[name] {
return !t.incomplete
return true
}
if t.name != "" {
visited[name] = true
}
switch t.cat {
case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT:
case aliasT:
if t.val != nil && t.val.cat != nilT {
// A type aliased to a partially defined type is considered complete, to allow recursivity.
return true
}
fallthrough
case arrayT, chanT, chanRecvT, chanSendT, ptrT:
return isComplete(t.val, visited)
case funcT:
complete := true
@@ -899,6 +908,8 @@ func isComplete(t *itype, visited map[string]bool) bool {
case interfaceT, structT:
complete := true
for _, f := range t.field {
// Field implicit type names must be marked as visited, to break false circles.
visited[f.typ.path+"/"+f.typ.name] = true
complete = complete && isComplete(f.typ, visited)
}
return complete
@@ -1540,7 +1551,7 @@ func hasRecursiveStruct(t *itype, defined map[string]*itype) bool {
defined[typ.path+"/"+typ.name] = typ
for _, f := range typ.field {
if hasRecursiveStruct(f.typ, defined) {
if hasRecursiveStruct(f.typ, copyDefined(defined)) {
return true
}
}

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:
@@ -171,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
@@ -297,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)
@@ -307,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)
}
}