Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
563270ca02 | ||
|
|
5eecbe515b | ||
|
|
0a79069dfc | ||
|
|
0c8f538cd9 | ||
|
|
ca80ada849 | ||
|
|
3c6df504df | ||
|
|
98eacf3610 | ||
|
|
16ff52a949 | ||
|
|
640d1429e5 | ||
|
|
659913eebe | ||
|
|
b3766509cc | ||
|
|
bc2b224bae | ||
|
|
9d4685deea | ||
|
|
2a70a71dc2 | ||
|
|
851444453c | ||
|
|
a8b1c2a017 | ||
|
|
d229c2a2c7 |
14
_test/comp2.go
Normal file
14
_test/comp2.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
type delta int32
|
||||
|
||||
func main() {
|
||||
a := delta(-1)
|
||||
|
||||
println(a != -1)
|
||||
println(a == -1)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// true
|
||||
15
_test/composite11.go
Normal file
15
_test/composite11.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := color.NRGBA64{1, 1, 1, 1}
|
||||
|
||||
fmt.Println(c)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {1 1 1 1}
|
||||
17
_test/const15.go
Normal file
17
_test/const15.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
type T1 t1
|
||||
|
||||
type t1 int8
|
||||
|
||||
const (
|
||||
P2 T1 = 2
|
||||
P3 T1 = 3
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(P3)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
12
_test/init1.go
Normal file
12
_test/init1.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func init() {
|
||||
println("here")
|
||||
}
|
||||
|
||||
func main() {
|
||||
init()
|
||||
}
|
||||
|
||||
// Error:
|
||||
// _test/init1.go:8:2: undefined: init
|
||||
19
_test/interface44.go
Normal file
19
_test/interface44.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
type S struct {
|
||||
a int
|
||||
}
|
||||
|
||||
func main() {
|
||||
var i interface{} = S{a: 1}
|
||||
|
||||
s, ok := i.(S)
|
||||
if !ok {
|
||||
println("bad")
|
||||
return
|
||||
}
|
||||
println(s.a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
13
_test/interface45.go
Normal file
13
_test/interface45.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var i interface{} = 1
|
||||
var s struct{}
|
||||
s, _ = i.(struct{})
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {}
|
||||
31
_test/issue-772.go
Normal file
31
_test/issue-772.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Data string
|
||||
}
|
||||
|
||||
func main() {
|
||||
tmpl := template.New("name")
|
||||
|
||||
_, err := tmpl.Parse("{{.Data}}")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = tmpl.Execute(os.Stdout, Message{
|
||||
Data: "Hello, World!!",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello, World!!
|
||||
18
_test/issue-775.go
Normal file
18
_test/issue-775.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
func main() {
|
||||
recorder := httptest.NewRecorder()
|
||||
recorder.Header().Add("Foo", "Bar")
|
||||
|
||||
for key, value := range recorder.Header() {
|
||||
fmt.Println(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Foo [Bar]
|
||||
39
_test/issue-776.go
Normal file
39
_test/issue-776.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Filter is a filter
|
||||
type Filter interface {
|
||||
Foo()
|
||||
}
|
||||
|
||||
// GIFT is a gift
|
||||
type GIFT struct {
|
||||
Filters []Filter
|
||||
}
|
||||
|
||||
// New is a new filter list
|
||||
func New(filters ...Filter) *GIFT {
|
||||
return &GIFT{
|
||||
Filters: filters,
|
||||
}
|
||||
}
|
||||
|
||||
// List lists filters
|
||||
func (g *GIFT) List() {
|
||||
fmt.Printf("Hello from List!\n")
|
||||
}
|
||||
|
||||
// MyFilter is one of the filters
|
||||
type MyFilter struct{}
|
||||
|
||||
// Foo is a foo
|
||||
func (f *MyFilter) Foo() {}
|
||||
|
||||
func main() {
|
||||
g := New(&MyFilter{})
|
||||
g.List()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello from List!
|
||||
12
_test/math2.go
Normal file
12
_test/math2.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
const c uint64 = 2
|
||||
|
||||
func main() {
|
||||
if c&(1<<(uint64(1))) > 0 {
|
||||
println("yes")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// yes
|
||||
18
_test/restricted0.go
Normal file
18
_test/restricted0.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
fmt.Println("recover:", r)
|
||||
}()
|
||||
log.Fatal("log.Fatal does not exit")
|
||||
println("not printed")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// recover: log.Fatal does not exit
|
||||
18
_test/restricted1.go
Normal file
18
_test/restricted1.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
fmt.Println("recover:", r)
|
||||
}()
|
||||
os.Exit(1)
|
||||
println("not printed")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// recover: os.Exit(1)
|
||||
14
_test/restricted2.go
Normal file
14
_test/restricted2.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
p, err := os.FindProcess(os.Getpid())
|
||||
fmt.Println(p, err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil> restricted
|
||||
23
_test/restricted3.go
Normal file
23
_test/restricted3.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
var (
|
||||
buf bytes.Buffer
|
||||
logger = log.New(&buf, "logger: ", log.Lshortfile)
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
fmt.Println("recover:", r, buf.String())
|
||||
}()
|
||||
logger.Fatal("test log")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// recover: test log logger: restricted.go:39: test log
|
||||
23
_test/struct53.go
Normal file
23
_test/struct53.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type T1 struct {
|
||||
P []*T
|
||||
}
|
||||
|
||||
type T2 struct {
|
||||
P2 *T
|
||||
}
|
||||
|
||||
type T struct {
|
||||
*T1
|
||||
S1 *T
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(T2{})
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {<nil>}
|
||||
26
_test/struct54.go
Normal file
26
_test/struct54.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
type S struct {
|
||||
t *T
|
||||
}
|
||||
|
||||
func newS() *S {
|
||||
return &S{
|
||||
t: &T{u: map[string]*U{}},
|
||||
}
|
||||
}
|
||||
|
||||
type T struct {
|
||||
u map[string]*U
|
||||
}
|
||||
|
||||
type U struct {
|
||||
a int
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := newS()
|
||||
_ = s
|
||||
|
||||
println("ok")
|
||||
}
|
||||
44
_test/type25.go
Normal file
44
_test/type25.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type wrappedError struct {
|
||||
wrapped error
|
||||
}
|
||||
|
||||
func (e wrappedError) Error() string {
|
||||
return "some outer error"
|
||||
}
|
||||
|
||||
func (e wrappedError) Unwrap() error {
|
||||
return e.wrapped
|
||||
}
|
||||
|
||||
var err atomic.Value
|
||||
|
||||
func getWrapped() *wrappedError {
|
||||
if v := err.Load(); v != nil {
|
||||
err := v.(wrappedError)
|
||||
if err.wrapped != nil {
|
||||
return &err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
err.Store(wrappedError{wrapped: errors.New("test")})
|
||||
|
||||
e := getWrapped()
|
||||
if e != nil {
|
||||
println(e.Error())
|
||||
println(e.wrapped.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// some outer error
|
||||
// test
|
||||
44
_test/type26.go
Normal file
44
_test/type26.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type wrappedError struct {
|
||||
wrapped error
|
||||
}
|
||||
|
||||
func (e *wrappedError) Error() string {
|
||||
return "some outer error"
|
||||
}
|
||||
|
||||
func (e *wrappedError) Unwrap() error {
|
||||
return e.wrapped
|
||||
}
|
||||
|
||||
var err atomic.Value
|
||||
|
||||
func getWrapped() *wrappedError {
|
||||
if v := err.Load(); v != nil {
|
||||
err := v.(*wrappedError)
|
||||
if err.wrapped != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
err.Store(&wrappedError{wrapped: errors.New("test")})
|
||||
|
||||
e := getWrapped()
|
||||
if e != nil {
|
||||
println(e.Error())
|
||||
println(e.wrapped.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// some outer error
|
||||
// test
|
||||
13
_test/var12.go
Normal file
13
_test/var12.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
var (
|
||||
a = b
|
||||
b = "hello"
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
23
_test/var13.go
Normal file
23
_test/var13.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
var (
|
||||
a = concat("hello", b)
|
||||
b = concat(" ", c, "!")
|
||||
c = d
|
||||
d = "world"
|
||||
)
|
||||
|
||||
func concat(a ...string) string {
|
||||
var s string
|
||||
for _, ss := range a {
|
||||
s += ss
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello world!
|
||||
10
_test/var14.go
Normal file
10
_test/var14.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "github.com/containous/yaegi/_test/vars"
|
||||
|
||||
func main() {
|
||||
println(vars.A)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello world!
|
||||
14
_test/vars/first.go
Normal file
14
_test/vars/first.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package vars
|
||||
|
||||
var (
|
||||
A = concat("hello", B)
|
||||
C = D
|
||||
)
|
||||
|
||||
func concat(a ...string) string {
|
||||
var s string
|
||||
for _, ss := range a {
|
||||
s += ss
|
||||
}
|
||||
return s
|
||||
}
|
||||
6
_test/vars/second.go
Normal file
6
_test/vars/second.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package vars
|
||||
|
||||
var (
|
||||
B = concat(" ", C, "!")
|
||||
D = "world"
|
||||
)
|
||||
@@ -115,6 +115,17 @@ type Wrap struct {
|
||||
Method []Method
|
||||
}
|
||||
|
||||
// restricted map defines symbols for which a special implementation is provided.
|
||||
var restricted = map[string]bool{
|
||||
"osExit": true,
|
||||
"osFindProcess": true,
|
||||
"logFatal": true,
|
||||
"logFatalf": true,
|
||||
"logFatalln": true,
|
||||
"logLogger": true,
|
||||
"logNew": true,
|
||||
}
|
||||
|
||||
func genContent(dest, pkgName, license string, skip map[string]bool) ([]byte, error) {
|
||||
p, err := importer.ForCompiler(token.NewFileSet(), "source", nil).Import(pkgName)
|
||||
if err != nil {
|
||||
@@ -150,6 +161,10 @@ func genContent(dest, pkgName, license string, skip map[string]bool) ([]byte, er
|
||||
if skip[pname] {
|
||||
continue
|
||||
}
|
||||
if rname := path.Base(pkgName) + name; restricted[rname] {
|
||||
// Restricted symbol, locally provided by stdlib wrapper.
|
||||
pname = rname
|
||||
}
|
||||
|
||||
switch o := o.(type) {
|
||||
case *types.Const:
|
||||
|
||||
@@ -96,17 +96,20 @@ import (
|
||||
"github.com/containous/yaegi/interp"
|
||||
"github.com/containous/yaegi/stdlib"
|
||||
"github.com/containous/yaegi/stdlib/syscall"
|
||||
"github.com/containous/yaegi/stdlib/unrestricted"
|
||||
"github.com/containous/yaegi/stdlib/unsafe"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var interactive bool
|
||||
var useUnsafe bool
|
||||
var useSyscall bool
|
||||
var useUnrestricted bool
|
||||
var useUnsafe bool
|
||||
var tags string
|
||||
var cmd string
|
||||
flag.BoolVar(&interactive, "i", false, "start an interactive REPL")
|
||||
flag.BoolVar(&useSyscall, "syscall", false, "include syscall symbols")
|
||||
flag.BoolVar(&useUnrestricted, "unrestricted", false, "include unrestricted symbols")
|
||||
flag.StringVar(&tags, "tags", "", "set a list of build tags")
|
||||
flag.BoolVar(&useUnsafe, "unsafe", false, "include usafe symbols")
|
||||
flag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
|
||||
@@ -128,6 +131,10 @@ func main() {
|
||||
if useUnsafe {
|
||||
i.Use(unsafe.Symbols)
|
||||
}
|
||||
if useUnrestricted {
|
||||
// Use of unrestricted symbols should always follow use of stdlib symbols, to update them.
|
||||
i.Use(unrestricted.Symbols)
|
||||
}
|
||||
|
||||
if cmd != `` {
|
||||
i.REPL(strings.NewReader(cmd), os.Stderr)
|
||||
|
||||
11
example/pkg/_pkg12/src/guthib.com/foo/main.go
Normal file
11
example/pkg/_pkg12/src/guthib.com/foo/main.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"guthib.com/foo/pkg"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%s", pkg.NewSample()())
|
||||
}
|
||||
17
example/pkg/_pkg12/src/guthib.com/foo/pkg/pkg.go
Normal file
17
example/pkg/_pkg12/src/guthib.com/foo/pkg/pkg.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"guthib.com/bar"
|
||||
)
|
||||
|
||||
func Here() string {
|
||||
return "hello"
|
||||
}
|
||||
|
||||
func NewSample() func() string {
|
||||
return func() string {
|
||||
return fmt.Sprintf("%s %s", bar.Bar(), Here())
|
||||
}
|
||||
}
|
||||
6
example/pkg/_pkg12/src/guthib.com/foo/vendor/guthib.com/bar/bar.go
generated
vendored
Normal file
6
example/pkg/_pkg12/src/guthib.com/foo/vendor/guthib.com/bar/bar.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package bar
|
||||
|
||||
// Bar is bar
|
||||
func Bar() string {
|
||||
return "Yo"
|
||||
}
|
||||
@@ -83,6 +83,18 @@ func TestPackages(t *testing.T) {
|
||||
expected: "Fromage",
|
||||
evalFile: "./_pkg11/src/foo/foo.go",
|
||||
},
|
||||
{
|
||||
desc: "vendor dir is a sibling or an uncle",
|
||||
goPath: "./_pkg12/",
|
||||
expected: "Yo hello",
|
||||
topImport: "guthib.com/foo/pkg",
|
||||
},
|
||||
{
|
||||
desc: "eval main with vendor as a sibling",
|
||||
goPath: "./_pkg12/",
|
||||
expected: "Yo hello",
|
||||
evalFile: "./_pkg12/src/guthib.com/foo/main.go",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -116,7 +128,7 @@ func TestPackages(t *testing.T) {
|
||||
os.Stdout = pw
|
||||
|
||||
if _, err := i.Eval(string(data)); err != nil {
|
||||
t.Fatal(err)
|
||||
fatalStderrf(t, "%v", err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
@@ -127,10 +139,10 @@ func TestPackages(t *testing.T) {
|
||||
}()
|
||||
|
||||
if err := pw.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
fatalStderrf(t, "%v", err)
|
||||
}
|
||||
if err := <-errC; err != nil {
|
||||
t.Fatal(err)
|
||||
fatalStderrf(t, "%v", err)
|
||||
}
|
||||
msg = buf.String()
|
||||
} else {
|
||||
@@ -153,12 +165,17 @@ func TestPackages(t *testing.T) {
|
||||
}
|
||||
|
||||
if msg != test.expected {
|
||||
t.Errorf("Got %q, want %q", msg, test.expected)
|
||||
fatalStderrf(t, "Got %q, want %q", msg, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func fatalStderrf(t *testing.T, format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
func TestPackagesError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
||||
@@ -412,6 +412,8 @@ func {{$name}}(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
{{- if or (eq $op.Name "==") (eq $op.Name "!=") }}
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
@@ -432,7 +434,7 @@ func {{$name}}(n *node) {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
dest(f).SetBool(i0 {{$op.Name}} i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -454,7 +456,7 @@ func {{$name}}(n *node) {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
dest(f).SetBool(i0 {{$op.Name}} i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -478,13 +480,14 @@ func {{$name}}(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
dest(f).SetBool(i0 {{$op.Name}} i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
|
||||
177
interp/cfg.go
177
interp/cfg.go
@@ -476,6 +476,9 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
dest.typ = sc.fixType(src.typ)
|
||||
}
|
||||
}
|
||||
if dest.typ.incomplete {
|
||||
return
|
||||
}
|
||||
if dest.typ.sizedef {
|
||||
dest.typ.size = arrayTypeLen(src)
|
||||
dest.typ.rtype = nil
|
||||
@@ -696,6 +699,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if !isShiftNode(n) && !isConstVal(c) && !c0.typ.equals(c1.typ) && t0 != nil && t1 != nil {
|
||||
switch {
|
||||
case isConstVal(c0) && isNumber(t1) || isConstVal(c1) && isNumber(t0): // const <-> numberic case
|
||||
case isNumber(t0) && isNumber(t1) && (c0.typ.untyped || c1.typ.untyped):
|
||||
case t0.Kind() == reflect.Uint8 && t1.Kind() == reflect.Int32 || t1.Kind() == reflect.Uint8 && t0.Kind() == reflect.Int32: // byte <-> rune case
|
||||
case isInterface(c0.typ) && isInterface(c1.typ): // interface <-> interface case
|
||||
default:
|
||||
@@ -732,6 +736,18 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.typ = c0.typ
|
||||
case aEqual, aNotEqual:
|
||||
n.typ = sc.getType("bool")
|
||||
if isConstVal(c0) && !isConstVal(c1) || !isConstVal(c0) && isConstVal(c1) {
|
||||
// if either node is a constant value, but the other is not, the constant
|
||||
// must be converted into the non-constants type.
|
||||
switch {
|
||||
case isConstVal(c0):
|
||||
convertConstantValue(c0)
|
||||
c0.rval = c0.rval.Convert(c1.typ.TypeOf())
|
||||
default:
|
||||
convertConstantValue(c1)
|
||||
c1.rval = c1.rval.Convert(c0.typ.TypeOf())
|
||||
}
|
||||
}
|
||||
if n.child[0].sym == nilSym || n.child[1].sym == nilSym {
|
||||
if n.action == aEqual {
|
||||
n.gen = isNil
|
||||
@@ -830,7 +846,15 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
sc = sc.pop()
|
||||
|
||||
case constDecl, varDecl:
|
||||
case constDecl:
|
||||
wireChild(n)
|
||||
|
||||
case varDecl:
|
||||
// Global varDecl do not need to be wired as this
|
||||
// will be handled after cfg.
|
||||
if n.anc.kind == fileStmt {
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
|
||||
case declStmt, exprStmt, sendStmt:
|
||||
@@ -1018,7 +1042,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
|
||||
case fileStmt:
|
||||
wireChild(n)
|
||||
wireChild(n, varDecl)
|
||||
sc = sc.pop()
|
||||
n.findex = -1
|
||||
|
||||
@@ -1130,11 +1154,11 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.types = sc.types
|
||||
sc = sc.pop()
|
||||
funcName := n.child[1].ident
|
||||
if !isMethod(n) {
|
||||
interp.scopes[pkgID].sym[funcName].index = -1 // to force value to n.val
|
||||
interp.scopes[pkgID].sym[funcName].typ = n.typ
|
||||
interp.scopes[pkgID].sym[funcName].kind = funcSym
|
||||
interp.scopes[pkgID].sym[funcName].node = n
|
||||
if sym := sc.sym[funcName]; !isMethod(n) && sym != nil {
|
||||
sym.index = -1 // to force value to n.val
|
||||
sym.typ = n.typ
|
||||
sym.kind = funcSym
|
||||
sym.node = n
|
||||
}
|
||||
|
||||
case funcLit:
|
||||
@@ -1149,6 +1173,10 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if isKey(n) || isNewDefine(n, sc) {
|
||||
break
|
||||
}
|
||||
if n.anc.kind == funcDecl && n.anc.child[1] == n {
|
||||
// Dont process a function name identExpr.
|
||||
break
|
||||
}
|
||||
|
||||
sym, level, found := sc.lookup(n.ident)
|
||||
if !found {
|
||||
@@ -1306,7 +1334,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
|
||||
case rangeStmt:
|
||||
if sc.rangeChanType(n) != nil {
|
||||
n.start = n.child[1] // Get chan
|
||||
n.start = n.child[1].start // Get chan
|
||||
n.child[1].tnext = n // then go to range function
|
||||
n.tnext = n.child[2].start // then go to range body
|
||||
n.child[2].tnext = n // then body go to range function (loop)
|
||||
@@ -1318,7 +1346,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
} else {
|
||||
k, o, body = n.child[0], n.child[1], n.child[2]
|
||||
}
|
||||
n.start = o // Get array or map object
|
||||
n.start = o.start // Get array or map object
|
||||
o.tnext = k.start // then go to iterator init
|
||||
k.tnext = n // then go to range function
|
||||
n.tnext = body.start // then go to range body
|
||||
@@ -1703,7 +1731,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
}
|
||||
if n.anc.action != aAssignX {
|
||||
if n.child[0].typ.cat == valueT {
|
||||
if n.child[0].typ.cat == valueT && !isStruct(n.child[1].typ) {
|
||||
// Avoid special wrapping of interfaces and func types.
|
||||
n.typ = &itype{cat: valueT, rtype: n.child[1].typ.TypeOf()}
|
||||
} else {
|
||||
@@ -1926,6 +1954,90 @@ func genRun(nod *node) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func genGlobalVars(roots []*node, sc *scope) (*node, error) {
|
||||
var vars []*node
|
||||
for _, n := range roots {
|
||||
vars = append(vars, getVars(n)...)
|
||||
}
|
||||
|
||||
if len(vars) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
varNode, err := genGlobalVarDecl(vars, sc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setExec(varNode.start)
|
||||
return varNode, nil
|
||||
}
|
||||
|
||||
func getVars(n *node) (vars []*node) {
|
||||
for _, child := range n.child {
|
||||
if child.kind == varDecl {
|
||||
vars = append(vars, child.child...)
|
||||
}
|
||||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
func genGlobalVarDecl(nodes []*node, sc *scope) (*node, error) {
|
||||
varNode := &node{kind: varDecl, action: aNop, gen: nop}
|
||||
|
||||
deps := map[*node][]*node{}
|
||||
for _, n := range nodes {
|
||||
deps[n] = getVarDependencies(n, sc)
|
||||
}
|
||||
|
||||
inited := map[*node]bool{}
|
||||
revisit := []*node{}
|
||||
for {
|
||||
for _, n := range nodes {
|
||||
canInit := true
|
||||
for _, d := range deps[n] {
|
||||
if !inited[d] {
|
||||
canInit = false
|
||||
}
|
||||
}
|
||||
if !canInit {
|
||||
revisit = append(revisit, n)
|
||||
continue
|
||||
}
|
||||
|
||||
varNode.child = append(varNode.child, n)
|
||||
inited[n] = true
|
||||
}
|
||||
|
||||
if len(revisit) == 0 || equalNodes(nodes, revisit) {
|
||||
break
|
||||
}
|
||||
|
||||
nodes = revisit
|
||||
revisit = []*node{}
|
||||
}
|
||||
|
||||
if len(revisit) > 0 {
|
||||
return nil, revisit[0].cfgErrorf("variable definition loop")
|
||||
}
|
||||
wireChild(varNode)
|
||||
return varNode, nil
|
||||
}
|
||||
|
||||
func getVarDependencies(nod *node, sc *scope) (deps []*node) {
|
||||
nod.Walk(func(n *node) bool {
|
||||
if n.kind == identExpr {
|
||||
if sym, _, ok := sc.lookup(n.ident); ok {
|
||||
if sym.kind != varSym || !sym.global || sym.node == nod {
|
||||
return false
|
||||
}
|
||||
deps = append(deps, sym.node)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}, nil)
|
||||
return deps
|
||||
}
|
||||
|
||||
// setFnext sets the cond fnext field to next, propagates it for parenthesis blocks
|
||||
// and sets the action to branch.
|
||||
func setFNext(cond, next *node) {
|
||||
@@ -1985,47 +2097,68 @@ func (n *node) isType(sc *scope) bool {
|
||||
}
|
||||
|
||||
// wireChild wires AST nodes for CFG in subtree.
|
||||
func wireChild(n *node) {
|
||||
func wireChild(n *node, exclude ...nkind) {
|
||||
child := excludeNodeKind(n.child, exclude)
|
||||
|
||||
// Set start node, in subtree (propagated to ancestors by post-order processing)
|
||||
for _, child := range n.child {
|
||||
switch child.kind {
|
||||
for _, c := range child {
|
||||
switch c.kind {
|
||||
case arrayType, chanType, chanTypeRecv, chanTypeSend, funcDecl, importDecl, mapType, basicLit, identExpr, typeDecl:
|
||||
continue
|
||||
default:
|
||||
n.start = child.start
|
||||
n.start = c.start
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Chain sequential operations inside a block (next is right sibling)
|
||||
for i := 1; i < len(n.child); i++ {
|
||||
switch n.child[i].kind {
|
||||
for i := 1; i < len(child); i++ {
|
||||
switch child[i].kind {
|
||||
case funcDecl:
|
||||
n.child[i-1].tnext = n.child[i]
|
||||
child[i-1].tnext = child[i]
|
||||
default:
|
||||
switch n.child[i-1].kind {
|
||||
switch child[i-1].kind {
|
||||
case breakStmt, continueStmt, gotoStmt, returnStmt:
|
||||
// tnext is already computed, no change
|
||||
default:
|
||||
n.child[i-1].tnext = n.child[i].start
|
||||
child[i-1].tnext = child[i].start
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chain subtree next to self
|
||||
for i := len(n.child) - 1; i >= 0; i-- {
|
||||
switch n.child[i].kind {
|
||||
for i := len(child) - 1; i >= 0; i-- {
|
||||
switch child[i].kind {
|
||||
case arrayType, chanType, chanTypeRecv, chanTypeSend, importDecl, mapType, funcDecl, basicLit, identExpr, typeDecl:
|
||||
continue
|
||||
case breakStmt, continueStmt, gotoStmt, returnStmt:
|
||||
// tnext is already computed, no change
|
||||
default:
|
||||
n.child[i].tnext = n
|
||||
child[i].tnext = n
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func excludeNodeKind(child []*node, kinds []nkind) []*node {
|
||||
if len(kinds) == 0 {
|
||||
return child
|
||||
}
|
||||
var res []*node
|
||||
for _, c := range child {
|
||||
exclude := false
|
||||
for _, k := range kinds {
|
||||
if c.kind == k {
|
||||
exclude = true
|
||||
}
|
||||
}
|
||||
if !exclude {
|
||||
res = append(res, c)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (n *node) name() (s string) {
|
||||
switch {
|
||||
case n.ident != "":
|
||||
|
||||
@@ -78,8 +78,8 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
if typ.isBinMethod {
|
||||
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true, scope: sc}
|
||||
}
|
||||
if sc.sym[dest.ident] == nil {
|
||||
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val}
|
||||
if sc.sym[dest.ident] == nil || sc.sym[dest.ident].typ.incomplete {
|
||||
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val, node: n}
|
||||
}
|
||||
if n.anc.kind == constDecl {
|
||||
sc.sym[dest.ident].kind = constSym
|
||||
@@ -112,7 +112,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
sym1, exists1 := sc.sym[asImportName]
|
||||
sym2, exists2 := sc.sym[c.ident]
|
||||
if !exists1 && !exists2 {
|
||||
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ}
|
||||
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ, node: n}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -138,11 +138,13 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
if n.typ, err = nodeType(interp, sc, n.child[2]); err != nil {
|
||||
return false
|
||||
}
|
||||
if isMethod(n) {
|
||||
ident := n.child[1].ident
|
||||
switch {
|
||||
case isMethod(n):
|
||||
// TODO(mpl): redeclaration detection
|
||||
// Add a method symbol in the receiver type name space
|
||||
var rcvrtype *itype
|
||||
n.ident = n.child[1].ident
|
||||
n.ident = ident
|
||||
rcvr := n.child[0].child[0]
|
||||
rtn := rcvr.lastChild()
|
||||
typeName := rtn.ident
|
||||
@@ -167,33 +169,32 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
}
|
||||
rcvrtype.method = append(rcvrtype.method, n)
|
||||
n.child[0].child[0].lastChild().typ = rcvrtype
|
||||
} else {
|
||||
ident := n.child[1].ident
|
||||
case ident == "init":
|
||||
// TODO(mpl): use constant instead of hardcoded string?
|
||||
if ident != "init" {
|
||||
asImportName := filepath.Join(ident, baseName)
|
||||
if _, exists := sc.sym[asImportName]; exists {
|
||||
// redeclaration error
|
||||
// TODO(mpl): improve error with position of previous declaration.
|
||||
err = n.cfgErrorf("%s redeclared in this block", ident)
|
||||
return false
|
||||
}
|
||||
sym, exists := sc.sym[ident]
|
||||
if exists {
|
||||
// Make sure the symbol we found seems to be about another node, before calling
|
||||
// it a redeclaration.
|
||||
if sym.typ.isComplete() {
|
||||
// TODO(mpl): this check might be too permissive?
|
||||
if sym.kind != funcSym || sym.typ.cat != n.typ.cat || sym.node != n || sym.index != -1 {
|
||||
// redeclaration error
|
||||
// TODO(mpl): improve error with position of previous declaration.
|
||||
err = n.cfgErrorf("%s redeclared in this block", ident)
|
||||
return false
|
||||
}
|
||||
// init functions do not get declared as per the Go spec.
|
||||
default:
|
||||
asImportName := filepath.Join(ident, baseName)
|
||||
if _, exists := sc.sym[asImportName]; exists {
|
||||
// redeclaration error
|
||||
// TODO(mpl): improve error with position of previous declaration.
|
||||
err = n.cfgErrorf("%s redeclared in this block", ident)
|
||||
return false
|
||||
}
|
||||
sym, exists := sc.sym[ident]
|
||||
if exists {
|
||||
// Make sure the symbol we found seems to be about another node, before calling
|
||||
// it a redeclaration.
|
||||
if sym.typ.isComplete() {
|
||||
// TODO(mpl): this check might be too permissive?
|
||||
if sym.kind != funcSym || sym.typ.cat != n.typ.cat || sym.node != n || sym.index != -1 {
|
||||
// redeclaration error
|
||||
// TODO(mpl): improve error with position of previous declaration.
|
||||
err = n.cfgErrorf("%s redeclared in this block", ident)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add a function symbol in the package name space
|
||||
// Add a function symbol in the package name space except for init
|
||||
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
|
||||
}
|
||||
if !n.typ.isComplete() {
|
||||
|
||||
@@ -405,6 +405,13 @@ func (interp *Interpreter) Eval(src string) (res reflect.Value, err error) {
|
||||
// Execute node closures
|
||||
interp.run(root, nil)
|
||||
|
||||
// Wire and execute global vars
|
||||
n, err := genGlobalVars([]*node{root}, interp.scopes[interp.Name])
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
interp.run(n, nil)
|
||||
|
||||
for _, n := range initNodes {
|
||||
interp.run(n, interp.frame)
|
||||
}
|
||||
@@ -474,7 +481,14 @@ func (interp *Interpreter) Use(values Exports) {
|
||||
continue
|
||||
}
|
||||
|
||||
interp.binPkg[k] = v
|
||||
if interp.binPkg[k] == nil {
|
||||
interp.binPkg[k] = v
|
||||
continue
|
||||
}
|
||||
|
||||
for s, sym := range v {
|
||||
interp.binPkg[k][s] = sym
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "fun22.go" || // expect error
|
||||
file.Name() == "if2.go" || // expect error
|
||||
file.Name() == "import6.go" || // expect error
|
||||
file.Name() == "init1.go" || // expect error
|
||||
file.Name() == "io0.go" || // use random number
|
||||
file.Name() == "op1.go" || // expect error
|
||||
file.Name() == "op7.go" || // expect error
|
||||
@@ -75,6 +76,10 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "redeclaration-global4.go" || // expect error
|
||||
file.Name() == "redeclaration-global5.go" || // expect error
|
||||
file.Name() == "redeclaration-global6.go" || // expect error
|
||||
file.Name() == "restricted0.go" || // expect error
|
||||
file.Name() == "restricted1.go" || // expect error
|
||||
file.Name() == "restricted2.go" || // expect error
|
||||
file.Name() == "restricted3.go" || // expect error
|
||||
file.Name() == "server6.go" || // syntax parsing
|
||||
file.Name() == "server5.go" || // syntax parsing
|
||||
file.Name() == "server4.go" || // syntax parsing
|
||||
|
||||
@@ -385,6 +385,25 @@ func TestEvalChan(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalFunctionCallWithFunctionParam(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
eval(t, i, `
|
||||
func Bar(s string, fn func(string)string) string { return fn(s) }
|
||||
`)
|
||||
|
||||
v := eval(t, i, "Bar")
|
||||
bar := v.Interface().(func(string, func(string) string) string)
|
||||
|
||||
got := bar("hello ", func(s string) string {
|
||||
return s + "world!"
|
||||
})
|
||||
|
||||
want := "hello world!"
|
||||
if got != want {
|
||||
t.Errorf("unexpected result of function eval: got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalMissingSymbol(t *testing.T) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
|
||||
302
interp/op.go
302
interp/op.go
@@ -2053,7 +2053,7 @@ func equal(n *node) {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
dest(f).SetBool(i0 == i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -2075,7 +2075,7 @@ func equal(n *node) {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
dest(f).SetBool(i0 == i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -2099,7 +2099,7 @@ func equal(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
dest(f).SetBool(i0 == i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -2536,80 +2536,6 @@ func greater(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
i0 := c0.rval.Interface()
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
case c1.rval.IsValid():
|
||||
i1 := c1.rval.Interface()
|
||||
v0 := genValue(c0)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
default:
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
@@ -2899,80 +2825,6 @@ func greaterEqual(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
i0 := c0.rval.Interface()
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
case c1.rval.IsValid():
|
||||
i1 := c1.rval.Interface()
|
||||
v0 := genValue(c0)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
default:
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
@@ -3262,80 +3114,6 @@ func lower(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
i0 := c0.rval.Interface()
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
case c1.rval.IsValid():
|
||||
i1 := c1.rval.Interface()
|
||||
v0 := genValue(c0)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
default:
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
@@ -3625,80 +3403,6 @@ func lowerEqual(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
i0 := c0.rval.Interface()
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
case c1.rval.IsValid():
|
||||
i1 := c1.rval.Interface()
|
||||
v0 := genValue(c0)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
default:
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
|
||||
@@ -84,6 +84,9 @@ func init() {
|
||||
}
|
||||
|
||||
func (interp *Interpreter) run(n *node, cf *frame) {
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
var f *frame
|
||||
if cf == nil {
|
||||
f = interp.frame
|
||||
@@ -230,10 +233,11 @@ func typeAssert(n *node) {
|
||||
|
||||
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
|
||||
typ := c1.typ // type to assert or convert to
|
||||
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
|
||||
typID := typ.id()
|
||||
rtype := typ.rtype // type to assert
|
||||
next := getExec(n.tnext)
|
||||
@@ -247,7 +251,9 @@ func typeAssert2(n *node) {
|
||||
} else {
|
||||
ok = false
|
||||
}
|
||||
value1(f).SetBool(ok)
|
||||
if setStatus {
|
||||
value1(f).SetBool(ok)
|
||||
}
|
||||
return next
|
||||
}
|
||||
case isInterface(typ):
|
||||
@@ -257,7 +263,9 @@ func typeAssert2(n *node) {
|
||||
if ok {
|
||||
value0(f).Set(v)
|
||||
}
|
||||
value1(f).SetBool(ok)
|
||||
if setStatus {
|
||||
value1(f).SetBool(ok)
|
||||
}
|
||||
return next
|
||||
}
|
||||
case n.child[0].typ.cat == valueT:
|
||||
@@ -267,7 +275,9 @@ func typeAssert2(n *node) {
|
||||
if ok {
|
||||
value0(f).Set(v)
|
||||
}
|
||||
value1(f).SetBool(ok)
|
||||
if setStatus {
|
||||
value1(f).SetBool(ok)
|
||||
}
|
||||
return next
|
||||
}
|
||||
default:
|
||||
@@ -277,13 +287,18 @@ func typeAssert2(n *node) {
|
||||
if ok {
|
||||
value0(f).Set(v.value)
|
||||
}
|
||||
value1(f).SetBool(ok)
|
||||
if setStatus {
|
||||
value1(f).SetBool(ok)
|
||||
}
|
||||
return next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func canAssertTypes(src, dest reflect.Type) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
if src == dest {
|
||||
return true
|
||||
}
|
||||
@@ -679,9 +694,13 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
|
||||
// Copy function input arguments in local frame
|
||||
for i, arg := range in {
|
||||
if def.typ.arg[i].cat == interfaceT {
|
||||
typ := def.typ.arg[i]
|
||||
switch {
|
||||
case typ.cat == interfaceT:
|
||||
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
|
||||
} else {
|
||||
case typ.cat == funcT && arg.Kind() == reflect.Func:
|
||||
d[i].Set(reflect.ValueOf(genFunctionNode(arg)))
|
||||
default:
|
||||
d[i].Set(arg)
|
||||
}
|
||||
}
|
||||
@@ -705,6 +724,10 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genFunctionNode(v reflect.Value) *node {
|
||||
return &node{kind: funcType, action: aNop, rval: v, typ: &itype{cat: valueT, rtype: v.Type()}}
|
||||
}
|
||||
|
||||
func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
if typ == nil || typ.Kind() != reflect.Interface || typ.NumMethod() == 0 || n.typ.cat == valueT {
|
||||
@@ -1057,7 +1080,7 @@ func callBin(n *node) {
|
||||
}
|
||||
|
||||
for i, c := range child {
|
||||
defType := funcType.In(pindex(i, variadic))
|
||||
defType := funcType.In(rcvrOffset + pindex(i, variadic))
|
||||
switch {
|
||||
case isBinCall(c):
|
||||
// Handle nested function calls: pass returned values as arguments
|
||||
@@ -2003,10 +2026,11 @@ func compositeBinStruct(n *node) {
|
||||
}
|
||||
} else {
|
||||
fieldIndex[i] = []int{i}
|
||||
convertLiteralValue(c.child[1], typ.Field(i).Type)
|
||||
if c.typ.cat == funcT {
|
||||
convertLiteralValue(c.child[1], typ.Field(i).Type)
|
||||
values[i] = genFunctionWrapper(c.child[1])
|
||||
} else {
|
||||
convertLiteralValue(c, typ.Field(i).Type)
|
||||
values[i] = genValue(c)
|
||||
}
|
||||
}
|
||||
@@ -2081,6 +2105,7 @@ func doCompositeSparse(n *node, hasType bool) {
|
||||
if hasType {
|
||||
child = n.child[1:]
|
||||
}
|
||||
destInterface := destType(n).cat == interfaceT
|
||||
|
||||
values := make(map[int]func(*frame) reflect.Value)
|
||||
a, _ := n.typ.zero()
|
||||
@@ -2091,6 +2116,10 @@ func doCompositeSparse(n *node, hasType bool) {
|
||||
switch {
|
||||
case c1.typ.cat == funcT:
|
||||
values[field] = genFunctionWrapper(c1)
|
||||
case c1.typ.cat == interfaceT:
|
||||
values[field] = genValueInterfaceValue(c1)
|
||||
case isArray(c1.typ) && c1.typ.val != nil && c1.typ.val.cat == interfaceT:
|
||||
values[field] = genValueInterfaceArray(c1)
|
||||
case isRecursiveType(n.typ.field[field].typ, n.typ.field[field].typ.rtype):
|
||||
values[field] = genValueRecursiveInterface(c1, n.typ.field[field].typ.rtype)
|
||||
default:
|
||||
@@ -2102,9 +2131,13 @@ func doCompositeSparse(n *node, hasType bool) {
|
||||
for i, v := range values {
|
||||
a.Field(i).Set(v(f))
|
||||
}
|
||||
if d := value(f); d.Type().Kind() == reflect.Ptr {
|
||||
d := value(f)
|
||||
switch {
|
||||
case d.Type().Kind() == reflect.Ptr:
|
||||
d.Set(a.Addr())
|
||||
} else {
|
||||
case destInterface:
|
||||
d.Set(reflect.ValueOf(valueInterface{n, a}))
|
||||
default:
|
||||
d.Set(a)
|
||||
}
|
||||
return next
|
||||
@@ -2750,7 +2783,7 @@ func convertLiteralValue(n *node, t reflect.Type) {
|
||||
case n.typ.cat == nilT:
|
||||
// Create a zero value of target type.
|
||||
n.rval = reflect.New(t).Elem()
|
||||
case !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface:
|
||||
case !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface || t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Interface:
|
||||
// Skip non-constant values, undefined target type or interface target type.
|
||||
case n.rval.IsValid():
|
||||
// Convert constant value to target type.
|
||||
|
||||
@@ -22,7 +22,7 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
|
||||
// In all other cases, absolute import paths are resolved from the GOPATH
|
||||
// and the nested "vendor" directories.
|
||||
if isPathRelative(path) {
|
||||
if rPath == "main" {
|
||||
if rPath == mainID {
|
||||
rPath = "."
|
||||
}
|
||||
dir = filepath.Join(filepath.Dir(interp.Name), rPath, path)
|
||||
@@ -132,6 +132,13 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
|
||||
interp.run(n, nil)
|
||||
}
|
||||
|
||||
// Wire and execute global vars
|
||||
n, err := genGlobalVars(rootNodes, interp.scopes[path])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
interp.run(n, nil)
|
||||
|
||||
// Add main to list of functions to run, after all inits
|
||||
if m := interp.main(); m != nil {
|
||||
initNodes = append(initNodes, m)
|
||||
@@ -146,7 +153,7 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
|
||||
|
||||
func (interp *Interpreter) rootFromSourceLocation(rPath string) (string, error) {
|
||||
sourceFile := interp.Name
|
||||
if rPath != "main" || !strings.HasSuffix(sourceFile, ".go") {
|
||||
if rPath != mainID || !strings.HasSuffix(sourceFile, ".go") {
|
||||
return rPath, nil
|
||||
}
|
||||
wd, err := os.Getwd()
|
||||
@@ -181,13 +188,62 @@ func pkgDir(goPath string, root, path string) (string, string, error) {
|
||||
return "", "", fmt.Errorf("unable to find source related to: %q", path)
|
||||
}
|
||||
|
||||
return pkgDir(goPath, previousRoot(root), path)
|
||||
rootPath := filepath.Join(goPath, "src", root)
|
||||
prevRoot, err := previousRoot(rootPath, root)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return pkgDir(goPath, prevRoot, path)
|
||||
}
|
||||
|
||||
// Find the previous source root (vendor > vendor > ... > GOPATH).
|
||||
func previousRoot(root string) string {
|
||||
splitRoot := strings.Split(root, string(filepath.Separator))
|
||||
const vendor = "vendor"
|
||||
|
||||
// Find the previous source root (vendor > vendor > ... > GOPATH).
|
||||
func previousRoot(rootPath, root string) (string, error) {
|
||||
rootPath = filepath.Clean(rootPath)
|
||||
parent, final := filepath.Split(rootPath)
|
||||
parent = filepath.Clean(parent)
|
||||
|
||||
// TODO(mpl): maybe it works for the special case main, but can't be bothered for now.
|
||||
if root != mainID && final != vendor {
|
||||
root = strings.TrimSuffix(root, string(filepath.Separator))
|
||||
prefix := strings.TrimSuffix(rootPath, root)
|
||||
|
||||
// look for the closest vendor in one of our direct ancestors, as it takes priority.
|
||||
var vendored string
|
||||
for {
|
||||
fi, err := os.Lstat(filepath.Join(parent, vendor))
|
||||
if err == nil && fi.IsDir() {
|
||||
vendored = strings.TrimPrefix(strings.TrimPrefix(parent, prefix), string(filepath.Separator))
|
||||
break
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// stop when we reach GOPATH/src/blah
|
||||
parent = filepath.Dir(parent)
|
||||
if parent == prefix {
|
||||
break
|
||||
}
|
||||
|
||||
// just an additional failsafe, stop if we reach the filesystem root.
|
||||
// TODO(mpl): It should probably be a critical error actually,
|
||||
// as we shouldn't have gone that high up in the tree.
|
||||
if parent == string(filepath.Separator) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if vendored != "" {
|
||||
return vendored, nil
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(mpl): the algorithm below might be redundant with the one above,
|
||||
// but keeping it for now. Investigate/simplify/remove later.
|
||||
splitRoot := strings.Split(root, string(filepath.Separator))
|
||||
var index int
|
||||
for i := len(splitRoot) - 1; i >= 0; i-- {
|
||||
if splitRoot[i] == "vendor" {
|
||||
@@ -197,10 +253,10 @@ func previousRoot(root string) string {
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
return ""
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return filepath.Join(splitRoot[:index]...)
|
||||
return filepath.Join(splitRoot[:index]...), nil
|
||||
}
|
||||
|
||||
func effectivePkg(root, path string) string {
|
||||
|
||||
@@ -196,9 +196,10 @@ func Test_pkgDir(t *testing.T) {
|
||||
|
||||
func Test_previousRoot(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
root string
|
||||
expected string
|
||||
desc string
|
||||
root string
|
||||
rootPathSuffix string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "GOPATH",
|
||||
@@ -215,6 +216,18 @@ func Test_previousRoot(t *testing.T) {
|
||||
root: "github.com/foo/pkg/vendor/guthib.com/containous/fromage/vendor/guthib.com/containous/fuu",
|
||||
expected: "github.com/foo/pkg/vendor/guthib.com/containous/fromage",
|
||||
},
|
||||
{
|
||||
desc: "vendor is sibling",
|
||||
root: "github.com/foo/bar",
|
||||
rootPathSuffix: "testdata/src/github.com/foo/bar",
|
||||
expected: "github.com/foo",
|
||||
},
|
||||
{
|
||||
desc: "vendor is uncle",
|
||||
root: "github.com/foo/bar/baz",
|
||||
rootPathSuffix: "testdata/src/github.com/foo/bar/baz",
|
||||
expected: "github.com/foo",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -222,7 +235,20 @@ func Test_previousRoot(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := previousRoot(test.root)
|
||||
var rootPath string
|
||||
if test.rootPathSuffix != "" {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rootPath = filepath.Join(wd, test.rootPathSuffix)
|
||||
} else {
|
||||
rootPath = vendor
|
||||
}
|
||||
p, err := previousRoot(rootPath, test.root)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if p != test.expected {
|
||||
t.Errorf("got: %s, want: %s", p, test.expected)
|
||||
|
||||
1
interp/testdata/src/github.com/foo/bar/baz/baz.go
vendored
Normal file
1
interp/testdata/src/github.com/foo/bar/baz/baz.go
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package baz
|
||||
1
interp/testdata/src/github.com/foo/vendor/whatever/whatever.go
vendored
Normal file
1
interp/testdata/src/github.com/foo/vendor/whatever/whatever.go
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package whatever
|
||||
@@ -1260,7 +1260,7 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
|
||||
if defined[name] != nil && defined[name].rtype != nil {
|
||||
return defined[name].rtype
|
||||
}
|
||||
if t.val != nil && t.val.cat == structT && t.val.rtype == nil && hasRecursiveStruct(t.val, defined) {
|
||||
if t.val != nil && t.val.cat == structT && t.val.rtype == nil && hasRecursiveStruct(t.val, copyDefined(defined)) {
|
||||
// Replace reference to self (direct or indirect) by an interface{} to handle
|
||||
// recursive types with reflect.
|
||||
typ := *t.val
|
||||
@@ -1367,6 +1367,14 @@ func (t *itype) implements(it *itype) bool {
|
||||
return t.methods().contains(it.methods())
|
||||
}
|
||||
|
||||
func copyDefined(m map[string]*itype) map[string]*itype {
|
||||
n := make(map[string]*itype, len(m))
|
||||
for k, v := range m {
|
||||
n[k] = v
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// hasRecursiveStruct determines if a struct is a recursion or a recursion
|
||||
// intermediate. A recursion intermediate is a struct that contains a recursive
|
||||
// struct.
|
||||
@@ -1385,6 +1393,8 @@ func hasRecursiveStruct(t *itype, defined map[string]*itype) bool {
|
||||
if defined[typ.path+"/"+typ.name] != nil {
|
||||
return true
|
||||
}
|
||||
defined[typ.path+"/"+typ.name] = typ
|
||||
|
||||
for _, f := range typ.field {
|
||||
if hasRecursiveStruct(f.typ, defined) {
|
||||
return true
|
||||
@@ -1531,6 +1541,10 @@ func isSendChan(t *itype) bool {
|
||||
return rt.Kind() == reflect.Chan && rt.ChanDir() == reflect.SendDir
|
||||
}
|
||||
func isMap(t *itype) bool { return t.TypeOf().Kind() == reflect.Map }
|
||||
func isArray(t *itype) bool {
|
||||
k := t.TypeOf().Kind()
|
||||
return k == reflect.Array || k == reflect.Slice
|
||||
}
|
||||
|
||||
func isInterfaceSrc(t *itype) bool {
|
||||
return t.cat == interfaceT || (t.cat == aliasT && isInterfaceSrc(t.val))
|
||||
|
||||
@@ -14,9 +14,9 @@ import (
|
||||
func init() {
|
||||
Symbols["log"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"Fatal": reflect.ValueOf(log.Fatal),
|
||||
"Fatalf": reflect.ValueOf(log.Fatalf),
|
||||
"Fatalln": reflect.ValueOf(log.Fatalln),
|
||||
"Fatal": reflect.ValueOf(logFatal),
|
||||
"Fatalf": reflect.ValueOf(logFatalf),
|
||||
"Fatalln": reflect.ValueOf(logFatalln),
|
||||
"Flags": reflect.ValueOf(log.Flags),
|
||||
"LUTC": reflect.ValueOf(constant.MakeFromLiteral("32", token.INT, 0)),
|
||||
"Ldate": reflect.ValueOf(constant.MakeFromLiteral("1", token.INT, 0)),
|
||||
@@ -25,7 +25,7 @@ func init() {
|
||||
"Lshortfile": reflect.ValueOf(constant.MakeFromLiteral("16", token.INT, 0)),
|
||||
"LstdFlags": reflect.ValueOf(constant.MakeFromLiteral("3", token.INT, 0)),
|
||||
"Ltime": reflect.ValueOf(constant.MakeFromLiteral("2", token.INT, 0)),
|
||||
"New": reflect.ValueOf(log.New),
|
||||
"New": reflect.ValueOf(logNew),
|
||||
"Output": reflect.ValueOf(log.Output),
|
||||
"Panic": reflect.ValueOf(log.Panic),
|
||||
"Panicf": reflect.ValueOf(log.Panicf),
|
||||
@@ -40,6 +40,6 @@ func init() {
|
||||
"Writer": reflect.ValueOf(log.Writer),
|
||||
|
||||
// type definitions
|
||||
"Logger": reflect.ValueOf((*log.Logger)(nil)),
|
||||
"Logger": reflect.ValueOf((*logLogger)(nil)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,10 @@ func init() {
|
||||
"ErrNotExist": reflect.ValueOf(&os.ErrNotExist).Elem(),
|
||||
"ErrPermission": reflect.ValueOf(&os.ErrPermission).Elem(),
|
||||
"Executable": reflect.ValueOf(os.Executable),
|
||||
"Exit": reflect.ValueOf(os.Exit),
|
||||
"Exit": reflect.ValueOf(osExit),
|
||||
"Expand": reflect.ValueOf(os.Expand),
|
||||
"ExpandEnv": reflect.ValueOf(os.ExpandEnv),
|
||||
"FindProcess": reflect.ValueOf(os.FindProcess),
|
||||
"FindProcess": reflect.ValueOf(osFindProcess),
|
||||
"Getegid": reflect.ValueOf(os.Getegid),
|
||||
"Getenv": reflect.ValueOf(os.Getenv),
|
||||
"Geteuid": reflect.ValueOf(os.Geteuid),
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// Code generated by 'goexports os/exec'. DO NOT EDIT.
|
||||
|
||||
// +build go1.13,!go1.14
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["os/exec"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"Command": reflect.ValueOf(exec.Command),
|
||||
"CommandContext": reflect.ValueOf(exec.CommandContext),
|
||||
"ErrNotFound": reflect.ValueOf(&exec.ErrNotFound).Elem(),
|
||||
"LookPath": reflect.ValueOf(exec.LookPath),
|
||||
|
||||
// type definitions
|
||||
"Cmd": reflect.ValueOf((*exec.Cmd)(nil)),
|
||||
"Error": reflect.ValueOf((*exec.Error)(nil)),
|
||||
"ExitError": reflect.ValueOf((*exec.ExitError)(nil)),
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,9 @@ import (
|
||||
func init() {
|
||||
Symbols["log"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"Fatal": reflect.ValueOf(log.Fatal),
|
||||
"Fatalf": reflect.ValueOf(log.Fatalf),
|
||||
"Fatalln": reflect.ValueOf(log.Fatalln),
|
||||
"Fatal": reflect.ValueOf(logFatal),
|
||||
"Fatalf": reflect.ValueOf(logFatalf),
|
||||
"Fatalln": reflect.ValueOf(logFatalln),
|
||||
"Flags": reflect.ValueOf(log.Flags),
|
||||
"LUTC": reflect.ValueOf(constant.MakeFromLiteral("32", token.INT, 0)),
|
||||
"Ldate": reflect.ValueOf(constant.MakeFromLiteral("1", token.INT, 0)),
|
||||
@@ -26,7 +26,7 @@ func init() {
|
||||
"Lshortfile": reflect.ValueOf(constant.MakeFromLiteral("16", token.INT, 0)),
|
||||
"LstdFlags": reflect.ValueOf(constant.MakeFromLiteral("3", token.INT, 0)),
|
||||
"Ltime": reflect.ValueOf(constant.MakeFromLiteral("2", token.INT, 0)),
|
||||
"New": reflect.ValueOf(log.New),
|
||||
"New": reflect.ValueOf(logNew),
|
||||
"Output": reflect.ValueOf(log.Output),
|
||||
"Panic": reflect.ValueOf(log.Panic),
|
||||
"Panicf": reflect.ValueOf(log.Panicf),
|
||||
@@ -41,6 +41,6 @@ func init() {
|
||||
"Writer": reflect.ValueOf(log.Writer),
|
||||
|
||||
// type definitions
|
||||
"Logger": reflect.ValueOf((*log.Logger)(nil)),
|
||||
"Logger": reflect.ValueOf((*logLogger)(nil)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,10 @@ func init() {
|
||||
"ErrNotExist": reflect.ValueOf(&os.ErrNotExist).Elem(),
|
||||
"ErrPermission": reflect.ValueOf(&os.ErrPermission).Elem(),
|
||||
"Executable": reflect.ValueOf(os.Executable),
|
||||
"Exit": reflect.ValueOf(os.Exit),
|
||||
"Exit": reflect.ValueOf(osExit),
|
||||
"Expand": reflect.ValueOf(os.Expand),
|
||||
"ExpandEnv": reflect.ValueOf(os.ExpandEnv),
|
||||
"FindProcess": reflect.ValueOf(os.FindProcess),
|
||||
"FindProcess": reflect.ValueOf(osFindProcess),
|
||||
"Getegid": reflect.ValueOf(os.Getegid),
|
||||
"Getenv": reflect.ValueOf(os.Getenv),
|
||||
"Geteuid": reflect.ValueOf(os.Geteuid),
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// Code generated by 'goexports os/exec'. DO NOT EDIT.
|
||||
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["os/exec"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"Command": reflect.ValueOf(exec.Command),
|
||||
"CommandContext": reflect.ValueOf(exec.CommandContext),
|
||||
"ErrNotFound": reflect.ValueOf(&exec.ErrNotFound).Elem(),
|
||||
"LookPath": reflect.ValueOf(exec.LookPath),
|
||||
|
||||
// type definitions
|
||||
"Cmd": reflect.ValueOf((*exec.Cmd)(nil)),
|
||||
"Error": reflect.ValueOf((*exec.Error)(nil)),
|
||||
"ExitError": reflect.ValueOf((*exec.ExitError)(nil)),
|
||||
}
|
||||
}
|
||||
55
stdlib/restricted.go
Normal file
55
stdlib/restricted.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var errRestricted = errors.New("restricted")
|
||||
|
||||
// osExit invokes panic instead of exit.
|
||||
func osExit(code int) { panic("os.Exit(" + strconv.Itoa(code) + ")") }
|
||||
|
||||
// osFindProcess returns os.FindProcess, except for self process.
|
||||
func osFindProcess(pid int) (*os.Process, error) {
|
||||
if pid == os.Getpid() {
|
||||
return nil, errRestricted
|
||||
}
|
||||
return os.FindProcess(pid)
|
||||
}
|
||||
|
||||
// The following functions call Panic instead of Fatal to avoid exit.
|
||||
func logFatal(v ...interface{}) { log.Panic(v...) }
|
||||
func logFatalf(f string, v ...interface{}) { log.Panicf(f, v...) }
|
||||
func logFatalln(v ...interface{}) { log.Panicln(v...) }
|
||||
|
||||
type logLogger struct {
|
||||
l *log.Logger
|
||||
}
|
||||
|
||||
// logNew Returns a wrapped logger.
|
||||
func logNew(out io.Writer, prefix string, flag int) *logLogger {
|
||||
return &logLogger{log.New(out, prefix, flag)}
|
||||
}
|
||||
|
||||
// The following methods call Panic instead of Fatal to avoid exit.
|
||||
func (l *logLogger) Fatal(v ...interface{}) { l.l.Panic(v...) }
|
||||
func (l *logLogger) Fatalf(f string, v ...interface{}) { l.l.Panicf(f, v...) }
|
||||
func (l *logLogger) Fatalln(v ...interface{}) { l.l.Panicln(v...) }
|
||||
|
||||
// The following methods just forward to wrapped logger.
|
||||
func (l *logLogger) Flags() int { return l.l.Flags() }
|
||||
func (l *logLogger) Output(d int, s string) error { return l.l.Output(d, s) }
|
||||
func (l *logLogger) Panic(v ...interface{}) { l.l.Panic(v...) }
|
||||
func (l *logLogger) Panicf(f string, v ...interface{}) { l.l.Panicf(f, v...) }
|
||||
func (l *logLogger) Panicln(v ...interface{}) { l.l.Panicln(v...) }
|
||||
func (l *logLogger) Prefix() string { return l.l.Prefix() }
|
||||
func (l *logLogger) Print(v ...interface{}) { l.l.Print(v...) }
|
||||
func (l *logLogger) Printf(f string, v ...interface{}) { l.l.Printf(f, v...) }
|
||||
func (l *logLogger) Println(v ...interface{}) { l.l.Println(v...) }
|
||||
func (l *logLogger) SetFlags(flag int) { l.l.SetFlags(flag) }
|
||||
func (l *logLogger) SetOutput(w io.Writer) { l.l.SetOutput(w) }
|
||||
func (l *logLogger) Writer() io.Writer { return l.l.Writer() }
|
||||
@@ -43,7 +43,7 @@ func init() {
|
||||
//go:generate ../cmd/goexports/goexports net net/http net/http/cgi net/http/cookiejar net/http/fcgi
|
||||
//go:generate ../cmd/goexports/goexports net/http/httptest net/http/httptrace net/http/httputil net/http/pprof
|
||||
//go:generate ../cmd/goexports/goexports net/mail net/rpc net/rpc/jsonrpc net/smtp net/textproto net/url
|
||||
//go:generate ../cmd/goexports/goexports os os/exec os/signal os/user
|
||||
//go:generate ../cmd/goexports/goexports os os/signal os/user
|
||||
//go:generate ../cmd/goexports/goexports path path/filepath reflect regexp regexp/syntax
|
||||
//go:generate ../cmd/goexports/goexports runtime runtime/debug runtime/pprof runtime/trace
|
||||
//go:generate ../cmd/goexports/goexports sort strconv strings sync sync/atomic
|
||||
|
||||
41
stdlib/unrestricted/unrestricted.go
Normal file
41
stdlib/unrestricted/unrestricted.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// Package unrestricted provides the original version of standard library symbols which may cause the interpreter process to exit.
|
||||
package unrestricted
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Symbols stores the map of syscall package symbols.
|
||||
var Symbols = map[string]map[string]reflect.Value{}
|
||||
|
||||
func init() {
|
||||
Symbols["os"] = map[string]reflect.Value{
|
||||
"Exit": reflect.ValueOf(os.Exit),
|
||||
"FindProcess": reflect.ValueOf(os.FindProcess),
|
||||
}
|
||||
|
||||
Symbols["os/exec"] = map[string]reflect.Value{
|
||||
"Command": reflect.ValueOf(exec.Command),
|
||||
"CommandContext": reflect.ValueOf(exec.CommandContext),
|
||||
"ErrNotFound": reflect.ValueOf(&exec.ErrNotFound).Elem(),
|
||||
"LookPath": reflect.ValueOf(exec.LookPath),
|
||||
"Cmd": reflect.ValueOf((*exec.Cmd)(nil)),
|
||||
"Error": reflect.ValueOf((*exec.Error)(nil)),
|
||||
"ExitError": reflect.ValueOf((*exec.ExitError)(nil)),
|
||||
}
|
||||
|
||||
Symbols["log"] = map[string]reflect.Value{
|
||||
"Fatal": reflect.ValueOf(log.Fatal),
|
||||
"Fatalf": reflect.ValueOf(log.Fatalf),
|
||||
"Fatalln": reflect.ValueOf(log.Fatalln),
|
||||
"New": reflect.ValueOf(log.New),
|
||||
"Logger": reflect.ValueOf((*log.Logger)(nil)),
|
||||
}
|
||||
|
||||
Symbols["github.com/containous/yaegi/stdlib/unrestricted"] = map[string]reflect.Value{
|
||||
"Symbols": reflect.ValueOf(Symbols),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user