Compare commits

...

37 Commits

Author SHA1 Message Date
Marc Vertes
c4174a7167 interp: fix assign of function call with implicit type assert.
De-optimize a use case to avoid skipping an assign operation with an implicit type assertion at function call return.

Fixes #1122.
2021-05-27 13:38:07 +02:00
Marc Vertes
2f9fe7003a interp: fix relative import path detection on MS-Windows
Fixes #1106.
2021-05-27 12:34:09 +02:00
Marc Vertes
c86436afa6 interp: fix litteral map containing binary functions
The case of assigning a binary function to a funcT object was
solved elsewhere. Factor the case in genDestValue to apply it
at multiple places.

Fixes #1100.
2021-05-27 12:04:11 +02:00
Marc Vertes
29e912e90b interp: fix method lookup on pointers to binary types
This case was missing in the selector expression processing.

Fixes #1083.
2021-05-27 11:54:04 +02:00
Marc Vertes
e29de04513 interp: fix constant types from imported packages
In binary packages, constants are wrapped in constant.Values, to
support arbitrary precision. Their type must therefore be converted
back to a regular type at import.

Fixes #1101.
2021-05-27 11:40:06 +02:00
Marc Vertes
c6945514cb interp: sandbox to preserve type of os.Stdin os.Stdout and os.Stderr
Use YAEGI_SPECIAL_STDIO env boolean to overwrite os.Stdxxx by
non file descriptors io.Writer / io.Reader interfaces. It is set
to true when testing to allow redirection to byte buffers.

The default behaviour is now to preserve the original concrete type
when sandboxing stdio, which maintains compatibility.

Fixes #1092.
2021-05-25 09:36:08 +02:00
Marc Vertes
847cd7ed2b interp: fix calling of variadic binary methods
Fixes #1046.
2021-05-22 11:48:07 +02:00
Marc Vertes
fbf897b047 interp: improve type check of binary methods
Some binary method calls were wrongly rejected. There is still
some ambiguous cases as binary method signature may include or
not the receiver as first argument, depending on how the method
was resolved.

With this fix, `import "golang.org/x/net/html"` doesn't panic
anymore, but not all tests are passing yet, i.e.
`yaegi test golang.org/x/net/html` still has failures, to be
investigated.

Fixes #1107.
2021-05-20 17:12:10 +02:00
Marc Vertes
45c7b8008a interp: fix special range on string
In this range variant "for k, v := range aString", k must
be the byte position of the rune in the byte array, rather than
the index of the rune in the rune array.

Fixes #1088.
2021-05-20 17:02:13 +02:00
Marc Vertes
4788775f8c interp: improve type checking for "+" operator
Fixes #1093.
2021-05-20 15:50:08 +02:00
Marc Vertes
bcb8546e91 interp: fix goto label by not overwriting destination
Fixes #1115
2021-05-20 11:38:04 +02:00
Marc Vertes
befa5a2b54 interp: fix return of function call involving a type conversion
In that case, direct propagation of result can not be attempted,
as the frame types will be different between the source and destination.
Disabling the optimisation and using The regular case involves an intermediate
frame entry, which enables the type conversion.

Fixes #1091.
2021-05-17 15:12:07 +02:00
Dan Kortschak
0ba64fc318 interp: add example to show use of Self
This example clarifies how `interp.Self` can be accessed.

Please take a look.
2021-05-10 14:06:09 +02:00
Marc Vertes
d16bd4bcdb interp: fix package name extraction from import path
The heuristic to generate a package name identifier was incorrect. Now for binary packages, the package identifier is obtained by a symbol, generated by extract, which contains the string argument of package statement in source file. This should ensure an always correct default package identifier. 

Fixes #1095.
2021-05-10 11:20:07 +02:00
Marc Vertes
33a532ee01 interp: fix a bug when assigning to an empty interface
The concrete type was not forwarded propertly in case of a binary
expression involving a valueT. The corresponding part in type.go
has been refactored and the now the multi-assign case should be
handled as well.

Fixes #1094.
2021-05-07 16:30:09 +02:00
Marc Vertes
cdc6b773c2 interp: add ImportUsed method to pre-import compiled packages
This feature was already present, but part of REPL only.
It's now also possible to apply it when evaluating a string
(-e flag). Default package names collision handling is no
longer hard-coded.

With -e flag, the eval result is now printed if valid, allowing
simpler commands:

     yaegi -e 'reflect.TypeOf(fmt.Printf)'

instead of:

     yaegi -e 'println(reflect.TypeOf(fmt.Printf))'

Fixes #1084.
2021-04-30 11:36:05 +02:00
Marc Vertes
17d5f1814a interp: fix support of interpreted functions in map values
Interpreted functions were represented in an inconsistent way in the frame: as a node pointer by default, and wrapped in a function wrapper for maps. 

We now simply use the default (*node) representation, as elsewhere, so values can be assigned, passed and called as for the other types. The alternative (generating a function wrapper) is more complex, costly and reserved for cases where the interpreted function can be called from the runtime.

Test that a map of functions can store both binary functions from used packages and interpreted ones.

Fixes #1090.
2021-04-30 11:26:04 +02:00
Marc Vertes
5f8be70066 interp: fix type string extraction for wrapped constants
Fixes #1089.
2021-04-29 18:08:03 +02:00
Marc Vertes
5530eca17d interp: give a self to the interpreter
The interpreter is exposed to itself through a "Self" var which
is set on "Use" of the interpreter package.

It allows meta-programming features, for example using "Eval" in
the current interpreter context, or enabling self-inspection
capabilities.
2021-04-21 16:26:03 +02:00
Marc Vertes
c8d9e25085 stdlib: add wrapper for testing/fstest package in go1.16 2021-04-19 09:38:03 +02:00
Marc Vertes
a241119bf7 interp: improve handling of aliased types
Avoid to test directly for a type category, as it may give wrong
results for aliased types, where the interesting category remains
masked.  Instead, use some property helpers, such as isFuncSrc,
isPtrSrc and isInterfaceSrc to check if a type is of source function,
source pointer or source interface respectively (versus runtime
defined function, pointer or interface).

Fixes #1068.
2021-04-14 17:52:04 +02:00
Marc Vertes
3e3f8d5c2f interp: implement unsafe.Offsetof
Offsetof returns the offset of a field in a struct. It is computed
during parsing at CFG, due to the constraint of operating on a
struct selector expression.

With this function, the support of 'unsafe' package is now
complete in yaegi.

Fixes #1062.
2021-04-14 14:28:03 +02:00
Marc Vertes
9aeb78fc36 interp: fix method lookup for aliased types
Fixes #1058.
2021-04-13 18:10:04 +02:00
Marc Vertes
7863456d52 interp: refactor slice type management
Add missing `sliceT` type category for consistency. Remove
`sizedef` field in `itype` struct. Rename field `size` to `length`.
Clean the various hacks used to cope with the absence of `sliceT`.
2021-04-13 18:00:12 +02:00
Ethan Reesor
428b658160 Allow colon-equal to override variable type
If I execute the following:

```
I := interp.New(interp.Options{})
I.Eval(`x := 1`)
I.Eval(`x := "foobar"`)
```

I expect the second declaration to override the first. `var x string` will override the previous type, and redeclaring a type, function, const, etc will override it, but the `:=` operator will not.

Currently, the result is: `reflect.Set: value of type string is not assignable to type int`
2021-04-02 09:08:04 +02:00
Ludovic Fernandez
350cf80bbf chore: restore macos env in GitHub Actions. 2021-04-02 08:48:04 +02:00
Ethan Reesor
d92051d40f interp: treat var declaration within a block as a define statement
This PR:

- Treats a `varDecl` within a block as a `defineStmt`
  - More specifically, any `varDecl` with a grandparent that is *not* a `fileStmt`
- Adds an extra condition to the handler for implicit const assignment
- Adds a tests to cover the changes
- Closes #1071
2021-04-01 15:22:03 +02:00
Marc Vertes
aa2621f6c6 interp: fix a panic when embedding an error interface
This patch brings the following modifications:
- consider that an interface is assignable to another if the former
  implements the latter
- call TypeOf() method instead of rtype field when resolving methods, to
  handle first met types
- unwrap error interface inplace rather than embedding it in an
  interface definition, as lower case named embbeded interface may
  not be handled by reflect when lookup for a method.

Fixes #1063. Partially improves #1058.
2021-04-01 10:38:05 +02:00
Marc Vertes
2b1d6f0e7a interp: fix append on variadic recursive struct
Fixes #1065. Improves #1058.
2021-03-26 09:34:03 +01:00
Ethan Reesor
992676722d interp: allow redeclaration of imports
This PR adds an interpreter option, `AllowRedeclaration`. If this option is set, `(*Interpreter).Eval` will allow package imports to be redeclared. That is, no error will be raised and the package symbol will be overwritten.

I would like to use Yaegi to power a Go notebook (VSCode extension), somewhat like Jupyter. A notebook can have multiple Go 'cells' which can be evaluated (using Yaegi). As much as is possible, evaluating cells should be idempotent - that is, evaluating a cell multiple times should have the same effect as evaluating it once, ideally. Cells that are not idempotent can degrade the user experience.

Specifically, Go files tend to declare all imports in a single block. In a notebook, I'd put all imports in a single block, in their own cell. When I decide I need to import an additional package, I want to add that import to the existing cell and evaluate it. Without this MR, reevaluating that block usually causes an error.
2021-03-25 23:34:03 +01:00
エガオノキミヘ
451c754068 extract: process interface wrapper method with variadic parameter
Fixes #1055
2021-03-24 11:48:03 +01:00
エガオノキミヘ
84ad46751a fix #1056, extract: replace the code for get package name.
fix #1056
2021-03-23 10:30:04 +01:00
Marc Vertes
ec5392d566 interp: do not skip use of tmp frame in multi-assign
Fixes #1052.
2021-03-19 11:24:04 +01:00
Marc Vertes
7d8fdbc1fc interp: fix closure in a struct field
Functions in a struct fields are always wrapped (as potentially
used by the runtime), so generate a function wrapper also for
closure when assigned to a struct field.

When such a function is called from the interpreter, ensure that
interface arguments are also wrapped so method and receiver resolution
can be performed.

Fixes partially #1043.
2021-03-11 15:26:04 +01:00
Marc Vertes
fdfcb9c1df interp: do not check properties of incomplete types
Fixes #1042.
2021-03-09 11:58:04 +01:00
Marc Vertes
a988459dcd interp: fix a memory management issue causing wrong closure context
The first change forces a variable definition to reallocate a
new memory slot to avoid corrupting a previously defined one in
a loop block.

The second change ensures that the frame clone operations obtains
a copy of the original data slice, to preserve the original context
set in a loop.

Fixes #1035.
2021-03-09 09:30:04 +01:00
Sylvain Muller
51e0b46256 Use fmt.Fprintln instead of fmt.Println in runCfg
As mentioned in #1030, when an Eval panic, it is print with `fmt.Println()` and not to the configured `interp.Options.Stderr`. According to https://github.com/traefik/yaegi/blob/master/interp/interp.go#L210, it should be removed in future version so I'm not sure if this pull request is necessary. However, it could fix the issue in the meanwhile.

Fixes #1030.
2021-02-20 12:08:03 +01:00
501 changed files with 1741 additions and 931 deletions

View File

@@ -22,6 +22,12 @@ func main() {
var g int = 2
a = 10 + g
println(a.(int))
// multiple assignment
var foo interface{}
foo, a = "hello", 11 + g
println(a.(int))
println(foo.(string))
}
// Output:
@@ -31,3 +37,5 @@ func main() {
// 10
// 11
// 12
// 13
// hello

18
_test/closure10.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func main() {
foos := []func(){}
for i := 0; i < 3; i++ {
a, b := i, i
foos = append(foos, func() { println(i, a, b) })
}
foos[0]()
foos[1]()
foos[2]()
}
// Output:
// 3 0 0
// 3 1 1
// 3 2 2

22
_test/closure11.go Normal file
View File

@@ -0,0 +1,22 @@
package main
type T struct {
F func()
}
func main() {
foos := []T{}
for i := 0; i < 3; i++ {
a := i
foos = append(foos, T{func() { println(i, a) }})
}
foos[0].F()
foos[1].F()
foos[2].F()
}
// Output:
// 3 0
// 3 1
// 3 2

25
_test/closure12.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import "fmt"
type T struct {
F func()
}
func main() {
foos := []T{}
for i := 0; i < 3; i++ {
a := i
n := fmt.Sprintf("i=%d", i)
foos = append(foos, T{func() { println(i, a, n) }})
}
foos[0].F()
foos[1].F()
foos[2].F()
}
// Output:
// 3 0 i=0
// 3 1 i=1
// 3 2 i=2

18
_test/closure9.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func main() {
foos := []func(){}
for i := 0; i < 3; i++ {
a := i
foos = append(foos, func() { println(i, a) })
}
foos[0]()
foos[1]()
foos[2]()
}
// Output:
// 3 0
// 3 1
// 3 2

11
_test/const25.go Normal file
View File

@@ -0,0 +1,11 @@
package main
const (
FGBlack Attribute = iota + 30
)
type Attribute int
func main() {
println(FGBlack)
}

23
_test/interface51.go Normal file
View File

@@ -0,0 +1,23 @@
package main
type Error interface {
error
Message() string
}
type T struct {
Msg string
}
func (t *T) Error() string { return t.Msg }
func (t *T) Message() string { return "message:" + t.Msg }
func newError() Error { return &T{"test"} }
func main() {
e := newError()
println(e.Error())
}
// Output:
// test

23
_test/issue-1052.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import "fmt"
func main() {
a, b := 1, 1
for i := 0; i < 10; i++ {
fmt.Println(a)
a, b = b, a+b
}
}
// Output:
// 1
// 1
// 2
// 3
// 5
// 8
// 13
// 21
// 34
// 55

20
_test/issue-1065.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import "fmt"
type AST struct {
Num int
Children []AST
}
func newAST(num int, root AST, children ...AST) AST {
return AST{num, append([]AST{root}, children...)}
}
func main() {
ast := newAST(1, AST{}, AST{})
fmt.Println(ast)
}
// Output:
// {1 [{0 []} {0 []}]}

19
_test/issue-1068.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type I interface {
Hello()
}
type T struct{}
func (t T) Hello() { println("hello") }
type I2 I
func main() {
var i I2 = T{}
i.Hello()
}
// Output:
// hello

14
_test/issue-1088.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
func main() {
for i, ch := range "日本語" {
fmt.Printf("%#U starts at byte position %d\n", ch, i)
}
}
// Output:
// U+65E5 '日' starts at byte position 0
// U+672C '本' starts at byte position 3
// U+8A9E '語' starts at byte position 6

13
_test/issue-1089.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println(`"` + time.RFC3339Nano + `"`)
}
// Output:
// "2006-01-02T15:04:05.999999999Z07:00"

13
_test/issue-1093.go Normal file
View File

@@ -0,0 +1,13 @@
package main
func b() string {
return "b"
}
func main() {
var x int
x = "a" + b()
}
// Error:
// 9:6: cannot use type string as type int in assignment

12
_test/issue-1094.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
var x interface{}
x = "a" + fmt.Sprintf("b")
fmt.Printf("%v %T\n", x, x)
}
// Ouput:
// ab string

17
_test/issue-1101.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import (
"fmt"
"net/http"
)
func main() {
method := "POST"
switch method {
case http.MethodPost:
fmt.Println("It's a post!")
}
}
// Output:
// It's a post!

24
_test/issue-1115.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import "fmt"
func main() {
outer:
for y := 0; y < 10; y++ {
for x := 0; x < 10; x++ {
if x == 5 && y == 5 {
break outer
}
}
fmt.Println(y)
}
fmt.Println("Yay! I finished!")
}
// Output:
// 0
// 1
// 2
// 3
// 4
// Yay! I finished!

27
_test/map30.go Normal file
View File

@@ -0,0 +1,27 @@
package main
import "strings"
func f(s string) string { return "hello " + s }
func g(s string) string { return "hi " + s }
var methods = map[string]func(string) string{
"f": f,
"h": strings.ToLower,
}
func main() {
methods["i"] = strings.ToUpper
methods["g"] = g
println(methods["f"]("test"))
println(methods["g"]("test"))
println(methods["i"]("test"))
println(methods["h"]("TEST"))
}
// Output:
// hello test
// hi test
// TEST
// test

26
_test/method36.go Normal file
View File

@@ -0,0 +1,26 @@
package main
type I interface{ Hello() }
type T struct{ Name string }
func (t *T) Hello() { println("Hello", t.Name) }
type FT func(i I)
type ST struct{ Handler FT }
func newF() FT {
return func(i I) {
i.Hello()
}
}
func main() {
st := &ST{}
st.Handler = newF()
st.Handler(&T{"test"})
}
// Output:
// Hello test

19
_test/method37.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import (
"net"
"os"
)
func writeBufs(bufs ...[]byte) error {
b := net.Buffers(bufs)
_, err := b.WriteTo(os.Stdout)
return err
}
func main() {
writeBufs([]byte("hello"))
}
// Output:
// hello

28
_test/ret8.go Normal file
View File

@@ -0,0 +1,28 @@
package main
import "fmt"
type CustomError string
func (s CustomError) Error() string {
return string(s)
}
func NewCustomError(errorText string) CustomError {
return CustomError(errorText)
}
func fail() (err error) {
return NewCustomError("Everything is going wrong!")
}
func main() {
fmt.Println(fail())
var myError error
myError = NewCustomError("ok")
fmt.Println(myError)
}
// Output:
// Everything is going wrong!
// ok

View File

@@ -7,7 +7,7 @@ import (
const (
period = 100 * time.Millisecond
precision = 5 * time.Millisecond
precision = 7 * time.Millisecond
)
func main() {

View File

@@ -12,11 +12,13 @@ type S struct {
}
func main() {
size := unsafe.Sizeof(S{})
align := unsafe.Alignof(S{})
x := S{}
size := unsafe.Sizeof(x)
align := unsafe.Alignof(x.Y)
offset := unsafe.Offsetof(x.Z)
fmt.Println(size, align)
fmt.Println(size, align, offset)
}
// Output:
// 24 8
// 24 8 16

View File

@@ -6,6 +6,7 @@ import (
"go/build"
"io/ioutil"
"os"
"reflect"
"strconv"
"strings"
@@ -18,6 +19,7 @@ import (
func run(arg []string) error {
var interactive bool
var noAutoImport bool
var tags string
var cmd string
var err error
@@ -33,6 +35,7 @@ func run(arg []string) error {
rflag.BoolVar(&useUnrestricted, "unrestricted", useUnrestricted, "include unrestricted symbols")
rflag.StringVar(&tags, "tags", "", "set a list of build tags")
rflag.BoolVar(&useUnsafe, "unsafe", useUnsafe, "include unsafe symbols")
rflag.BoolVar(&noAutoImport, "noautoimport", false, "do not auto import pre-compiled packages. Import names that would result in collisions (e.g. rand from crypto/rand and rand from math/rand) are automatically renamed (crypto_rand and math_rand)")
rflag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
rflag.Usage = func() {
fmt.Println("Usage: yaegi run [options] [path] [args]")
@@ -69,24 +72,34 @@ func run(arg []string) error {
}
if cmd != "" {
_, err = i.Eval(cmd)
if !noAutoImport {
i.ImportUsed()
}
var v reflect.Value
v, err = i.Eval(cmd)
if len(args) == 0 && v.IsValid() {
fmt.Println(v)
}
}
if len(args) == 0 {
if interactive || cmd == "" {
if cmd == "" || interactive {
showError(err)
if !noAutoImport {
i.ImportUsed()
}
_, err = i.REPL()
}
return err
}
// Skip first os arg to set command line as expected by interpreted main
// Skip first os arg to set command line as expected by interpreted main.
path := args[0]
os.Args = arg
flag.CommandLine = flag.NewFlagSet(path, flag.ExitOnError)
if isFile(path) {
err = runFile(i, path)
err = runFile(i, path, noAutoImport)
} else {
_, err = i.EvalPath(path)
}
@@ -106,7 +119,7 @@ func isFile(path string) bool {
return err == nil && fi.Mode().IsRegular()
}
func runFile(i *interp.Interpreter, path string) error {
func runFile(i *interp.Interpreter, path string, noAutoImport bool) error {
b, err := ioutil.ReadFile(path)
if err != nil {
return err
@@ -115,6 +128,9 @@ func runFile(i *interp.Interpreter, path string) error {
if s := string(b); strings.HasPrefix(s, "#!") {
// Allow executable go scripts, Have the same behavior as in interactive mode.
s = strings.Replace(s, "#!", "//", 1)
if !noAutoImport {
i.ImportUsed()
}
_, err = i.Eval(s)
return err
}

View File

@@ -82,7 +82,7 @@ func TestYaegiCmdCancel(t *testing.T) {
if err != nil {
t.Errorf("failed pipe test source to yaegi command: %v", err)
}
Sleep(200 * time.Millisecond)
Sleep(500 * time.Millisecond)
err = cmd.Process.Signal(os.Interrupt)
if err != nil {
t.Errorf("failed to send os.Interrupt to yaegi command: %v", err)

View File

@@ -25,7 +25,7 @@ import (
"text/template"
)
const model = `// Code generated by 'yaegi extract {{.PkgName}}'. DO NOT EDIT.
const model = `// Code generated by 'yaegi extract {{.ImportPath}}'. DO NOT EDIT.
{{.License}}
@@ -39,7 +39,7 @@ import (
"{{$key}}"
{{- end}}
{{- end}}
"{{.PkgName}}"
"{{.ImportPath}}"
"reflect"
)
@@ -167,8 +167,8 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
continue
}
pname := path.Base(importPath) + "." + name
if rname := path.Base(importPath) + name; restricted[rname] {
pname := p.Name() + "." + name
if rname := p.Name() + name; restricted[rname] {
// Restricted symbol, locally provided by stdlib wrapper.
pname = rname
}
@@ -203,7 +203,15 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
if args[j] = v.Name(); args[j] == "" {
args[j] = fmt.Sprintf("a%d", j)
}
params[j] = args[j] + " " + types.TypeString(v.Type(), qualify)
// process interface method variadic parameter
if sign.Variadic() && j == len(args)-1 { // check is last arg
// only replace the first "[]" to "..."
at := types.TypeString(v.Type(), qualify)[2:]
params[j] = args[j] + " ..." + at
args[j] += "..."
} else {
params[j] = args[j] + " " + types.TypeString(v.Type(), qualify)
}
}
arg := "(" + strings.Join(args, ", ") + ")"
param := "(" + strings.Join(params, ", ") + ")"
@@ -263,14 +271,15 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
b := new(bytes.Buffer)
data := map[string]interface{}{
"Dest": e.Dest,
"Imports": imports,
"PkgName": importPath,
"Val": val,
"Typ": typ,
"Wrap": wrap,
"BuildTags": buildTags,
"License": e.License,
"Dest": e.Dest,
"Imports": imports,
"ImportPath": importPath,
"PkgName": path.Join(importPath, p.Name()),
"Val": val,
"Typ": typ,
"Wrap": wrap,
"BuildTags": buildTags,
"License": e.License,
}
err = parse.Execute(b, data)
if err != nil {

View File

@@ -18,7 +18,7 @@ import (
)
func init() {
Symbols["guthib.com/baz"] = map[string]reflect.Value{
Symbols["guthib.com/baz/baz"] = map[string]reflect.Value{
// function, constant and variable definitions
"Hello": reflect.ValueOf(baz.Hello),
}
@@ -78,6 +78,48 @@ func TestPackages(t *testing.T) {
importPath: "guthib.com/baz",
expected: expectedOutput,
},
{
desc: "using relative path, package name is not same as import path",
wd: "./testdata/6/src/guthib.com/bar",
arg: "../baz-baz",
importPath: "guthib.com/baz",
expected: expectedOutput,
},
{
desc: "using relative path, interface method parameter is variadic",
wd: "./testdata/7/src/guthib.com/variadic",
arg: "../variadic",
importPath: "guthib.com/variadic",
expected: `
// Code generated by 'yaegi extract guthib.com/variadic'. DO NOT EDIT.
package variadic
import (
"guthib.com/variadic"
"reflect"
)
func init() {
Symbols["guthib.com/variadic/variadic"] = map[string]reflect.Value{
// type definitions
"Variadic": reflect.ValueOf((*variadic.Variadic)(nil)),
// interface wrapper definitions
"_Variadic": reflect.ValueOf((*_guthib_com_variadic_Variadic)(nil)),
}
}
// _guthib_com_variadic_Variadic is an interface wrapper for Variadic type
type _guthib_com_variadic_Variadic struct {
WCall func(method string, args ...[]interface{}) (interface{}, error)
}
func (W _guthib_com_variadic_Variadic) Call(method string, args ...[]interface{}) (interface{}, error) {
return W.WCall(method, args...)
}
`[1:],
},
}
for _, test := range testCases {

View File

@@ -0,0 +1,9 @@
package main
import (
"guthib.com/baz"
)
func main() {
baz.Hello()
}

View File

@@ -0,0 +1,5 @@
package baz
func Hello() {
println("HELLO")
}

View File

@@ -0,0 +1 @@
module guthib.com/baz-baz

View File

@@ -0,0 +1 @@
module guthib.com/baz-baz/variadic

View File

@@ -0,0 +1,5 @@
package variadic
type Variadic interface {
Call(method string, args ...[]interface{}) (interface{}, error)
}

View File

@@ -716,7 +716,7 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
n := addChild(&root, anc, pos, identExpr, aNop)
n.ident = a.Name
st.push(n, nod)
if n.anc.kind == defineStmt && n.anc.nright == 0 {
if n.anc.kind == defineStmt && n.anc.anc.kind == constDecl && n.anc.nright == 0 {
// Implicit assign expression (in a ConstDecl block).
// Clone assign source and type from previous
a := n.anc
@@ -858,7 +858,8 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
case *ast.ValueSpec:
kind := valueSpec
act := aNop
if a.Values != nil {
switch {
case a.Values != nil:
if len(a.Names) > 1 && len(a.Values) == 1 {
if anc.node.kind == constDecl || anc.node.kind == varDecl {
kind = defineXStmt
@@ -874,7 +875,9 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
}
act = aAssign
}
} else if anc.node.kind == constDecl {
case anc.node.kind == constDecl:
kind, act = defineStmt, aAssign
case anc.node.kind == varDecl && anc.node.anc.kind != fileStmt:
kind, act = defineStmt, aAssign
}
n := addChild(&root, anc, pos, kind, act)

View File

@@ -7,7 +7,6 @@ import (
"math"
"path/filepath"
"reflect"
"regexp"
"strings"
"unicode"
)
@@ -44,8 +43,6 @@ var constBltn = map[string]func(*node){
bltnReal: realConst,
}
var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
const nilIdent = "nil"
// cfg generates a control flow graph (CFG) from AST (wiring successors in AST)
@@ -82,7 +79,14 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
i--
}
dest := a.child[i]
if dest.typ != nil && !isInterface(dest.typ) {
if dest.typ == nil {
break
}
if dest.typ.incomplete {
err = n.cfgErrorf("invalid type declaration")
return false
}
if !isInterface(dest.typ) {
// Interface type are not propagated, and will be resolved at post-order.
n.typ = dest.typ
}
@@ -147,6 +151,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
vtyp = &itype{cat: valueT, rtype: typ.Elem()}
case reflect.String:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
sc.add(sc.getType("int")) // Add a dummy type to store index for range
ktyp = sc.getType("int")
vtyp = sc.getType("rune")
case reflect.Array, reflect.Slice:
@@ -170,9 +175,10 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
case stringT:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
sc.add(sc.getType("int")) // Add a dummy type to store index for range
ktyp = sc.getType("int")
vtyp = sc.getType("rune")
case arrayT, variadicT:
case arrayT, sliceT, variadicT:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
ktyp = sc.getType("int")
vtyp = o.typ.val
@@ -518,10 +524,6 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
if dest.typ.incomplete {
return
}
if dest.typ.sizedef {
dest.typ.size = arrayTypeLen(src)
dest.typ.rtype = nil
}
if sc.global {
// Do not overload existing symbols (defined in GTA) in global scope
sym, _, _ = sc.lookup(dest.ident)
@@ -555,9 +557,6 @@ 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.
// 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.
@@ -571,8 +570,14 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
// Setting a map entry requires 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):
case isFuncField(dest):
// Setting a struct field of function type requires an extra step. Do not optimize.
case isCall(src) && !isInterfaceSrc(dest.typ) && !isRecursiveField(dest) && n.kind != defineStmt:
// Call action may perform the assignment directly.
if dest.typ.id() != src.typ.id() {
// Skip optimitization if returned type doesn't match assigned one.
break
}
n.gen = nop
src.level = level
src.findex = dest.findex
@@ -605,6 +610,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case src.kind == basicLit:
// Assign to nil.
src.rval = reflect.New(dest.typ.TypeOf()).Elem()
case n.nright == 0:
n.gen = reset
}
n.typ = dest.typ
@@ -724,7 +731,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
// by constOp and available in n.rval. Nothing else to do at execution.
n.gen = nop
n.findex = notInFrame
case n.anc.kind == assignStmt && n.anc.action == aAssign:
case n.anc.kind == assignStmt && n.anc.action == aAssign && n.anc.nleft == 1:
// To avoid a copy in frame, if the result is to be assigned, store it directly
// at the frame location of destination.
dest := n.anc.child[childPos(n)-n.anc.nright]
@@ -974,6 +981,20 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
}
}
case isOffsetof(n):
if len(n.child) != 2 || n.child[1].kind != selectorExpr || !isStruct(n.child[1].child[0].typ) {
err = n.cfgErrorf("Offsetof argument: invalid expression")
break
}
c1 := n.child[1]
field, ok := c1.child[0].typ.rtype.FieldByName(c1.child[1].ident)
if !ok {
err = n.cfgErrorf("struct does not contain field: %s", c1.child[1].ident)
break
}
n.typ = &itype{cat: valueT, rtype: reflect.TypeOf(field.Offset)}
n.rval = reflect.ValueOf(field.Offset)
n.gen = nop
default:
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
if err != nil {
@@ -986,7 +1007,9 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
if typ := n.child[0].typ; len(typ.ret) > 0 {
n.typ = typ.ret[0]
if n.anc.kind == returnStmt {
if n.anc.kind == returnStmt && n.typ.id() == sc.def.typ.ret[0].id() {
// Store the result directly to the return value area of frame.
// It can be done only if no type conversion at return is involved.
n.findex = childPos(n)
} else {
n.findex = sc.add(n.typ)
@@ -1043,8 +1066,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
switch n.typ.cat {
case arrayT:
err = check.arrayLitExpr(child, n.typ.val, n.typ.size)
case arrayT, sliceT:
err = check.arrayLitExpr(child, n.typ)
case mapT:
err = check.mapLitExpr(child, n.typ.key, n.typ.val)
case structT:
@@ -1426,7 +1449,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
// TODO(mpl): move any of that code to typecheck?
c.typ.node = c
if !c.typ.assignableTo(typ) {
err = fmt.Errorf("cannot use %v (type %v) as type %v in return argument", c.ident, c.typ.cat, typ.cat)
err = c.cfgErrorf("cannot use %v (type %v) as type %v in return argument", c.ident, c.typ.cat, typ.cat)
return
}
if c.typ.cat == nilT {
@@ -1468,28 +1491,28 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
n.typ = &itype{cat: valueT, rtype: field.Type}
n.val = field.Index
n.gen = getPtrIndexSeq
} else {
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
break
}
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
case n.typ.rtype.Kind() == reflect.Struct:
if field, ok := n.typ.rtype.FieldByName(n.child[1].ident); ok {
n.typ = &itype{cat: valueT, rtype: field.Type}
n.val = field.Index
n.gen = getIndexSeq
} else {
// method lookup failed on type, now lookup on pointer to type
pt := reflect.PtrTo(n.typ.rtype)
if m2, ok2 := pt.MethodByName(n.child[1].ident); ok2 {
n.val = m2.Index
n.gen = getIndexBinPtrMethod
n.typ = &itype{cat: valueT, rtype: m2.Type, recv: &itype{cat: valueT, rtype: pt}}
n.recv = &receiver{node: n.child[0]}
n.action = aGetMethod
} else {
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
}
break
}
fallthrough
default:
// method lookup failed on type, now lookup on pointer to type
pt := reflect.PtrTo(n.typ.rtype)
if m2, ok2 := pt.MethodByName(n.child[1].ident); ok2 {
n.val = m2.Index
n.gen = getIndexBinPtrMethod
n.typ = &itype{cat: valueT, rtype: m2.Type, recv: &itype{cat: valueT, rtype: pt}}
n.recv = &receiver{node: n.child[0]}
n.action = aGetMethod
break
}
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
}
} else if n.typ.cat == ptrT && (n.typ.val.cat == valueT || n.typ.val.cat == errorT) {
@@ -1521,7 +1544,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
if isBinType(s) {
n.typ = &itype{cat: valueT, rtype: s.Type().Elem()}
} else {
n.typ = &itype{cat: valueT, rtype: s.Type(), untyped: isValueUntyped(s)}
n.typ = &itype{cat: valueT, rtype: fixPossibleConstType(s.Type()), untyped: isValueUntyped(s)}
n.rval = s
}
n.action = aGetSym
@@ -2388,6 +2411,10 @@ func isMethod(n *node) bool {
return len(n.child[0].child) > 0 // receiver defined
}
func isFuncField(n *node) bool {
return isField(n) && isFunc(n.typ)
}
func isMapEntry(n *node) bool {
return n.action == aGetIndex && isMap(n.child[0].typ)
}
@@ -2400,6 +2427,10 @@ func isBinCall(n *node) bool {
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
}
func isOffsetof(n *node) bool {
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].rval.String() == "Offsetof"
}
func mustReturnValue(n *node) bool {
if len(n.child) < 2 {
return false
@@ -2490,7 +2521,9 @@ func gotoLabel(s *symbol) {
return
}
for _, c := range s.from {
c.tnext = s.node.start
if c.tnext == nil {
c.tnext = s.node.start
}
}
}
@@ -2498,7 +2531,7 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
switch typ.cat {
case aliasT, ptrT:
gen = compositeGenerator(n, n.typ.val, rtyp)
case arrayT:
case arrayT, sliceT:
gen = arrayLit
case mapT:
gen = mapLit
@@ -2552,8 +2585,8 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
// array variable it is determined from the value's type, otherwise it is
// computed from the source definition.
func arrayTypeLen(n *node) int {
if n.typ != nil && n.typ.sizedef {
return n.typ.size
if n.typ != nil && n.typ.cat == arrayT {
return n.typ.length
}
max := -1
for i, c := range n.child[1:] {

View File

@@ -80,9 +80,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
if typ.isBinMethod {
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true, scope: sc}
}
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}
}
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
if childPos(n) == len(n.anc.child)-1 {
@@ -189,11 +187,11 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
}
// Try to import a binary package first, or a source package
var pkgName string
if interp.binPkg[ipath] != nil {
if pkg := interp.binPkg[ipath]; pkg != nil {
switch name {
case "_": // no import of symbols
case ".": // import symbols in current scope
for n, v := range interp.binPkg[ipath] {
for n, v := range pkg {
typ := v.Type()
if isBinType(v) {
typ = typ.Elem()
@@ -202,16 +200,19 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
}
default: // import symbols in package namespace
if name == "" {
name = identifier.FindString(ipath)
name = interp.pkgNames[ipath]
}
// imports of a same package are all mapped in the same scope, so we cannot just
// Imports of a same package are all mapped in the same scope, so we cannot just
// map them by their names, otherwise we could have collisions from same-name
// imports in different source files of the same package. Therefore, we suffix
// the key with the basename of the source file.
name = filepath.Join(name, baseName)
if _, exists := sc.sym[name]; !exists {
if sym, exists := sc.sym[name]; !exists {
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath, scope: sc}}
break
} else if sym.kind == pkgSym && sym.typ.cat == srcPkgT && sym.typ.path == ipath {
// ignore re-import of identical package
break
}
// redeclaration error. Not caught by the parser.
@@ -233,9 +234,12 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
name = pkgName
}
name = filepath.Join(name, baseName)
if _, exists := sc.sym[name]; !exists {
if sym, exists := sc.sym[name]; !exists {
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: ipath, scope: sc}}
break
} else if sym.kind == pkgSym && sym.typ.cat == srcPkgT && sym.typ.path == ipath {
// ignore re-import of identical package
break
}
// redeclaration error

View File

@@ -14,6 +14,8 @@ import (
"log"
"os"
"os/signal"
"path"
"path/filepath"
"reflect"
"runtime"
"runtime/debug"
@@ -92,21 +94,29 @@ func newFrame(anc *frame, length int, id uint64) *frame {
func (f *frame) runid() uint64 { return atomic.LoadUint64(&f.id) }
func (f *frame) setrunid(id uint64) { atomic.StoreUint64(&f.id, id) }
func (f *frame) clone() *frame {
func (f *frame) clone(fork bool) *frame {
f.mutex.RLock()
defer f.mutex.RUnlock()
return &frame{
nf := &frame{
anc: f.anc,
root: f.root,
data: f.data,
deferred: f.deferred,
recovered: f.recovered,
id: f.runid(),
done: f.done,
}
if fork {
nf.data = make([]reflect.Value, len(f.data))
copy(nf.data, f.data)
} else {
nf.data = f.data
}
return nf
}
// Exports stores the map of binary packages per package path.
// The package path is the path joined from the import path and the package name
// as specified in source files by the "package" statement.
type Exports map[string]map[string]reflect.Value
// imports stores the map of source packages per package path.
@@ -118,13 +128,14 @@ type opt struct {
cfgDot bool // display CFG graph (debug)
// dotCmd is the command to process the dot graph produced when astDot and/or
// cfgDot is enabled. It defaults to 'dot -Tdot -o <filename>.dot'.
dotCmd string
noRun bool // compile, but do not run
fastChan bool // disable cancellable chan operations
context build.Context // build context: GOPATH, build constraints
stdin io.Reader // standard input
stdout io.Writer // standard output
stderr io.Writer // standard error
dotCmd string
noRun bool // compile, but do not run
fastChan bool // disable cancellable chan operations
context build.Context // build context: GOPATH, build constraints
specialStdio bool // Allows os.Stdin, os.Stdout, os.Stderr to not be file descriptors
stdin io.Reader // standard input
stdout io.Writer // standard output
stderr io.Writer // standard error
}
// Interpreter contains global resources and state.
@@ -162,7 +173,7 @@ type Interpreter struct {
const (
mainID = "main"
selfPrefix = "github.com/traefik/yaegi"
selfPath = selfPrefix + "/interp"
selfPath = selfPrefix + "/interp/interp"
// DefaultSourceName is the name used by default when the name of the input
// source file has not been specified for an Eval.
// TODO(mpl): something even more special as a name?
@@ -174,6 +185,9 @@ const (
NoTest = true
)
// Self points to the current interpreter if accessed from within itself, or is nil.
var Self *Interpreter
// Symbols exposes interpreter values.
var Symbols = Exports{
selfPath: map[string]reflect.Value{
@@ -208,7 +222,7 @@ type Panic struct {
}
// TODO: Capture interpreter stack frames also and remove
// fmt.Println(n.cfgErrorf("panic")) in runCfg.
// fmt.Fprintln(n.interp.stderr, oNode.cfgErrorf("panic")) in runCfg.
func (e Panic) Error() string { return fmt.Sprint(e.Value) }
@@ -235,7 +249,7 @@ type Options struct {
BuildTags []string
// Standard input, output and error streams.
// They default to os.Stding, os.Stdout and os.Stderr respectively.
// They default to os.Stdin, os.Stdout and os.Stderr respectively.
Stdin io.Reader
Stdout, Stderr io.Writer
}
@@ -288,6 +302,10 @@ func New(options Options) *Interpreter {
// fastChan disables the cancellable version of channel operations in evalWithContext
i.opt.fastChan, _ = strconv.ParseBool(os.Getenv("YAEGI_FAST_CHAN"))
// specialStdio allows to assign directly io.Writer and io.Reader to os.Stdxxx, even if they are not file descriptors.
i.opt.specialStdio, _ = strconv.ParseBool(os.Getenv("YAEGI_SPECIAL_STDIO"))
return &i
}
@@ -637,31 +655,38 @@ func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
// they can be used in interpreted code.
func (interp *Interpreter) Use(values Exports) {
for k, v := range values {
if k == selfPrefix {
importPath := path.Dir(k)
packageName := path.Base(k)
if importPath == selfPrefix {
interp.hooks.Parse(v)
continue
}
if interp.binPkg[k] == nil {
interp.binPkg[k] = make(map[string]reflect.Value)
if interp.binPkg[importPath] == nil {
interp.binPkg[importPath] = make(map[string]reflect.Value)
interp.pkgNames[importPath] = packageName
}
for s, sym := range v {
interp.binPkg[k][s] = sym
interp.binPkg[importPath][s] = sym
}
if k == selfPath {
interp.binPkg[importPath]["Self"] = reflect.ValueOf(interp)
}
}
// Checks if input values correspond to stdlib packages by looking for one
// well known stdlib package path.
if _, ok := values["fmt"]; ok {
if _, ok := values["fmt/fmt"]; ok {
fixStdio(interp)
}
}
// fixStdio redefines interpreter stdlib symbols to use the standard input,
// output and errror assigned to the interpreter. The changes are limited to
// the interpreter only. Global values os.Stdin, os.Stdout and os.Stderr are
// not changed. Note that it is possible to escape the virtualized stdio by
// the interpreter only.
// Note that it is possible to escape the virtualized stdio by
// read/write directly to file descriptors 0, 1, 2.
func fixStdio(interp *Interpreter) {
p := interp.binPkg["fmt"]
@@ -708,9 +733,23 @@ func fixStdio(interp *Interpreter) {
}
if p = interp.binPkg["os"]; p != nil {
p["Stdin"] = reflect.ValueOf(&stdin).Elem()
p["Stdout"] = reflect.ValueOf(&stdout).Elem()
p["Stderr"] = reflect.ValueOf(&stderr).Elem()
if interp.specialStdio {
// Inherit streams from interpreter even if they do not have a file descriptor.
p["Stdin"] = reflect.ValueOf(&stdin).Elem()
p["Stdout"] = reflect.ValueOf(&stdout).Elem()
p["Stderr"] = reflect.ValueOf(&stderr).Elem()
} else {
// Inherits streams from interpreter only if they have a file descriptor and preserve original type.
if s, ok := stdin.(*os.File); ok {
p["Stdin"] = reflect.ValueOf(&s).Elem()
}
if s, ok := stdout.(*os.File); ok {
p["Stdout"] = reflect.ValueOf(&s).Elem()
}
if s, ok := stderr.(*os.File); ok {
p["Stderr"] = reflect.ValueOf(&s).Elem()
}
}
}
}
@@ -730,24 +769,47 @@ func ignoreScannerError(e *scanner.Error, s string) bool {
return false
}
// ImportUsed automatically imports pre-compiled packages included by Use().
// This is mainly useful for REPLs, or single command lines. In case of an ambiguous default
// package name, for example "rand" for crypto/rand and math/rand, the package name is
// constructed by replacing the last "/" by a "_", producing crypto_rand and math_rand.
// ImportUsed should not be called more than once, and not after a first Eval, as it may
// rename packages.
func (interp *Interpreter) ImportUsed() {
sc := interp.universe
for k := range interp.binPkg {
// By construction, the package name is the last path element of the key.
name := path.Base(k)
if sym, ok := sc.sym[name]; ok {
// Handle collision by renaming old and new entries.
name2 := key2name(fixKey(sym.typ.path))
sc.sym[name2] = sym
if name2 != name {
delete(sc.sym, name)
}
name = key2name(fixKey(k))
}
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: k, scope: sc}}
}
}
func key2name(name string) string {
return filepath.Join(name, DefaultSourceName)
}
func fixKey(k string) string {
i := strings.LastIndex(k, "/")
if i >= 0 {
k = k[:i] + "_" + k[i+1:]
}
return k
}
// REPL performs a Read-Eval-Print-Loop on input reader.
// Results are printed to the output writer of the Interpreter, provided as option
// at creation time. Errors are printed to the similarly defined errors writer.
// The last interpreter result value and error are returned.
func (interp *Interpreter) REPL() (reflect.Value, error) {
// Preimport used bin packages, to avoid having to import these packages manually
// in REPL mode. These packages are already loaded anyway.
sc := interp.universe
for k := range interp.binPkg {
name := identifier.FindString(k)
if name == "" || name == "rand" || name == "scanner" || name == "template" || name == "pprof" {
// Skip any package with an ambiguous name (i.e crypto/rand vs math/rand).
// Those will have to be imported explicitly.
continue
}
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: k, scope: sc}}
}
in, out, errs := interp.stdin, interp.stdout, interp.stderr
ctx, cancel := context.WithCancel(context.Background())
end := make(chan struct{}) // channel to terminate the REPL

View File

@@ -50,6 +50,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
file.Name() == "import6.go" || // expect error
file.Name() == "init1.go" || // expect error
file.Name() == "io0.go" || // use random number
file.Name() == "issue-1093.go" || // expect error
file.Name() == "op1.go" || // expect error
file.Name() == "op7.go" || // expect error
file.Name() == "op9.go" || // expect error
@@ -209,6 +210,11 @@ func TestInterpErrorConsistency(t *testing.T) {
expectedInterp: "3:17: too many arguments to return",
expectedExec: "3:17: too many arguments to return",
},
{
fileName: "issue-1093.go",
expectedInterp: "9:6: cannot use type string as type int in assignment",
expectedExec: `9:4: cannot use "a" + b() (type string) as type int in assignment`,
},
{
fileName: "op1.go",
expectedInterp: "5:2: invalid operation: mismatched types int and float64",

View File

@@ -104,7 +104,7 @@ func TestEvalStar(t *testing.T) {
func TestEvalAssign(t *testing.T) {
i := interp.New(interp.Options{})
i.Use(interp.Exports{
"testpkg": {
"testpkg/testpkg": {
"val": reflect.ValueOf(int64(11)),
},
})
@@ -123,6 +123,7 @@ func TestEvalAssign(t *testing.T) {
{src: "h := 1; h >>= 8", res: "0"},
{src: "i := 1; j := &i; (*j) = 2", res: "2"},
{src: "i64 := testpkg.val; i64 == 11", res: "true"},
{pre: func() { eval(t, i, "k := 1") }, src: `k := "Hello world"`, res: "Hello world"}, // allow reassignment in subsequent evaluations
})
}
@@ -139,8 +140,8 @@ func TestEvalBuiltin(t *testing.T) {
{src: `g := cap(a)`, res: "1"},
{src: `g := len("test")`, res: "4"},
{src: `g := len(map[string]string{"a": "b"})`, res: "1"},
{src: `a := len()`, err: "not enough arguments in call to len"},
{src: `a := len([]int, 0)`, err: "too many arguments for len"},
{src: `n := len()`, err: "not enough arguments in call to len"},
{src: `n := len([]int, 0)`, err: "too many arguments for len"},
{src: `g := cap("test")`, err: "1:37: invalid argument for cap"},
{src: `g := cap(map[string]string{"a": "b"})`, err: "1:37: invalid argument for cap"},
{src: `h := make(chan int, 1); close(h); len(h)`, res: "0"},
@@ -182,6 +183,15 @@ func TestEvalDecl(t *testing.T) {
})
}
func TestEvalDeclWithExpr(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
{src: `a1 := ""; var a2 int; a2 = 2`, res: "2"},
{src: `b1 := ""; const b2 = 2; b2`, res: "2"},
{src: `c1 := ""; var c2, c3 [8]byte; c3[3]`, res: "0"},
})
}
func TestEvalFunc(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
@@ -652,7 +662,7 @@ func TestEvalMissingSymbol(t *testing.T) {
F S2
}
i := interp.New(interp.Options{})
i.Use(interp.Exports{"p": map[string]reflect.Value{
i.Use(interp.Exports{"p/p": map[string]reflect.Value{
"S1": reflect.Zero(reflect.TypeOf(&S1{})),
}})
_, err := i.Eval(`import "p"`)
@@ -816,6 +826,7 @@ func assertEval(t *testing.T, i *interp.Interpreter, src, expectedError, expecte
}
func TestMultiEval(t *testing.T) {
t.Skip("fail in CI only ?")
// catch stdout
backupStdout := os.Stdout
defer func() {
@@ -862,6 +873,7 @@ func TestMultiEval(t *testing.T) {
}
func TestMultiEvalNoName(t *testing.T) {
t.Skip("fail in CI only ?")
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
var err error
@@ -949,9 +961,6 @@ func TestImportPathIsKey(t *testing.T) {
}
packages := i.Packages()
if len(packages) != len(wantPackages) {
t.Fatalf("want %d, got %d", len(wantPackages), len(packages))
}
for k, v := range wantPackages {
pkg := packages[k]
if pkg != v {
@@ -1388,11 +1397,11 @@ func applyCIMultiplier(timeout time.Duration) time.Duration {
return time.Duration(float64(timeout) * CITimeoutMultiplier)
}
func TestREPLDivision(t *testing.T) {
func TestREPLCommands(t *testing.T) {
if testing.Short() {
return
}
_ = os.Setenv("YAEGI_PROMPT", "1")
_ = os.Setenv("YAEGI_PROMPT", "1") // To force prompts over non-tty streams
defer func() {
_ = os.Setenv("YAEGI_PROMPT", "0")
}()
@@ -1421,12 +1430,16 @@ func TestREPLDivision(t *testing.T) {
`7/3`,
`16/5`,
`3./2`, // float
`reflect.TypeOf(math_rand.Int)`,
`reflect.TypeOf(crypto_rand.Int)`,
}
output := []string{
`1`,
`2`,
`3`,
`1.5`,
`func() int`,
`func(io.Reader, *big.Int) (*big.Int, error)`,
}
go func() {
@@ -1478,3 +1491,16 @@ func TestREPLDivision(t *testing.T) {
t.Fatal("timeout")
}
}
func TestStdio(t *testing.T) {
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
i.ImportUsed()
if _, err := i.Eval(`var x = os.Stdout`); err != nil {
t.Fatal(err)
}
v, _ := i.Eval(`x`)
if _, ok := v.Interface().(*os.File); !ok {
t.Fatalf("%v not *os.file", v.Interface())
}
}

View File

@@ -45,7 +45,7 @@ func TestInterface(t *testing.T) {
i := interp.New(interp.Options{})
// export the Wrap type to the interpreter under virtual "wrap" package
i.Use(interp.Exports{
"wrap": {
"wrap/wrap": {
"Wrap": reflect.ValueOf((*Wrap)(nil)),
},
})
@@ -66,3 +66,26 @@ func NewMyInt(i int) wrap.Wrap {
w := NewMyInt(4)
Hi(w)
}
type T struct{}
func (t T) Bar(s ...string) {}
func TestCallBinVariadicMethod(t *testing.T) {
i := interp.New(interp.Options{})
i.Use(interp.Exports{
"mypkg/mypkg": {
"T": reflect.ValueOf((*T)(nil)),
},
})
eval(t, i, `
package p
import "mypkg"
func Foo(x mypkg.T) { x.Bar("s") }
`)
v := eval(t, i, "p.Foo")
bar := v.Interface().(func(t T))
bar(T{})
}

View File

@@ -20,6 +20,11 @@ func TestFile(t *testing.T) {
filePath := "../_test/str.go"
runCheck(t, filePath)
defer func() {
_ = os.Setenv("YAEGI_SPECIAL_STDIO", "0")
}()
_ = os.Setenv("YAEGI_SPECIAL_STDIO", "1")
baseDir := filepath.Join("..", "_test")
files, err := ioutil.ReadDir(baseDir)
if err != nil {

View File

@@ -180,7 +180,7 @@ func runCfg(n *node, f *frame) {
if oNode == nil {
oNode = n
}
fmt.Println(oNode.cfgErrorf("panic"))
fmt.Fprintln(n.interp.stderr, oNode.cfgErrorf("panic"))
f.mutex.Unlock()
panic(f.recovered)
}
@@ -372,8 +372,7 @@ func typeAssert(n *node, withResult, withOk bool) {
}
return next
}
// empty interface
case n.child[0].typ.cat == interfaceT && len(n.child[0].typ.field) == 0:
case isEmptyInterface(n.child[0].typ):
n.exec = func(f *frame) bltn {
var ok bool
if setStatus {
@@ -503,7 +502,7 @@ func convert(n *node) {
if c.isNil() { // convert nil to type
// TODO(mpl): Try to completely remove, as maybe frameType already does the job for interfaces.
if n.child[0].typ.cat == interfaceT && len(n.child[0].typ.field) > 0 {
if isInterfaceSrc(n.child[0].typ) && !isEmptyInterface(n.child[0].typ) {
typ = reflect.TypeOf((*valueInterface)(nil)).Elem()
}
n.exec = func(f *frame) bltn {
@@ -513,7 +512,7 @@ func convert(n *node) {
return
}
if n.child[0].typ.cat == funcT && c.typ.cat == funcT {
if isFuncSrc(n.child[0].typ) && isFuncSrc(c.typ) {
value := genValue(c)
n.exec = func(f *frame) bltn {
n, ok := value(f).Interface().(*node)
@@ -531,9 +530,9 @@ func convert(n *node) {
doConvert := true
var value func(*frame) reflect.Value
switch {
case c.typ.cat == funcT:
case isFuncSrc(c.typ):
value = genFunctionWrapper(c)
case n.child[0].typ.cat == funcT && c.typ.cat == valueT:
case isFuncSrc(n.child[0].typ) && c.typ.cat == valueT:
doConvert = false
value = genValueNode(c)
default:
@@ -571,7 +570,7 @@ func isRecursiveType(t *itype, rtype reflect.Type) bool {
return true
}
switch t.cat {
case ptrT, arrayT, mapT:
case aliasT, arrayT, mapT, ptrT, sliceT:
return isRecursiveType(t.val, t.val.rtype)
default:
return false
@@ -590,37 +589,13 @@ func assign(n *node) {
for i := 0; i < n.nleft; i++ {
dest, src := n.child[i], n.child[sbase+i]
switch {
case dest.typ.cat == interfaceT:
if len(dest.typ.field) > 0 {
svalue[i] = genValueInterface(src)
break
}
svalue[i] = genValue(src)
case (dest.typ.cat == valueT || dest.typ.cat == errorT) && dest.typ.rtype.Kind() == reflect.Interface:
svalue[i] = genInterfaceWrapper(src, dest.typ.rtype)
case src.typ.cat == funcT && dest.typ.cat == valueT:
if isFuncSrc(src.typ) && isField(dest) {
svalue[i] = genFunctionWrapper(src)
case src.typ.cat == funcT && isField(dest):
svalue[i] = genFunctionWrapper(src)
case dest.typ.cat == funcT && src.typ.cat == valueT:
svalue[i] = genValueNode(src)
case src.kind == basicLit && src.val == nil:
t := dest.typ.TypeOf()
svalue[i] = func(*frame) reflect.Value { return reflect.New(t).Elem() }
case isRecursiveType(dest.typ, dest.typ.rtype):
svalue[i] = genValueRecursiveInterface(src, dest.typ.rtype)
case isRecursiveType(src.typ, src.typ.rtype):
svalue[i] = genValueRecursiveInterfacePtrValue(src)
case src.typ.untyped && isComplex(dest.typ.TypeOf()):
svalue[i] = genValueComplex(src)
case src.typ.untyped && !dest.typ.untyped:
svalue[i] = genValueAs(src, dest.typ.TypeOf())
default:
svalue[i] = genValue(src)
} else {
svalue[i] = genDestValue(dest.typ, src)
}
if isMapEntry(dest) {
if dest.child[1].typ.cat == interfaceT { // key
if isInterfaceSrc(dest.child[1].typ) { // key
ivalue[i] = genValueInterface(dest.child[1])
} else {
ivalue[i] = genValue(dest.child[1])
@@ -632,6 +607,7 @@ func assign(n *node) {
}
if n.nleft == 1 {
// Single assign operation.
switch s, d, i := svalue[0], dvalue[0], ivalue[0]; {
case n.child[0].ident == "_":
n.exec = func(f *frame) bltn {
@@ -642,51 +618,84 @@ func assign(n *node) {
d(f).SetMapIndex(i(f), s(f))
return next
}
case n.kind == defineStmt:
l := n.level
ind := n.findex
n.exec = func(f *frame) bltn {
data := getFrame(f, l).data
data[ind] = reflect.New(data[ind].Type()).Elem()
data[ind].Set(s(f))
return next
}
default:
n.exec = func(f *frame) bltn {
d(f).Set(s(f))
return next
}
}
} else {
types := make([]reflect.Type, n.nright)
for i := range types {
var t reflect.Type
switch typ := n.child[sbase+i].typ; typ.cat {
case funcT:
t = reflect.TypeOf((*node)(nil))
case interfaceT:
t = reflect.TypeOf((*valueInterface)(nil)).Elem()
default:
t = typ.TypeOf()
}
types[i] = t
}
return
}
// To handle swap in multi-assign:
// evaluate and copy all values in assign right hand side into temporary
// then evaluate assign left hand side and copy temporary into it
// Multi assign operation.
types := make([]reflect.Type, n.nright)
index := make([]int, n.nright)
level := make([]int, n.nright)
for i := range types {
var t reflect.Type
switch typ := n.child[sbase+i].typ; {
case isFuncSrc(typ):
t = reflect.TypeOf((*node)(nil))
case isInterfaceSrc(typ):
t = reflect.TypeOf((*valueInterface)(nil)).Elem()
default:
t = typ.TypeOf()
}
types[i] = t
index[i] = n.child[i].findex
level[i] = n.child[i].level
}
if n.kind == defineStmt {
// Handle a multiple var declararation / assign. It cannot be a swap.
n.exec = func(f *frame) bltn {
t := make([]reflect.Value, len(svalue))
for i, s := range svalue {
if n.child[i].ident == "_" {
continue
}
t[i] = reflect.New(types[i]).Elem()
t[i].Set(s(f))
}
for i, d := range dvalue {
if n.child[i].ident == "_" {
continue
}
if j := ivalue[i]; j != nil {
d(f).SetMapIndex(j(f), t[i]) // Assign a map entry
} else {
d(f).Set(t[i]) // Assign a var or array/slice entry
}
data := getFrame(f, level[i]).data
j := index[i]
data[j] = reflect.New(data[j].Type()).Elem()
data[j].Set(s(f))
}
return next
}
return
}
// To handle possible swap in multi-assign:
// evaluate and copy all values in assign right hand side into temporary
// then evaluate assign left hand side and copy temporary into it
n.exec = func(f *frame) bltn {
t := make([]reflect.Value, len(svalue))
for i, s := range svalue {
if n.child[i].ident == "_" {
continue
}
t[i] = reflect.New(types[i]).Elem()
t[i].Set(s(f))
}
for i, d := range dvalue {
if n.child[i].ident == "_" {
continue
}
if j := ivalue[i]; j != nil {
d(f).SetMapIndex(j(f), t[i]) // Assign a map entry
} else {
d(f).Set(t[i]) // Assign a var or array/slice entry
}
}
return next
}
}
@@ -718,19 +727,20 @@ func addr(n *node) {
next := getExec(n.tnext)
c0 := n.child[0]
value := genValue(c0)
switch c0.typ.cat {
case interfaceT, ptrT:
if isInterfaceSrc(c0.typ) || isPtrSrc(c0.typ) {
i := n.findex
l := n.level
n.exec = func(f *frame) bltn {
getFrame(f, l).data[i] = value(f).Addr()
return next
}
default:
n.exec = func(f *frame) bltn {
dest(f).Set(value(f).Addr())
return next
}
return
}
n.exec = func(f *frame) bltn {
dest(f).Set(value(f).Addr())
return next
}
}
@@ -804,14 +814,14 @@ func _recover(n *node) {
n.exec = func(f *frame) bltn {
if f.anc.recovered == nil {
// TODO(mpl): maybe we don't need that special case, and we're just forgetting to unwrap the valueInterface somewhere else.
if n.typ.cat == interfaceT && len(n.typ.field) == 0 {
if isEmptyInterface(n.typ) {
return tnext
}
dest(f).Set(reflect.ValueOf(valueInterface{}))
return tnext
}
if n.typ.cat == interfaceT && len(n.typ.field) == 0 {
if isEmptyInterface(n.typ) {
dest(f).Set(reflect.ValueOf(f.anc.recovered))
} else {
dest(f).Set(reflect.ValueOf(valueInterface{n, reflect.ValueOf(f.anc.recovered)}))
@@ -892,7 +902,7 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
funcType := n.typ.TypeOf()
return func(f *frame) reflect.Value {
if n.frame != nil { // Use closure context if defined
if n.frame != nil { // Use closure context if defined.
f = n.frame
}
return reflect.MakeFunc(funcType, func(in []reflect.Value) []reflect.Value {
@@ -903,7 +913,7 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
d[i] = reflect.New(t).Elem()
}
// Copy method receiver as first argument, if defined
// Copy method receiver as first argument, if defined.
if rcvr != nil {
src, dest := rcvr(f), d[numRet]
if src.Type().Kind() != dest.Type().Kind() {
@@ -919,24 +929,22 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
d = d[numRet:]
}
// Copy function input arguments in local frame
// Copy function input arguments in local frame.
for i, arg := range in {
typ := def.typ.arg[i]
switch {
case typ.cat == interfaceT:
if len(typ.field) > 0 {
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
break
}
case isEmptyInterface(typ):
d[i].Set(arg)
case typ.cat == funcT && arg.Kind() == reflect.Func:
case isInterfaceSrc(typ):
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
case isFuncSrc(typ) && arg.Kind() == reflect.Func:
d[i].Set(reflect.ValueOf(genFunctionNode(arg)))
default:
d[i].Set(arg)
}
}
// Interpreter code execution
// Interpreter code execution.
runCfg(start, fr)
result := fr.data[:numRet]
@@ -1027,7 +1035,7 @@ func call(n *node) {
values = append(values, genValueRecv(n.child[0]))
}
method = true
case len(n.child[0].child) > 0 && n.child[0].child[0].typ != nil && n.child[0].child[0].typ.cat == interfaceT:
case len(n.child[0].child) > 0 && n.child[0].child[0].typ != nil && isInterfaceSrc(n.child[0].child[0].typ):
recvIndexLater = true
values = append(values, genValueBinRecv(n.child[0], &receiver{node: n.child[0].child[0]}))
value = genValueBinMethodOnInterface(n, value)
@@ -1072,13 +1080,10 @@ func call(n *node) {
convertLiteralValue(c, argType)
}
switch {
case arg.cat == interfaceT:
if len(arg.field) > 0 {
values = append(values, genValueInterface(c))
break
}
// empty interface, do not wrap it.
case isEmptyInterface(arg):
values = append(values, genValue(c))
case isInterfaceSrc(arg):
values = append(values, genValueInterface(c))
case isRecursiveType(c.typ, c.typ.rtype):
values = append(values, genValueRecursiveInterfacePtrValue(c))
default:
@@ -1097,13 +1102,8 @@ func call(n *node) {
switch {
case c.ident == "_":
// Skip assigning return value to blank var.
case c.typ.cat == interfaceT && rtypes[i].cat != interfaceT:
if len(c.typ.field) > 0 {
rvalues[i] = genValueInterfaceValue(c)
break
}
// empty interface, do not wrap
fallthrough
case isInterfaceSrc(c.typ) && !isEmptyInterface(c.typ) && !isInterfaceSrc(rtypes[i]):
rvalues[i] = genValueInterfaceValue(c)
default:
rvalues[i] = genValue(c)
}
@@ -1114,7 +1114,8 @@ func call(n *node) {
j := n.findex + i
ret := n.child[0].typ.ret[i]
callret := n.anc.val.(*node).typ.ret[i]
if callret.cat == interfaceT && len(callret.field) > 0 && ret.cat != interfaceT {
if isInterfaceSrc(callret) && !isEmptyInterface(callret) && !isInterfaceSrc(ret) {
// Wrap the returned value in a valueInterface in caller frame.
rvalues[i] = func(f *frame) reflect.Value {
v := reflect.New(ret.rtype).Elem()
@@ -1300,16 +1301,18 @@ func callBin(n *node) {
tnext := getExec(n.tnext)
fnext := getExec(n.fnext)
child := n.child[1:]
value := genValue(n.child[0])
c0 := n.child[0]
value := genValue(c0)
var values []func(*frame) reflect.Value
funcType := n.child[0].typ.rtype
funcType := c0.typ.rtype
wt := wrappedType(c0)
variadic := -1
if funcType.IsVariadic() {
variadic = funcType.NumIn() - 1
}
// A method signature obtained from reflect.Type includes receiver as 1st arg, except for interface types.
rcvrOffset := 0
if recv := n.child[0].recv; recv != nil && !isInterface(recv.node.typ) {
if recv := c0.recv; recv != nil && !isInterface(recv.node.typ) {
if variadic > 0 || funcType.NumIn() > len(child) {
rcvrOffset = 1
}
@@ -1323,7 +1326,7 @@ func callBin(n *node) {
for i, c := range child {
var defType reflect.Type
if variadic >= 0 && i >= variadic {
if variadic >= 0 && i+rcvrOffset >= variadic {
defType = funcType.In(variadic)
} else {
defType = funcType.In(rcvrOffset + i)
@@ -1347,7 +1350,7 @@ func callBin(n *node) {
if c.kind == basicLit || c.rval.IsValid() {
// Convert literal value (untyped) to function argument type (if not an interface{})
var argType reflect.Type
if variadic >= 0 && i >= variadic {
if variadic >= 0 && i+rcvrOffset >= variadic {
argType = funcType.In(variadic).Elem()
} else {
argType = funcType.In(i + rcvrOffset)
@@ -1358,34 +1361,34 @@ func callBin(n *node) {
}
}
switch c.typ.cat {
case funcT:
if wt != nil && isInterfaceSrc(wt.arg[i]) {
values = append(values, genValueInterface(c))
break
}
switch {
case isFuncSrc(c.typ):
values = append(values, genFunctionWrapper(c))
case interfaceT:
if len(c.typ.field) > 0 {
values = append(values, genValueInterfaceValue(c))
break
}
// empty interface, do not wrap it.
case isEmptyInterface(c.typ):
values = append(values, genValue(c))
case arrayT, variadicT:
switch c.typ.val.cat {
case interfaceT:
if len(c.typ.val.field) > 0 {
values = append(values, genValueInterfaceArray(c))
break
}
case isInterfaceSrc(c.typ):
values = append(values, genValueInterfaceValue(c))
case c.typ.cat == arrayT || c.typ.cat == variadicT:
switch {
case isEmptyInterface(c.typ.val):
values = append(values, genValueArray(c))
case isInterfaceSrc(c.typ.val):
values = append(values, genValueInterfaceArray(c))
default:
values = append(values, genInterfaceWrapper(c, defType))
}
case ptrT:
case isPtrSrc(c.typ):
if c.typ.val.cat == valueT {
values = append(values, genValue(c))
} else {
values = append(values, genInterfaceWrapper(c, defType))
}
case valueT:
case c.typ.cat == valueT:
values = append(values, genValue(c))
default:
values = append(values, genInterfaceWrapper(c, defType))
@@ -1444,7 +1447,7 @@ func callBin(n *node) {
for i := range rvalues {
c := n.anc.child[i]
if c.ident != "_" {
if c.typ.cat == interfaceT {
if isInterfaceSrc(c.typ) {
rvalues[i] = genValueInterfaceValue(c)
} else {
rvalues[i] = genValue(c)
@@ -1598,7 +1601,7 @@ func getIndexMap(n *node) {
dest(f).Set(z)
return fnext
}
case n.typ.cat == interfaceT:
case isInterfaceSrc(n.typ):
z = reflect.New(n.child[0].typ.val.frameType()).Elem()
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(mi)
@@ -1643,7 +1646,7 @@ func getIndexMap(n *node) {
dest(f).Set(z)
return fnext
}
case n.typ.cat == interfaceT:
case isInterfaceSrc(n.typ):
z = reflect.New(n.child[0].typ.val.frameType()).Elem()
n.exec = func(f *frame) bltn {
if v := value0(f).MapIndex(value1(f)); v.IsValid() {
@@ -1693,7 +1696,7 @@ func getIndexMap2(n *node) {
value2(f).SetBool(v.IsValid())
return next
}
case typ.cat == interfaceT:
case isInterfaceSrc(typ):
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(mi)
if v.IsValid() {
@@ -1729,7 +1732,7 @@ func getIndexMap2(n *node) {
value2(f).SetBool(v.IsValid())
return next
}
case typ.cat == interfaceT:
case isInterfaceSrc(typ):
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(value1(f))
if v.IsValid() {
@@ -1759,12 +1762,14 @@ func getIndexMap2(n *node) {
}
}
const fork = true // Duplicate frame in frame.clone().
func getFunc(n *node) {
dest := genValue(n)
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
fr := f.clone()
fr := f.clone(fork)
nod := *n
nod.val = &nod
nod.frame = fr
@@ -1779,7 +1784,7 @@ func getMethod(n *node) {
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
fr := f.clone()
fr := f.clone(!fork)
nod := *(n.val.(*node))
nod.val = &nod
nod.recv = n.recv
@@ -1815,7 +1820,7 @@ func getMethodByName(n *node) {
return next
}
m, li := val.node.typ.lookupMethod(name)
fr := f.clone()
fr := f.clone(!fork)
nod := *m
nod.val = &nod
nod.recv = &receiver{nil, val.value, li}
@@ -2230,12 +2235,16 @@ func _return(n *node) {
case 0:
n.exec = nil
case 1:
// This is an optimisation that is applied for binary expressions or function
// calls, but not for (binary) expressions involving const, as the values are not
// stored in the frame in that case.
if !child[0].rval.IsValid() && child[0].kind == binaryExpr || isCall(child[0]) {
switch {
case !child[0].rval.IsValid() && child[0].kind == binaryExpr:
// No additional runtime operation is necessary for constants (not in frame) or
// binary expressions (stored directly at the right location in frame).
n.exec = nil
} else {
case isCall(child[0]) && n.child[0].typ.id() == def.typ.ret[0].id():
// Calls are optmized as long as no type conversion is involved.
n.exec = nil
default:
// Regular return: store the value to return at to start of the frame.
v := values[0]
n.exec = func(f *frame) bltn {
f.data[0].Set(v(f))
@@ -2269,25 +2278,14 @@ func arrayLit(n *node) {
values := make([]func(*frame) reflect.Value, len(child))
index := make([]int, len(child))
rtype := n.typ.val.TypeOf()
var max, prev int
for i, c := range child {
if c.kind == keyValueExpr {
convertLiteralValue(c.child[1], rtype)
if n.typ.val.cat == interfaceT && len(n.typ.val.field) > 0 {
values[i] = genValueInterface(c.child[1])
} else {
values[i] = genValue(c.child[1])
}
values[i] = genDestValue(n.typ.val, c.child[1])
index[i] = int(vInt(c.child[0].rval))
} else {
convertLiteralValue(c, rtype)
if n.typ.val.cat == interfaceT && len(n.typ.val.field) > 0 {
values[i] = genValueInterface(c)
} else {
values[i] = genValue(c)
}
values[i] = genDestValue(n.typ.val, c)
index[i] = prev
}
prev = index[i] + 1
@@ -2297,12 +2295,13 @@ func arrayLit(n *node) {
}
typ := n.typ.frameType()
kind := typ.Kind()
n.exec = func(f *frame) bltn {
var a reflect.Value
if n.typ.sizedef {
a, _ = n.typ.zero()
} else {
if kind == reflect.Slice {
a = reflect.MakeSlice(typ, max, max)
} else {
a, _ = n.typ.zero()
}
for i, v := range values {
a.Index(index[i]).Set(v(f))
@@ -2319,22 +2318,12 @@ func mapLit(n *node) {
if n.nleft == 1 {
child = n.child[1:]
}
typ := n.typ.TypeOf()
typ := n.typ.frameType()
keys := make([]func(*frame) reflect.Value, len(child))
values := make([]func(*frame) reflect.Value, len(child))
for i, c := range child {
convertLiteralValue(c.child[0], n.typ.key.TypeOf())
convertLiteralValue(c.child[1], n.typ.val.TypeOf())
if n.typ.key.cat == interfaceT {
keys[i] = genValueInterface(c.child[0])
} else {
keys[i] = genValue(c.child[0])
}
if n.typ.val.cat == interfaceT && len(n.typ.val.field) > 0 {
values[i] = genValueInterface(c.child[1])
} else {
values[i] = genValue(c.child[1])
}
keys[i] = genDestValue(n.typ.key, c.child[0])
values[i] = genDestValue(n.typ.val, c.child[1])
}
n.exec = func(f *frame) bltn {
@@ -2354,7 +2343,7 @@ func compositeBinMap(n *node) {
if n.nleft == 1 {
child = n.child[1:]
}
typ := n.typ.TypeOf()
typ := n.typ.frameType()
keys := make([]func(*frame) reflect.Value, len(child))
values := make([]func(*frame) reflect.Value, len(child))
for i, c := range child {
@@ -2362,7 +2351,7 @@ func compositeBinMap(n *node) {
convertLiteralValue(c.child[1], typ.Elem())
keys[i] = genValue(c.child[0])
if c.child[1].typ.cat == funcT {
if isFuncSrc(c.child[1].typ) {
values[i] = genFunctionWrapper(c.child[1])
} else {
values[i] = genValue(c.child[1])
@@ -2409,12 +2398,13 @@ func compositeBinSlice(n *node) {
}
typ := n.typ.frameType()
kind := typ.Kind()
n.exec = func(f *frame) bltn {
var a reflect.Value
if n.typ.sizedef {
a, _ = n.typ.zero()
} else {
if kind == reflect.Slice {
a = reflect.MakeSlice(typ, max, max)
} else {
a, _ = n.typ.zero()
}
for i, v := range values {
a.Index(index[i]).Set(v(f))
@@ -2443,7 +2433,7 @@ func doCompositeBinStruct(n *node, hasType bool) {
if sf, ok := typ.FieldByName(c.child[0].ident); ok {
fieldIndex[i] = sf.Index
convertLiteralValue(c.child[1], sf.Type)
if c.child[1].typ.cat == funcT {
if isFuncSrc(c.child[1].typ) {
values[i] = genFunctionWrapper(c.child[1])
} else {
values[i] = genValue(c.child[1])
@@ -2451,7 +2441,7 @@ func doCompositeBinStruct(n *node, hasType bool) {
}
} else {
fieldIndex[i] = []int{i}
if c.typ.cat == funcT && len(c.child) > 1 {
if isFuncSrc(c.typ) && len(c.child) > 1 {
convertLiteralValue(c.child[1], typ.Field(i).Type)
values[i] = genFunctionWrapper(c.child[1])
} else {
@@ -2502,7 +2492,7 @@ func doComposite(n *node, hasType bool, keyed bool) {
if hasType {
child = n.child[1:]
}
destInterface := destType(n).cat == interfaceT
destInterface := isInterfaceSrc(destType(n))
values := make(map[int]func(*frame) reflect.Value)
for i, c := range child {
@@ -2521,9 +2511,9 @@ func doComposite(n *node, hasType bool, keyed bool) {
switch {
case val.typ.cat == nilT:
values[fieldIndex] = func(*frame) reflect.Value { return reflect.New(rft).Elem() }
case val.typ.cat == funcT:
values[fieldIndex] = genFunctionWrapper(val)
case isArray(val.typ) && val.typ.val != nil && val.typ.val.cat == interfaceT:
case isFuncSrc(val.typ):
values[fieldIndex] = genValueAsFunctionWrapper(val)
case isArray(val.typ) && val.typ.val != nil && isInterfaceSrc(val.typ.val):
values[fieldIndex] = genValueInterfaceArray(val)
case isRecursiveType(ft, rft):
values[fieldIndex] = genValueRecursiveInterface(val, rft)
@@ -2584,31 +2574,50 @@ var rat = reflect.ValueOf((*[]rune)(nil)).Type().Elem() // runes array type
func _range(n *node) {
index0 := n.child[0].findex // array index location in frame
index2 := index0 - 1 // shallow array for range, always just behind index0
index3 := index2 - 1 // additional location to store string char position
fnext := getExec(n.fnext)
tnext := getExec(n.tnext)
var value func(*frame) reflect.Value
var an *node
if len(n.child) == 4 {
an := n.child[2]
an = n.child[2]
index1 := n.child[1].findex // array value location in frame
if isString(an.typ.TypeOf()) {
// Special variant of "range" for string, where the index indicates the byte position
// of the rune in the string, rather than the index of the rune in array.
stringType := reflect.TypeOf("")
value = genValueAs(an, rat) // range on string iterates over runes
n.exec = func(f *frame) bltn {
a := f.data[index2]
v0 := f.data[index3]
v0.SetInt(v0.Int() + 1)
i := int(v0.Int())
if i >= a.Len() {
return fnext
}
// Compute byte position of the rune in string
pos := a.Slice(0, i).Convert(stringType).Len()
f.data[index0].SetInt(int64(pos))
f.data[index1].Set(a.Index(i))
return tnext
}
} else {
value = genValueRangeArray(an)
}
n.exec = func(f *frame) bltn {
a := f.data[index2]
v0 := f.data[index0]
v0.SetInt(v0.Int() + 1)
i := int(v0.Int())
if i >= a.Len() {
return fnext
n.exec = func(f *frame) bltn {
a := f.data[index2]
v0 := f.data[index0]
v0.SetInt(v0.Int() + 1)
i := int(v0.Int())
if i >= a.Len() {
return fnext
}
f.data[index1].Set(a.Index(i))
return tnext
}
f.data[index1].Set(a.Index(i))
return tnext
}
} else {
an := n.child[1]
an = n.child[1]
if isString(an.typ.TypeOf()) {
value = genValueAs(an, rat) // range on string iterates over runes
} else {
@@ -2626,9 +2635,13 @@ func _range(n *node) {
// Init sequence
next := n.exec
index := index0
if isString(an.typ.TypeOf()) && len(n.child) == 4 {
index = index3
}
n.child[0].exec = func(f *frame) bltn {
f.data[index2] = value(f) // set array shallow copy for range
f.data[index0].SetInt(-1) // assing index value
f.data[index].SetInt(-1) // assing index value
return next
}
}
@@ -2666,7 +2679,7 @@ func rangeMap(n *node) {
if len(n.child) == 4 {
index1 := n.child[1].findex // map value location in frame
value = genValue(n.child[2]) // map
if n.child[1].typ.cat == interfaceT {
if isInterfaceSrc(n.child[1].typ) {
if len(n.child[1].typ.field) > 0 {
n.exec = func(f *frame) bltn {
iter := f.data[index2].Interface().(*reflect.MapIter)
@@ -2904,7 +2917,7 @@ func _append(n *node) {
if len(n.child) == 3 {
c1, c2 := n.child[1], n.child[2]
if (c1.typ.cat == valueT || c2.typ.cat == valueT) && c1.typ.rtype == c2.typ.rtype ||
c2.typ.cat == arrayT && c2.typ.val.id() == n.typ.val.id() ||
(c2.typ.cat == arrayT || c2.typ.cat == sliceT || c2.typ.cat == variadicT) && c2.typ.val.id() == n.typ.val.id() ||
isByteArray(c1.typ.TypeOf()) && isString(c2.typ.TypeOf()) {
appendSlice(n)
return
@@ -2921,12 +2934,10 @@ func _append(n *node) {
values := make([]func(*frame) reflect.Value, l)
for i, arg := range args {
switch {
case n.typ.val.cat == interfaceT:
if len(n.typ.val.field) > 0 {
values[i] = genValueInterface(arg)
break
}
case isEmptyInterface(n.typ.val):
values[i] = genValue(arg)
case isInterfaceSrc(n.typ.val):
values[i] = genValueInterface(arg)
case isRecursiveType(n.typ.val, n.typ.val.rtype):
values[i] = genValueRecursiveInterface(arg, n.typ.val.rtype)
case arg.typ.untyped:
@@ -2947,12 +2958,10 @@ func _append(n *node) {
} else {
var value0 func(*frame) reflect.Value
switch {
case n.typ.val.cat == interfaceT:
if len(n.typ.val.field) > 0 {
value0 = genValueInterface(n.child[2])
break
}
case isEmptyInterface(n.typ.val):
value0 = genValue(n.child[2])
case isInterfaceSrc(n.typ.val):
value0 = genValueInterface(n.child[2])
case isRecursiveType(n.typ.val, n.typ.val.rtype):
value0 = genValueRecursiveInterface(n.child[2], n.typ.val.rtype)
case n.child[2].typ.untyped:
@@ -3396,15 +3405,7 @@ func send(n *node) {
next := getExec(n.tnext)
c0, c1 := n.child[0], n.child[1]
value0 := genValue(c0) // Send channel.
convertLiteralValue(c1, c0.typ.val.TypeOf())
var value1 func(*frame) reflect.Value // Value to send.
switch {
case isInterfaceBin(c0.typ.val):
value1 = genInterfaceWrapper(c1, c0.typ.val.rtype)
default:
value1 = genValue(c1)
}
value1 := genDestValue(c0.typ.val, c1)
if !n.interp.cancelChan {
// Send is non-cancellable, has the least overhead.
@@ -3614,7 +3615,7 @@ func slice0(n *node) {
func isNil(n *node) {
var value func(*frame) reflect.Value
c0 := n.child[0]
if c0.typ.cat == funcT {
if isFuncSrc(c0.typ) {
value = genValueAsFunctionWrapper(c0)
} else {
value = genValue(c0)
@@ -3625,7 +3626,7 @@ func isNil(n *node) {
dest := genValue(n)
if n.fnext == nil {
if c0.typ.cat != interfaceT {
if !isInterfaceSrc(c0.typ) {
if isInterface {
n.exec = func(f *frame) bltn {
dest(f).Set(reflect.ValueOf(value(f).IsNil()).Convert(typ))
@@ -3669,7 +3670,7 @@ func isNil(n *node) {
fnext := getExec(n.fnext)
if c0.typ.cat != interfaceT {
if !isInterfaceSrc(c0.typ) {
n.exec = func(f *frame) bltn {
if value(f).IsNil() {
dest(f).SetBool(true)
@@ -3703,7 +3704,7 @@ func isNil(n *node) {
func isNotNil(n *node) {
var value func(*frame) reflect.Value
c0 := n.child[0]
if c0.typ.cat == funcT {
if isFuncSrc(c0.typ) {
value = genValueAsFunctionWrapper(c0)
} else {
value = genValue(c0)
@@ -3714,7 +3715,7 @@ func isNotNil(n *node) {
dest := genValue(n)
if n.fnext == nil {
if c0.typ.cat != interfaceT {
if isInterfaceSrc(c0.typ) {
if isInterface {
n.exec = func(f *frame) bltn {
dest(f).Set(reflect.ValueOf(!value(f).IsNil()).Convert(typ))
@@ -3759,7 +3760,7 @@ func isNotNil(n *node) {
fnext := getExec(n.fnext)
if c0.typ.cat != interfaceT {
if isInterfaceSrc(c0.typ) {
n.exec = func(f *frame) bltn {
if value(f).IsNil() {
dest(f).SetBool(false)

View File

@@ -0,0 +1,45 @@
package interp_test
import (
"log"
"github.com/traefik/yaegi/interp"
"github.com/traefik/yaegi/stdlib"
)
func ExampleInterpreter_self() {
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
i.Use(interp.Symbols)
_, err := i.Eval(`import (
"fmt"
"log"
// Import interp to gain access to Self.
"github.com/traefik/yaegi/interp"
)`)
if err != nil {
log.Fatal(err)
}
_, err = i.Eval(`
// Evaluate code directly.
fmt.Println("Hello Yaegi from Go")
// Evaluate code indirectly via the Self access point.
_, err := interp.Self.Eval("fmt.Println(\"Hello Yaegi from Yaegi\")")
if err != nil {
log.Fatal(err)
}
`)
if err != nil {
log.Fatal(err)
}
// Output:
//
// Hello Yaegi from Go
// Hello Yaegi from Yaegi
}

View File

@@ -303,7 +303,7 @@ func effectivePkg(root, path string) string {
}
// isPathRelative returns true if path starts with "./" or "../".
// It is intended for use on import paths, where "/" is always the directory separator.
func isPathRelative(s string) bool {
p := "." + string(filepath.Separator)
return strings.HasPrefix(s, p) || strings.HasPrefix(s, "."+p)
return strings.HasPrefix(s, "./") || strings.HasPrefix(s, "../")
}

View File

@@ -38,6 +38,7 @@ const (
int64T
mapT
ptrT
sliceT
srcPkgT
stringT
structT
@@ -75,6 +76,7 @@ var cats = [...]string{
int64T: "int64T",
mapT: "mapT",
ptrT: "ptrT",
sliceT: "sliceT",
srcPkgT: "srcPkgT",
stringT: "stringT",
structT: "structT",
@@ -109,19 +111,18 @@ 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, sliceT 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
method []*node // Associated methods or nil
name string // name of type within its package for a defined type
path string // for a defined type, the package import path
size int // Size of array if ArrayT
length int // length of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil
incomplete bool // true if type must be parsed again (out of order declarations)
recursive bool // true if the type has an element which refer to itself
untyped bool // true for a literal value (string or number)
sizedef bool // true if array size is computed from type definition
isBinMethod bool // true if the type refers to a bin method function
node *node // root AST node of type definition
scope *scope // type declaration scope (in case of re-parse incomplete type)
@@ -134,12 +135,13 @@ func untypedInt() *itype { return &itype{cat: intT, name: "int", untyped: tr
func untypedFloat() *itype { return &itype{cat: float64T, name: "float64", untyped: true} }
func untypedComplex() *itype { return &itype{cat: complex128T, name: "complex128", untyped: true} }
func errorMethodType(sc *scope) *itype {
return &itype{cat: funcT, ret: []*itype{sc.getType("string")}}
}
// nodeType returns a type definition for the corresponding AST subtree.
func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
if n.typ != nil && !n.typ.incomplete {
if n.kind == sliceExpr {
n.typ.sizedef = false
}
return n.typ, nil
}
@@ -165,10 +167,10 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
t.incomplete = t.val.incomplete
case arrayType:
t.cat = arrayT
c0 := n.child[0]
if len(n.child) == 1 {
// Array size is not defined.
t.cat = sliceT
if t.val, err = nodeType(interp, sc, c0); err != nil {
return nil, err
}
@@ -176,18 +178,19 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
break
}
// Array size is defined.
t.cat = arrayT
switch v := c0.rval; {
case v.IsValid():
// Size if defined by a constant litteral value.
if isConstantValue(v.Type()) {
c := v.Interface().(constant.Value)
t.size = constToInt(c)
t.length = constToInt(c)
} else {
t.size = int(v.Int())
t.length = int(v.Int())
}
case c0.kind == ellipsisExpr:
// [...]T expression, get size from the length of composite array.
t.size = arrayTypeLen(n.anc)
t.length = arrayTypeLen(n.anc)
case c0.kind == identExpr:
sym, _, ok := sc.lookup(c0.ident)
if !ok {
@@ -203,11 +206,11 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
break
}
if v, ok := sym.rval.Interface().(int); ok {
t.size = v
t.length = v
break
}
if c, ok := sym.rval.Interface().(constant.Value); ok {
t.size = constToInt(c)
t.length = constToInt(c)
break
}
t.incomplete = true
@@ -221,12 +224,11 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
t.incomplete = true
break
}
t.size = constToInt(v)
t.length = constToInt(v)
}
if t.val, err = nodeType(interp, sc, n.child[1]); err != nil {
return nil, err
}
t.sizedef = true
t.incomplete = t.incomplete || t.val.incomplete
case basicLit:
@@ -275,40 +277,26 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
}
// Because an empty interface concrete type "mutates" as different values are
// assigned to it, we need to make a new itype from scratch everytime a new
// assignment is made, and not let different nodes (of the same variable) share the
// same itype. Otherwise they would overwrite each other.
if n.anc.kind == assignStmt && isInterface(n.anc.child[0].typ) && len(n.anc.child[0].typ.field) == 0 {
// TODO(mpl): do the indexes properly for multiple assignments on the same line.
// Also, maybe we should use nodeType to figure out dt.cat? but isn't it always
// gonna be an interfaceT anyway?
dt := new(itype)
dt.cat = interfaceT
val := new(itype)
val.cat = t.cat
dt.val = val
// TODO(mpl): do the indexes properly for multiple assignments on the same line.
// Also, maybe we should use nodeType to figure out dt.cat? but isn't it always
// gonna be an interfaceT anyway?
n.anc.child[0].typ = dt
// TODO(mpl): not sure yet whether we should do that last step. It doesn't seem
// to change anything either way though.
// t = dt
break
}
// If the node is to be assigned or returned, the node type is the destination type.
dt := t
switch a := n.anc; {
case a.kind == assignStmt && isEmptyInterface(a.child[0].typ):
// Because an empty interface concrete type "mutates" as different values are
// assigned to it, we need to make a new itype from scratch everytime a new
// assignment is made, and not let different nodes (of the same variable) share the
// same itype. Otherwise they would overwrite each other.
a.child[0].typ = &itype{cat: interfaceT, val: dt}
case a.kind == defineStmt && len(a.child) > a.nleft+a.nright:
if dt, err = nodeType(interp, sc, a.child[a.nleft]); err != nil {
return nil, err
}
case a.kind == returnStmt:
dt = sc.def.typ.ret[childPos(n)]
}
if isInterface(dt) {
dt.val = t
}
@@ -499,7 +487,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
break
}
switch lt.cat {
case arrayT, mapT:
case arrayT, mapT, sliceT, variadicT:
t = lt.val
}
@@ -512,21 +500,28 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
}
for _, field := range n.child[0].child {
f0 := field.child[0]
if len(field.child) == 1 {
typ, err := nodeType(interp, sc, field.child[0])
if f0.ident == "error" {
// Unwrap error interface inplace rather than embedding it, because
// "error" is lower case which may cause problems with reflect for method lookup.
t.field = append(t.field, structField{name: "Error", typ: errorMethodType(sc)})
continue
}
typ, err := nodeType(interp, sc, f0)
if err != nil {
return nil, err
}
t.field = append(t.field, structField{name: fieldName(field.child[0]), embed: true, typ: typ})
incomplete = incomplete || typ.incomplete
} else {
typ, err := nodeType(interp, sc, field.child[1])
if err != nil {
return nil, err
}
t.field = append(t.field, structField{name: field.child[0].ident, typ: typ})
t.field = append(t.field, structField{name: fieldName(f0), embed: true, typ: typ})
incomplete = incomplete || typ.incomplete
continue
}
typ, err := nodeType(interp, sc, field.child[1])
if err != nil {
return nil, err
}
t.field = append(t.field, structField{name: f0.ident, typ: typ})
incomplete = incomplete || typ.incomplete
}
t.incomplete = incomplete
@@ -615,15 +610,14 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
case sliceExpr:
t, err = nodeType(interp, sc, n.child[0])
if err != nil {
return nil, err
}
if t.cat == ptrT {
t = t.val
}
if err == nil && t.size != 0 {
t1 := *t
t1.size = 0
t1.sizedef = false
t1.rtype = nil
t = &t1
if t.cat == arrayT {
t = &itype{cat: sliceT, val: t.val, incomplete: t.incomplete, node: n, scope: sc}
}
case structType:
@@ -775,7 +769,7 @@ func (t *itype) referTo(name string, seen map[*itype]bool) bool {
}
seen[t] = true
switch t.cat {
case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT:
case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT, sliceT, variadicT:
return t.val.referTo(name, seen)
case funcT:
for _, a := range t.arg {
@@ -883,6 +877,10 @@ func (t *itype) isRecursive() bool {
return false
}
func (t *itype) isIndirectRecursive() bool {
return t.isRecursive() || t.val != nil && t.val.isIndirectRecursive()
}
// isVariadic returns true if the function type is variadic.
// If the type is not a function or is not variadic, it will
// return false.
@@ -919,7 +917,7 @@ func isComplete(t *itype, visited map[string]bool) bool {
return true
}
fallthrough
case arrayT, chanT, chanRecvT, chanSendT, ptrT:
case arrayT, chanT, chanRecvT, chanSendT, ptrT, sliceT, variadicT:
return isComplete(t.val, visited)
case funcT:
complete := true
@@ -968,6 +966,10 @@ func (t *itype) assignableTo(o *itype) bool {
return true
}
if isInterface(o) && t.implements(o) {
return true
}
n := t.node
if n == nil || !n.rval.IsValid() {
return false
@@ -1053,6 +1055,10 @@ func (t *itype) methods() methodSet {
seen[typ] = true
switch typ.cat {
case aliasT:
for k, v := range getMethods(typ.val) {
res[k] = v
}
case interfaceT:
// Get methods from recursive analysis of interface fields.
for _, f := range typ.field {
@@ -1066,7 +1072,7 @@ func (t *itype) methods() methodSet {
}
case valueT, errorT:
// Get method from corresponding reflect.Type.
for i := typ.rtype.NumMethod() - 1; i >= 0; i-- {
for i := typ.TypeOf().NumMethod() - 1; i >= 0; i-- {
m := typ.rtype.Method(i)
res[m.Name] = m.Type.String()
}
@@ -1114,11 +1120,7 @@ func (t *itype) id() (res string) {
case nilT:
res = "nil"
case arrayT:
if t.size == 0 {
res = "[]" + t.val.id()
} else {
res = "[" + strconv.Itoa(t.size) + "]" + t.val.id()
}
res = "[" + strconv.Itoa(t.length) + "]" + t.val.id()
case chanT:
res = "chan " + t.val.id()
case chanSendT:
@@ -1151,6 +1153,8 @@ func (t *itype) id() (res string) {
res = "map[" + t.key.id() + "]" + t.val.id()
case ptrT:
res = "*" + t.val.id()
case sliceT:
res = "[]" + t.val.id()
case structT:
res = "struct{"
for _, t := range t.field {
@@ -1158,15 +1162,44 @@ func (t *itype) id() (res string) {
}
res += "}"
case valueT:
if isConstantValue(t.rtype) {
res = fixPossibleConstType(t.rtype).String()
break
}
res = ""
if t.rtype.PkgPath() != "" {
res += t.rtype.PkgPath() + "."
}
res += t.rtype.Name()
case variadicT:
res = "..." + t.val.id()
}
return res
}
// fixPossibleConstType returns the input type if it not a constant value,
// otherwise, it returns the default Go type corresponding to the
// constant.Value.
func fixPossibleConstType(t reflect.Type) (r reflect.Type) {
cv, ok := reflect.New(t).Elem().Interface().(constant.Value)
if !ok {
return t
}
switch cv.Kind() {
case constant.Bool:
r = reflect.TypeOf(true)
case constant.Int:
r = reflect.TypeOf(0)
case constant.String:
r = reflect.TypeOf("")
case constant.Float:
r = reflect.TypeOf(float64(0))
case constant.Complex:
r = reflect.TypeOf(complex128(0))
}
return r
}
// zero instantiates and return a zero value object for the given type during execution.
func (t *itype) zero() (v reflect.Value, err error) {
if t, err = t.finalize(); err != nil {
@@ -1176,7 +1209,7 @@ func (t *itype) zero() (v reflect.Value, err error) {
case aliasT:
v, err = t.val.zero()
case arrayT, ptrT, structT:
case arrayT, ptrT, structT, sliceT:
v = reflect.New(t.frameType()).Elem()
case valueT:
@@ -1423,12 +1456,10 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
switch t.cat {
case aliasT:
t.rtype = t.val.refType(defined, wrapRecursive)
case arrayT, variadicT:
if t.sizedef {
t.rtype = reflect.ArrayOf(t.size, t.val.refType(defined, wrapRecursive))
} else {
t.rtype = reflect.SliceOf(t.val.refType(defined, wrapRecursive))
}
case arrayT:
t.rtype = reflect.ArrayOf(t.length, t.val.refType(defined, wrapRecursive))
case sliceT, variadicT:
t.rtype = reflect.SliceOf(t.val.refType(defined, wrapRecursive))
case chanT:
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.refType(defined, wrapRecursive))
case chanRecvT:
@@ -1503,12 +1534,10 @@ func (t *itype) frameType() (r reflect.Type) {
switch t.cat {
case aliasT:
r = t.val.frameType()
case arrayT, variadicT:
if t.sizedef {
r = reflect.ArrayOf(t.size, t.val.frameType())
} else {
r = reflect.SliceOf(t.val.frameType())
}
case arrayT:
r = reflect.ArrayOf(t.length, t.val.frameType())
case sliceT, variadicT:
r = reflect.SliceOf(t.val.frameType())
case funcT:
r = reflect.TypeOf((*node)(nil))
case interfaceT:
@@ -1518,6 +1547,8 @@ func (t *itype) frameType() (r reflect.Type) {
break
}
r = reflect.TypeOf((*valueInterface)(nil)).Elem()
case mapT:
r = reflect.MapOf(t.key.frameType(), t.val.frameType())
case ptrT:
r = reflect.PtrTo(t.val.frameType())
default:
@@ -1631,6 +1662,13 @@ func defRecvType(n *node) *itype {
return nil
}
func wrappedType(n *node) *itype {
if n.typ.cat != valueT {
return nil
}
return n.typ.val
}
func isShiftNode(n *node) bool {
switch n.action {
case aShl, aShr, aShlAssign, aShrAssign:
@@ -1658,6 +1696,18 @@ func isFunc(t *itype) bool { return t.TypeOf().Kind() == reflect.Func }
func isMap(t *itype) bool { return t.TypeOf().Kind() == reflect.Map }
func isPtr(t *itype) bool { return t.TypeOf().Kind() == reflect.Ptr }
func isEmptyInterface(t *itype) bool {
return t.cat == interfaceT && len(t.field) == 0
}
func isFuncSrc(t *itype) bool {
return t.cat == funcT || (t.cat == aliasT && isFuncSrc(t.val))
}
func isPtrSrc(t *itype) bool {
return t.cat == ptrT || (t.cat == aliasT && isPtrSrc(t.val))
}
func isSendChan(t *itype) bool {
rt := t.TypeOf()
return rt.Kind() == reflect.Chan && rt.ChanDir() == reflect.SendDir
@@ -1673,7 +1723,7 @@ func isInterfaceSrc(t *itype) bool {
}
func isInterfaceBin(t *itype) bool {
return t.cat == valueT && t.rtype.Kind() == reflect.Interface
return t.cat == valueT && t.rtype.Kind() == reflect.Interface || t.cat == errorT
}
func isInterface(t *itype) bool {

View File

@@ -32,6 +32,9 @@ func (check typecheck) op(p opPredicates, a action, n, c *node, t reflect.Type)
//
// Use typ == nil to indicate assignment to an untyped blank identifier.
func (check typecheck) assignment(n *node, typ *itype, context string) error {
if n.typ == nil {
return n.cfgErrorf("invalid type in %s", context)
}
if n.typ.untyped {
if typ == nil || isInterface(typ) {
if typ == nil && n.typ.cat == nilT {
@@ -48,6 +51,10 @@ func (check typecheck) assignment(n *node, typ *itype, context string) error {
return nil
}
if typ.isIndirectRecursive() || n.typ.isIndirectRecursive() {
return nil
}
if !n.typ.assignableTo(typ) {
if context == "" {
return n.cfgErrorf("cannot use type %s as type %s", n.typ.id(), typ.id())
@@ -228,6 +235,15 @@ func (check typecheck) binaryExpr(n *node) error {
}
switch n.action {
case aAdd:
if n.typ == nil {
break
}
// Catch mixing string and number for "+" operator use.
k, k0, k1 := isNumber(n.typ.TypeOf()), isNumber(c0.typ.TypeOf()), isNumber(c1.typ.TypeOf())
if k != k0 || k != k1 {
return n.cfgErrorf("cannot use type %s as type %s in assignment", c0.typ.id(), n.typ.id())
}
case aRem:
if zeroConst(c1) {
return n.cfgErrorf("invalid operation: division by zero")
@@ -285,7 +301,10 @@ func (check typecheck) index(n *node, max int) error {
}
// arrayLitExpr type checks an array composite literal expression.
func (check typecheck) arrayLitExpr(child []*node, typ *itype, length int) error {
func (check typecheck) arrayLitExpr(child []*node, typ *itype) error {
cat := typ.cat
length := typ.length
typ = typ.val
visited := make(map[int]bool, len(child))
index := 0
for _, c := range child {
@@ -297,7 +316,7 @@ func (check typecheck) arrayLitExpr(child []*node, typ *itype, length int) error
}
n = c.child[1]
index = int(vInt(c.child[0].rval))
case length > 0 && index >= length:
case cat == arrayT && index >= length:
return c.cfgErrorf("index %d is out of bounds (>= %d)", index, length)
}
@@ -900,7 +919,7 @@ func arrayDeref(typ *itype) *itype {
return typ
}
if typ.cat == ptrT && typ.val.cat == arrayT && typ.val.sizedef {
if typ.cat == ptrT && typ.val.cat == arrayT {
return typ.val
}
return typ
@@ -956,7 +975,7 @@ func (check typecheck) argument(p param, ftyp *itype, i, l int, ellipsis bool) e
}
t := p.Type().TypeOf()
if t.Kind() != reflect.Slice || !(&itype{cat: valueT, rtype: t.Elem()}).assignableTo(atyp) {
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.typ.id(), (&itype{cat: arrayT, val: atyp}).id())
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.typ.id(), (&itype{cat: sliceT, val: atyp}).id())
}
return nil
}
@@ -978,6 +997,8 @@ func getArg(ftyp *itype, i int) *itype {
return arg
case i < l:
return ftyp.in(i)
case ftyp.cat == valueT && i < ftyp.rtype.NumIn():
return &itype{cat: valueT, rtype: ftyp.rtype.In(i)}
default:
return nil
}

View File

@@ -45,21 +45,35 @@ func genValueBinMethodOnInterface(n *node, defaultGen func(*frame) reflect.Value
n.child[0].child == nil || n.child[0].child[0] == nil {
return defaultGen
}
if n.child[0].child[1] == nil || n.child[0].child[1].ident == "" {
c0 := n.child[0]
if c0.child[1] == nil || c0.child[1].ident == "" {
return defaultGen
}
value0 := genValue(n.child[0].child[0])
value0 := genValue(c0.child[0])
return func(f *frame) reflect.Value {
val, ok := value0(f).Interface().(valueInterface)
if !ok {
v := value0(f)
var nod *node
for v.IsValid() {
// Traverse interface indirections to find out concrete type.
vi, ok := v.Interface().(valueInterface)
if !ok {
break
}
v = vi.value
nod = vi.node
}
if nod == nil {
return defaultGen(f)
}
typ := val.node.typ
typ := nod.typ
if typ.node != nil || typ.cat != valueT {
return defaultGen(f)
}
meth, _ := typ.rtype.MethodByName(n.child[0].child[1].ident)
meth, _ := typ.rtype.MethodByName(c0.child[1].ident)
return meth.Func
}
}
@@ -200,6 +214,31 @@ func genValue(n *node) func(*frame) reflect.Value {
}
}
func genDestValue(typ *itype, n *node) func(*frame) reflect.Value {
convertLiteralValue(n, typ.TypeOf())
switch {
case isInterfaceSrc(typ) && !isEmptyInterface(typ):
return genValueInterface(n)
case isFuncSrc(typ) && n.typ.cat == valueT:
return genValueNode(n)
case typ.cat == valueT && isFuncSrc(n.typ):
return genFunctionWrapper(n)
case isInterfaceBin(typ):
return genInterfaceWrapper(n, typ.rtype)
case n.kind == basicLit && n.val == nil:
return func(*frame) reflect.Value { return reflect.New(typ.rtype).Elem() }
case isRecursiveType(typ, typ.rtype):
return genValueRecursiveInterface(n, typ.rtype)
case isRecursiveType(n.typ, n.typ.rtype):
return genValueRecursiveInterfacePtrValue(n)
case n.typ.untyped && isComplex(typ.TypeOf()):
return genValueComplex(n)
case n.typ.untyped && !typ.untyped:
return genValueAs(n, typ.TypeOf())
}
return genValue(n)
}
func genValueArray(n *node) func(*frame) reflect.Value {
value := genValue(n)
// dereference array pointer, to support array operations on array pointer
@@ -296,7 +335,7 @@ func zeroInterfaceValue() reflect.Value {
}
func wantEmptyInterface(n *node) bool {
return n.typ.cat == interfaceT && len(n.typ.field) == 0 ||
return isEmptyInterface(n.typ) ||
n.anc.action == aAssign && n.anc.typ.cat == interfaceT && len(n.anc.typ.field) == 0 ||
n.anc.kind == returnStmt && n.anc.val.(*node).typ.ret[0].cat == interfaceT && len(n.anc.val.(*node).typ.ret[0].field) == 0
}

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["archive/tar"] = map[string]reflect.Value{
Symbols["archive/tar/tar"] = map[string]reflect.Value{
// function, constant and variable definitions
"ErrFieldTooLong": reflect.ValueOf(&tar.ErrFieldTooLong).Elem(),
"ErrHeader": reflect.ValueOf(&tar.ErrHeader).Elem(),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["archive/zip"] = map[string]reflect.Value{
Symbols["archive/zip/zip"] = map[string]reflect.Value{
// function, constant and variable definitions
"Deflate": reflect.ValueOf(zip.Deflate),
"ErrAlgorithm": reflect.ValueOf(&zip.ErrAlgorithm).Elem(),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["bufio"] = map[string]reflect.Value{
Symbols["bufio/bufio"] = map[string]reflect.Value{
// function, constant and variable definitions
"ErrAdvanceTooFar": reflect.ValueOf(&bufio.ErrAdvanceTooFar).Elem(),
"ErrBadReadCount": reflect.ValueOf(&bufio.ErrBadReadCount).Elem(),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["bytes"] = map[string]reflect.Value{
Symbols["bytes/bytes"] = map[string]reflect.Value{
// function, constant and variable definitions
"Compare": reflect.ValueOf(bytes.Compare),
"Contains": reflect.ValueOf(bytes.Contains),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["compress/bzip2"] = map[string]reflect.Value{
Symbols["compress/bzip2/bzip2"] = map[string]reflect.Value{
// function, constant and variable definitions
"NewReader": reflect.ValueOf(bzip2.NewReader),

View File

@@ -13,7 +13,7 @@ import (
)
func init() {
Symbols["compress/flate"] = map[string]reflect.Value{
Symbols["compress/flate/flate"] = map[string]reflect.Value{
// function, constant and variable definitions
"BestCompression": reflect.ValueOf(constant.MakeFromLiteral("9", token.INT, 0)),
"BestSpeed": reflect.ValueOf(constant.MakeFromLiteral("1", token.INT, 0)),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["compress/gzip"] = map[string]reflect.Value{
Symbols["compress/gzip/gzip"] = map[string]reflect.Value{
// function, constant and variable definitions
"BestCompression": reflect.ValueOf(constant.MakeFromLiteral("9", token.INT, 0)),
"BestSpeed": reflect.ValueOf(constant.MakeFromLiteral("1", token.INT, 0)),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["compress/lzw"] = map[string]reflect.Value{
Symbols["compress/lzw/lzw"] = map[string]reflect.Value{
// function, constant and variable definitions
"LSB": reflect.ValueOf(lzw.LSB),
"MSB": reflect.ValueOf(lzw.MSB),

View File

@@ -13,7 +13,7 @@ import (
)
func init() {
Symbols["compress/zlib"] = map[string]reflect.Value{
Symbols["compress/zlib/zlib"] = map[string]reflect.Value{
// function, constant and variable definitions
"BestCompression": reflect.ValueOf(constant.MakeFromLiteral("9", token.INT, 0)),
"BestSpeed": reflect.ValueOf(constant.MakeFromLiteral("1", token.INT, 0)),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["container/heap"] = map[string]reflect.Value{
Symbols["container/heap/heap"] = map[string]reflect.Value{
// function, constant and variable definitions
"Fix": reflect.ValueOf(heap.Fix),
"Init": reflect.ValueOf(heap.Init),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["container/list"] = map[string]reflect.Value{
Symbols["container/list/list"] = map[string]reflect.Value{
// function, constant and variable definitions
"New": reflect.ValueOf(list.New),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["container/ring"] = map[string]reflect.Value{
Symbols["container/ring/ring"] = map[string]reflect.Value{
// function, constant and variable definitions
"New": reflect.ValueOf(ring.New),

View File

@@ -11,7 +11,7 @@ import (
)
func init() {
Symbols["context"] = map[string]reflect.Value{
Symbols["context/context"] = map[string]reflect.Value{
// function, constant and variable definitions
"Background": reflect.ValueOf(context.Background),
"Canceled": reflect.ValueOf(&context.Canceled).Elem(),

View File

@@ -11,7 +11,7 @@ import (
)
func init() {
Symbols["crypto"] = map[string]reflect.Value{
Symbols["crypto/crypto"] = map[string]reflect.Value{
// function, constant and variable definitions
"BLAKE2b_256": reflect.ValueOf(crypto.BLAKE2b_256),
"BLAKE2b_384": reflect.ValueOf(crypto.BLAKE2b_384),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["crypto/aes"] = map[string]reflect.Value{
Symbols["crypto/aes/aes"] = map[string]reflect.Value{
// function, constant and variable definitions
"BlockSize": reflect.ValueOf(constant.MakeFromLiteral("16", token.INT, 0)),
"NewCipher": reflect.ValueOf(aes.NewCipher),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["crypto/cipher"] = map[string]reflect.Value{
Symbols["crypto/cipher/cipher"] = map[string]reflect.Value{
// function, constant and variable definitions
"NewCBCDecrypter": reflect.ValueOf(cipher.NewCBCDecrypter),
"NewCBCEncrypter": reflect.ValueOf(cipher.NewCBCEncrypter),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["crypto/des"] = map[string]reflect.Value{
Symbols["crypto/des/des"] = map[string]reflect.Value{
// function, constant and variable definitions
"BlockSize": reflect.ValueOf(constant.MakeFromLiteral("8", token.INT, 0)),
"NewCipher": reflect.ValueOf(des.NewCipher),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["crypto/dsa"] = map[string]reflect.Value{
Symbols["crypto/dsa/dsa"] = map[string]reflect.Value{
// function, constant and variable definitions
"ErrInvalidPublicKey": reflect.ValueOf(&dsa.ErrInvalidPublicKey).Elem(),
"GenerateKey": reflect.ValueOf(dsa.GenerateKey),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["crypto/ecdsa"] = map[string]reflect.Value{
Symbols["crypto/ecdsa/ecdsa"] = map[string]reflect.Value{
// function, constant and variable definitions
"GenerateKey": reflect.ValueOf(ecdsa.GenerateKey),
"Sign": reflect.ValueOf(ecdsa.Sign),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["crypto/ed25519"] = map[string]reflect.Value{
Symbols["crypto/ed25519/ed25519"] = map[string]reflect.Value{
// function, constant and variable definitions
"GenerateKey": reflect.ValueOf(ed25519.GenerateKey),
"NewKeyFromSeed": reflect.ValueOf(ed25519.NewKeyFromSeed),

View File

@@ -11,7 +11,7 @@ import (
)
func init() {
Symbols["crypto/elliptic"] = map[string]reflect.Value{
Symbols["crypto/elliptic/elliptic"] = map[string]reflect.Value{
// function, constant and variable definitions
"GenerateKey": reflect.ValueOf(elliptic.GenerateKey),
"Marshal": reflect.ValueOf(elliptic.Marshal),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["crypto/hmac"] = map[string]reflect.Value{
Symbols["crypto/hmac/hmac"] = map[string]reflect.Value{
// function, constant and variable definitions
"Equal": reflect.ValueOf(hmac.Equal),
"New": reflect.ValueOf(hmac.New),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["crypto/md5"] = map[string]reflect.Value{
Symbols["crypto/md5/md5"] = map[string]reflect.Value{
// function, constant and variable definitions
"BlockSize": reflect.ValueOf(constant.MakeFromLiteral("64", token.INT, 0)),
"New": reflect.ValueOf(md5.New),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["crypto/rand"] = map[string]reflect.Value{
Symbols["crypto/rand/rand"] = map[string]reflect.Value{
// function, constant and variable definitions
"Int": reflect.ValueOf(rand.Int),
"Prime": reflect.ValueOf(rand.Prime),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["crypto/rc4"] = map[string]reflect.Value{
Symbols["crypto/rc4/rc4"] = map[string]reflect.Value{
// function, constant and variable definitions
"NewCipher": reflect.ValueOf(rc4.NewCipher),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["crypto/rsa"] = map[string]reflect.Value{
Symbols["crypto/rsa/rsa"] = map[string]reflect.Value{
// function, constant and variable definitions
"DecryptOAEP": reflect.ValueOf(rsa.DecryptOAEP),
"DecryptPKCS1v15": reflect.ValueOf(rsa.DecryptPKCS1v15),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["crypto/sha1"] = map[string]reflect.Value{
Symbols["crypto/sha1/sha1"] = map[string]reflect.Value{
// function, constant and variable definitions
"BlockSize": reflect.ValueOf(constant.MakeFromLiteral("64", token.INT, 0)),
"New": reflect.ValueOf(sha1.New),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["crypto/sha256"] = map[string]reflect.Value{
Symbols["crypto/sha256/sha256"] = map[string]reflect.Value{
// function, constant and variable definitions
"BlockSize": reflect.ValueOf(constant.MakeFromLiteral("64", token.INT, 0)),
"New": reflect.ValueOf(sha256.New),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["crypto/sha512"] = map[string]reflect.Value{
Symbols["crypto/sha512/sha512"] = map[string]reflect.Value{
// function, constant and variable definitions
"BlockSize": reflect.ValueOf(constant.MakeFromLiteral("128", token.INT, 0)),
"New": reflect.ValueOf(sha512.New),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["crypto/subtle"] = map[string]reflect.Value{
Symbols["crypto/subtle/subtle"] = map[string]reflect.Value{
// function, constant and variable definitions
"ConstantTimeByteEq": reflect.ValueOf(subtle.ConstantTimeByteEq),
"ConstantTimeCompare": reflect.ValueOf(subtle.ConstantTimeCompare),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["crypto/tls"] = map[string]reflect.Value{
Symbols["crypto/tls/tls"] = map[string]reflect.Value{
// function, constant and variable definitions
"CipherSuiteName": reflect.ValueOf(tls.CipherSuiteName),
"CipherSuites": reflect.ValueOf(tls.CipherSuites),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["crypto/x509"] = map[string]reflect.Value{
Symbols["crypto/x509/x509"] = map[string]reflect.Value{
// function, constant and variable definitions
"CANotAuthorizedForExtKeyUsage": reflect.ValueOf(x509.CANotAuthorizedForExtKeyUsage),
"CANotAuthorizedForThisName": reflect.ValueOf(x509.CANotAuthorizedForThisName),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["crypto/x509/pkix"] = map[string]reflect.Value{
Symbols["crypto/x509/pkix/pkix"] = map[string]reflect.Value{
// type definitions
"AlgorithmIdentifier": reflect.ValueOf((*pkix.AlgorithmIdentifier)(nil)),
"AttributeTypeAndValue": reflect.ValueOf((*pkix.AttributeTypeAndValue)(nil)),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["database/sql"] = map[string]reflect.Value{
Symbols["database/sql/sql"] = map[string]reflect.Value{
// function, constant and variable definitions
"Drivers": reflect.ValueOf(sql.Drivers),
"ErrConnDone": reflect.ValueOf(&sql.ErrConnDone).Elem(),

View File

@@ -11,7 +11,7 @@ import (
)
func init() {
Symbols["database/sql/driver"] = map[string]reflect.Value{
Symbols["database/sql/driver/driver"] = map[string]reflect.Value{
// function, constant and variable definitions
"Bool": reflect.ValueOf(&driver.Bool).Elem(),
"DefaultParameterConverter": reflect.ValueOf(&driver.DefaultParameterConverter).Elem(),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["debug/dwarf"] = map[string]reflect.Value{
Symbols["debug/dwarf/dwarf"] = map[string]reflect.Value{
// function, constant and variable definitions
"AttrAbstractOrigin": reflect.ValueOf(dwarf.AttrAbstractOrigin),
"AttrAccessibility": reflect.ValueOf(dwarf.AttrAccessibility),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["debug/elf"] = map[string]reflect.Value{
Symbols["debug/elf/elf"] = map[string]reflect.Value{
// function, constant and variable definitions
"ARM_MAGIC_TRAMP_NUMBER": reflect.ValueOf(constant.MakeFromLiteral("1543503875", token.INT, 0)),
"COMPRESS_HIOS": reflect.ValueOf(elf.COMPRESS_HIOS),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["debug/gosym"] = map[string]reflect.Value{
Symbols["debug/gosym/gosym"] = map[string]reflect.Value{
// function, constant and variable definitions
"NewLineTable": reflect.ValueOf(gosym.NewLineTable),
"NewTable": reflect.ValueOf(gosym.NewTable),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["debug/macho"] = map[string]reflect.Value{
Symbols["debug/macho/macho"] = map[string]reflect.Value{
// function, constant and variable definitions
"ARM64_RELOC_ADDEND": reflect.ValueOf(macho.ARM64_RELOC_ADDEND),
"ARM64_RELOC_BRANCH26": reflect.ValueOf(macho.ARM64_RELOC_BRANCH26),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["debug/pe"] = map[string]reflect.Value{
Symbols["debug/pe/pe"] = map[string]reflect.Value{
// function, constant and variable definitions
"COFFSymbolSize": reflect.ValueOf(constant.MakeFromLiteral("18", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_ARCHITECTURE": reflect.ValueOf(constant.MakeFromLiteral("7", token.INT, 0)),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["debug/plan9obj"] = map[string]reflect.Value{
Symbols["debug/plan9obj/plan9obj"] = map[string]reflect.Value{
// function, constant and variable definitions
"Magic386": reflect.ValueOf(constant.MakeFromLiteral("491", token.INT, 0)),
"Magic64": reflect.ValueOf(constant.MakeFromLiteral("32768", token.INT, 0)),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["encoding"] = map[string]reflect.Value{
Symbols["encoding/encoding"] = map[string]reflect.Value{
// type definitions
"BinaryMarshaler": reflect.ValueOf((*encoding.BinaryMarshaler)(nil)),
"BinaryUnmarshaler": reflect.ValueOf((*encoding.BinaryUnmarshaler)(nil)),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["encoding/ascii85"] = map[string]reflect.Value{
Symbols["encoding/ascii85/ascii85"] = map[string]reflect.Value{
// function, constant and variable definitions
"Decode": reflect.ValueOf(ascii85.Decode),
"Encode": reflect.ValueOf(ascii85.Encode),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["encoding/asn1"] = map[string]reflect.Value{
Symbols["encoding/asn1/asn1"] = map[string]reflect.Value{
// function, constant and variable definitions
"ClassApplication": reflect.ValueOf(constant.MakeFromLiteral("1", token.INT, 0)),
"ClassContextSpecific": reflect.ValueOf(constant.MakeFromLiteral("2", token.INT, 0)),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["encoding/base32"] = map[string]reflect.Value{
Symbols["encoding/base32/base32"] = map[string]reflect.Value{
// function, constant and variable definitions
"HexEncoding": reflect.ValueOf(&base32.HexEncoding).Elem(),
"NewDecoder": reflect.ValueOf(base32.NewDecoder),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["encoding/base64"] = map[string]reflect.Value{
Symbols["encoding/base64/base64"] = map[string]reflect.Value{
// function, constant and variable definitions
"NewDecoder": reflect.ValueOf(base64.NewDecoder),
"NewEncoder": reflect.ValueOf(base64.NewEncoder),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["encoding/binary"] = map[string]reflect.Value{
Symbols["encoding/binary/binary"] = map[string]reflect.Value{
// function, constant and variable definitions
"BigEndian": reflect.ValueOf(&binary.BigEndian).Elem(),
"LittleEndian": reflect.ValueOf(&binary.LittleEndian).Elem(),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["encoding/csv"] = map[string]reflect.Value{
Symbols["encoding/csv/csv"] = map[string]reflect.Value{
// function, constant and variable definitions
"ErrBareQuote": reflect.ValueOf(&csv.ErrBareQuote).Elem(),
"ErrFieldCount": reflect.ValueOf(&csv.ErrFieldCount).Elem(),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["encoding/gob"] = map[string]reflect.Value{
Symbols["encoding/gob/gob"] = map[string]reflect.Value{
// function, constant and variable definitions
"NewDecoder": reflect.ValueOf(gob.NewDecoder),
"NewEncoder": reflect.ValueOf(gob.NewEncoder),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["encoding/hex"] = map[string]reflect.Value{
Symbols["encoding/hex/hex"] = map[string]reflect.Value{
// function, constant and variable definitions
"Decode": reflect.ValueOf(hex.Decode),
"DecodeString": reflect.ValueOf(hex.DecodeString),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["encoding/json"] = map[string]reflect.Value{
Symbols["encoding/json/json"] = map[string]reflect.Value{
// function, constant and variable definitions
"Compact": reflect.ValueOf(json.Compact),
"HTMLEscape": reflect.ValueOf(json.HTMLEscape),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["encoding/pem"] = map[string]reflect.Value{
Symbols["encoding/pem/pem"] = map[string]reflect.Value{
// function, constant and variable definitions
"Decode": reflect.ValueOf(pem.Decode),
"Encode": reflect.ValueOf(pem.Encode),

View File

@@ -12,7 +12,7 @@ import (
)
func init() {
Symbols["encoding/xml"] = map[string]reflect.Value{
Symbols["encoding/xml/xml"] = map[string]reflect.Value{
// function, constant and variable definitions
"CopyToken": reflect.ValueOf(xml.CopyToken),
"Escape": reflect.ValueOf(xml.Escape),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["errors"] = map[string]reflect.Value{
Symbols["errors/errors"] = map[string]reflect.Value{
// function, constant and variable definitions
"As": reflect.ValueOf(errors.As),
"Is": reflect.ValueOf(errors.Is),

View File

@@ -10,7 +10,7 @@ import (
)
func init() {
Symbols["expvar"] = map[string]reflect.Value{
Symbols["expvar/expvar"] = map[string]reflect.Value{
// function, constant and variable definitions
"Do": reflect.ValueOf(expvar.Do),
"Get": reflect.ValueOf(expvar.Get),

Some files were not shown because too many files have changed in this diff Show More