Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f46cd5efb | ||
|
|
35e645c690 | ||
|
|
03596dac45 | ||
|
|
8db7a815e3 | ||
|
|
424e7ac90d | ||
|
|
effd64c980 | ||
|
|
8a88a1ab8a | ||
|
|
030dd3cbc2 | ||
|
|
bee21968c7 | ||
|
|
9abaeeb729 | ||
|
|
c8ae73ae8c | ||
|
|
5e49f83519 | ||
|
|
ec1ee5f5b6 | ||
|
|
e03016b6d7 | ||
|
|
400b625153 | ||
|
|
f7810d9761 | ||
|
|
7d4e5fb224 | ||
|
|
b08a51cb16 | ||
|
|
9e664ee8dd | ||
|
|
cda23836f0 | ||
|
|
058c121273 | ||
|
|
3645904a15 | ||
|
|
82dd3f2953 | ||
|
|
2f0279f0f5 | ||
|
|
bfa9a267be | ||
|
|
978bbe0301 | ||
|
|
63a537c8d9 | ||
|
|
b0937618b0 | ||
|
|
d23a7e1d8b | ||
|
|
71fd938040 | ||
|
|
869b6d2850 | ||
|
|
a4e15d7788 | ||
|
|
c88297459d | ||
|
|
8aa22b9ebb | ||
|
|
75ad29c6b4 | ||
|
|
a89a8b9ce4 | ||
|
|
458e8e911a | ||
|
|
ee81ee7fea | ||
|
|
aaddc39981 | ||
|
|
5bf6618a53 | ||
|
|
aad4c5a99b |
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Propose a change to Yaegi!
|
||||
---
|
||||
|
||||
<!-- ⚠️ If you do not respect this template your issue will be closed. -->
|
||||
<!-- ⚠️ Make sure to browse the opened and closed issues before submit your issue. -->
|
||||
|
||||
#### Proposal
|
||||
|
||||
<!-- Write your feature request in the form of a proposal to be considered for implementation -->
|
||||
|
||||
#### Background
|
||||
|
||||
<!-- Describe the background problem or need that led to this feature request -->
|
||||
|
||||
#### Workarounds
|
||||
|
||||
<!-- Are there any current workarounds that you're using that others in similar positions should know about? -->
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@
|
||||
cmd/goexports/goexports
|
||||
example/inception/inception
|
||||
_test/tmp/
|
||||
/dist
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"gocyclo",
|
||||
"gochecknoinits",
|
||||
"gochecknoglobals",
|
||||
"typecheck", # v1.17.1 and Go1.13 => bug
|
||||
]
|
||||
|
||||
[issues]
|
||||
@@ -44,7 +45,6 @@
|
||||
path = "cmd/goexports/goexports.go"
|
||||
text = "SA1019: importer.For is deprecated: use ForCompiler, which populates a FileSet with the positions of objects created by the importer."
|
||||
|
||||
# structcheck false-positive
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/interp.go"
|
||||
text = "`(astDot|cfgDot|noRun)` is unused"
|
||||
path = "interp/.+_test\\.go"
|
||||
linters = ["goconst"]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
project_name: yaegi
|
||||
|
||||
builds:
|
||||
- binary: yaegi
|
||||
- id: yaegi
|
||||
binary: yaegi
|
||||
main: ./cmd/yaegi/yaegi.go
|
||||
|
||||
goos:
|
||||
@@ -25,6 +26,31 @@ builds:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
|
||||
- id: goexports
|
||||
binary: goexports
|
||||
main: ./cmd/goexports/goexports.go
|
||||
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
# - windows
|
||||
- freebsd
|
||||
- openbsd
|
||||
- solaris
|
||||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
- arm
|
||||
- arm64
|
||||
goarm:
|
||||
- 7
|
||||
- 6
|
||||
- 5
|
||||
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
@@ -35,11 +61,12 @@ changelog:
|
||||
- '^test:'
|
||||
- '^tests:'
|
||||
|
||||
archive:
|
||||
name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
files:
|
||||
- LICENSE
|
||||
archives:
|
||||
- id: archive
|
||||
name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
files:
|
||||
- LICENSE
|
||||
|
||||
28
.travis.yml
28
.travis.yml
@@ -2,23 +2,37 @@ language: go
|
||||
|
||||
dist: xenial
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: change
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/pkg/mod
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- go: 1.11.x
|
||||
- go: 1.12.x
|
||||
- go: 1.13.x
|
||||
env: STABLE=true
|
||||
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
|
||||
go_import_path: github.com/containous/yaegi
|
||||
|
||||
before_install:
|
||||
# Install linters and misspell
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.16.0
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin ${GOLANGCI_LINT_VERSION}
|
||||
- golangci-lint --version
|
||||
|
||||
install:
|
||||
@@ -44,4 +58,4 @@ deploy:
|
||||
script: curl -sL https://git.io/goreleaser | bash
|
||||
on:
|
||||
tags: true
|
||||
condition: $TRAVIS_GO_VERSION =~ ^1\.12\.x$
|
||||
condition: $STABLE = true
|
||||
|
||||
19
CONTRIBUTING.md
Normal file
19
CONTRIBUTING.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Contributing
|
||||
|
||||
Yaegi is an open source project, and your feedback and contributions
|
||||
are needed and always welcome.
|
||||
|
||||
[Issues] and [Pull Requests] are opened at https://github.com/containous/yaegi.
|
||||
|
||||
Non trivial changes should be discussed with the project maintainers by
|
||||
opening a [Feature Request] clearly explaining rationale, background
|
||||
and possible implementation ideas. Feel free to provide code in such
|
||||
discussions.
|
||||
|
||||
Once the proposal is approved, a Pull Request can be opened. If you want
|
||||
to provide early visibility to reviewers, create a [Draft Pull Request].
|
||||
|
||||
[Issues]: https://github.com/containous/yaegi/issues
|
||||
[Pull Requests]: https://github.com/containous/yaegi/issues
|
||||
[Feature Request]: https://github.com/containous/yaegi/issues/new?template=feature_request.md
|
||||
[Draft Pull Request]: https://github.blog/2019-02-14-introducing-draft-pull-requests/
|
||||
@@ -5,6 +5,7 @@
|
||||
[](https://github.com/containous/yaegi/releases)
|
||||
[](https://travis-ci.com/containous/yaegi)
|
||||
[](https://godoc.org/github.com/containous/yaegi)
|
||||
[](https://community.containo.us/c/yaegi)
|
||||
|
||||
Yaegi is Another Elegant Go Interpreter.
|
||||
It powers executable Go scripts and plugins, in embedded interpreters or interactive shells, on top of the Go runtime.
|
||||
@@ -17,7 +18,7 @@ It powers executable Go scripts and plugins, in embedded interpreters or interac
|
||||
* Works everywhere Go works
|
||||
* All Go & runtime resources accessible from script (with control)
|
||||
* Security: `unsafe` and `syscall` packages neither used nor exported by default
|
||||
* Support Go 1.11 and Go 1.12 (the latest 2 major releases)
|
||||
* Support Go 1.12 and Go 1.13 (the latest 2 major releases)
|
||||
|
||||
## Install
|
||||
|
||||
@@ -144,9 +145,7 @@ Beside the known [bugs] which are supposed to be fixed in the short term, there
|
||||
|
||||
## Contributing
|
||||
|
||||
Yaegi is an open source project, and your feedback and contributions are needed and always welcome.
|
||||
|
||||
[Issues] and [pull requests] are opened at https://github.com/containous/yaegi
|
||||
[Contributing guide](CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
@@ -156,6 +155,4 @@ Yaegi is an open source project, and your feedback and contributions are needed
|
||||
[docs]: https://godoc.org/github.com/containous/yaegi
|
||||
[license]: https://github.com/containous/yaegi/blob/master/LICENSE
|
||||
[github]: https://github.com/containous/yaegi
|
||||
[Issues]: https://github.com/containous/yaegi/issues
|
||||
[pull requests]: https://github.com/containous/yaegi/issues
|
||||
[bugs]: https://github.com/containous/yaegi/issues?q=is%3Aissue+is%3Aopen+label%3Abug
|
||||
|
||||
10
_test/a30.go
Normal file
10
_test/a30.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
for range []struct{}{} {
|
||||
}
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
11
_test/a31.go
Normal file
11
_test/a31.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
for range []int{0, 1, 2} {
|
||||
print("hello ")
|
||||
}
|
||||
println("")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello hello hello
|
||||
14
_test/a32.go
Normal file
14
_test/a32.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type T struct{}
|
||||
|
||||
var a = []T{{}}
|
||||
|
||||
func main() {
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [{}]
|
||||
16
_test/alias1.go
Normal file
16
_test/alias1.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type MyT T
|
||||
|
||||
type T struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(MyT{})
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {}
|
||||
15
_test/assign9.go
Normal file
15
_test/assign9.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
type foo func(b int)
|
||||
|
||||
func boo(b int) { println("boo", b) }
|
||||
|
||||
func main() {
|
||||
var f foo
|
||||
|
||||
f = boo
|
||||
f(4)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// boo 4
|
||||
4
_test/bad0.go
Normal file
4
_test/bad0.go
Normal file
@@ -0,0 +1,4 @@
|
||||
println("Hello")
|
||||
|
||||
// Error:
|
||||
// _test/bad0.go:1:1: expected 'package', found println
|
||||
5
_test/c1/c1.go
Normal file
5
_test/c1/c1.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package c1
|
||||
|
||||
import "github.com/containous/yaegi/_test/c2"
|
||||
|
||||
var C1 = c2.C2 + "x"
|
||||
5
_test/c2/c2.go
Normal file
5
_test/c2/c2.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package c2
|
||||
|
||||
import "github.com/containous/yaegi/_test/c1"
|
||||
|
||||
var C2 = c1.C1 + "Y"
|
||||
14
_test/composite0.go
Normal file
14
_test/composite0.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var a = &[]*T{}
|
||||
|
||||
type T struct{ name string }
|
||||
|
||||
func main() {
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &[]
|
||||
14
_test/composite1.go
Normal file
14
_test/composite1.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var a = &[]*T{{}}
|
||||
|
||||
type T struct{ name string }
|
||||
|
||||
func main() {
|
||||
fmt.Println((*a)[0])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{}
|
||||
14
_test/composite2.go
Normal file
14
_test/composite2.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var a = &[]*T{{"hello"}}
|
||||
|
||||
type T struct{ name string }
|
||||
|
||||
func main() {
|
||||
fmt.Println((*a)[0])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{hello}
|
||||
14
_test/copy1.go
Normal file
14
_test/copy1.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
a := []int{10, 20, 30}
|
||||
b := &[4]int{}
|
||||
c := b[:]
|
||||
copy(c, a)
|
||||
fmt.Println(c)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [10 20 30 0]
|
||||
@@ -1,6 +1,8 @@
|
||||
package foo
|
||||
|
||||
import "./boo"
|
||||
import "github.com/containous/yaegi/_test/foo/boo"
|
||||
|
||||
var Bar = "BARR"
|
||||
var Boo = boo.Boo
|
||||
|
||||
func init() { println("init foo") }
|
||||
|
||||
5
_test/foo/bir.go
Normal file
5
_test/foo/bir.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package foo
|
||||
|
||||
import "github.com/containous/yaegi/_test/foo/boo"
|
||||
|
||||
var Bir = boo.Boo + "22"
|
||||
@@ -1,3 +1,5 @@
|
||||
package boo
|
||||
|
||||
var Boo = "Boo"
|
||||
|
||||
func init() { println("init boo") }
|
||||
|
||||
19
_test/for6.go
Normal file
19
_test/for6.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := "三"
|
||||
for i := 0; i < len(s); i++ {
|
||||
fmt.Printf("byte %d: %d\n", i, s[i])
|
||||
}
|
||||
for i, r := range s {
|
||||
fmt.Printf("rune %d: %d\n", i, r)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// byte 0: 228
|
||||
// byte 1: 184
|
||||
// byte 2: 137
|
||||
// rune 0: 19977
|
||||
21
_test/fun6.go
Normal file
21
_test/fun6.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func NewPool() Pool { return Pool{} }
|
||||
|
||||
type Pool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
var _pool = NewPool()
|
||||
|
||||
func main() {
|
||||
fmt.Println(_pool)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {<nil>}
|
||||
18
_test/fun7.go
Normal file
18
_test/fun7.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
goflag "flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Foo(goflag *goflag.Flag) {
|
||||
fmt.Println(goflag)
|
||||
}
|
||||
|
||||
func main() {
|
||||
g := &goflag.Flag{}
|
||||
Foo(g)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{ <nil> }
|
||||
8
_test/fun8.go
Normal file
8
_test/fun8.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package main
|
||||
|
||||
func main() { println(f == nil) }
|
||||
|
||||
var f func()
|
||||
|
||||
// Output:
|
||||
// true
|
||||
12
_test/fun9.go
Normal file
12
_test/fun9.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
type T uint
|
||||
|
||||
func main() {
|
||||
type myint int
|
||||
var i = myint(1)
|
||||
println(i)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
@@ -1,8 +1,10 @@
|
||||
package main
|
||||
|
||||
import "./foo"
|
||||
import "github.com/containous/yaegi/_test/foo"
|
||||
|
||||
func main() { println(foo.Bar, foo.Boo) }
|
||||
|
||||
// Output:
|
||||
// init boo
|
||||
// init foo
|
||||
// BARR Boo
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package main
|
||||
|
||||
import "./p1"
|
||||
import "github.com/containous/yaegi/_test/p1"
|
||||
|
||||
func main() { println("num:", p1.Uint32()) }
|
||||
|
||||
|
||||
10
_test/import5.go
Normal file
10
_test/import5.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import boo "github.com/containous/yaegi/_test/foo"
|
||||
|
||||
func main() { println(boo.Bar, boo.Boo, boo.Bir) }
|
||||
|
||||
// Output:
|
||||
// init boo
|
||||
// init foo
|
||||
// BARR Boo Boo22
|
||||
11
_test/import6.go
Normal file
11
_test/import6.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "github.com/containous/yaegi/_test/c1"
|
||||
|
||||
func main() {
|
||||
println(c1.C1)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// import cycle not allowed
|
||||
// imports github.com/containous/yaegi/_test/c1
|
||||
12
_test/interface10.go
Normal file
12
_test/interface10.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
type Edge interface {
|
||||
ReverseEdge() Edge
|
||||
}
|
||||
|
||||
func main() {
|
||||
println("hello")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
27
_test/interface11.go
Normal file
27
_test/interface11.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Error interface {
|
||||
error
|
||||
Code() string
|
||||
}
|
||||
|
||||
type MyError Error
|
||||
|
||||
type T struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (t *T) Error() string { return "err: " + t.Name }
|
||||
func (t *T) Code() string { return "code: " + t.Name }
|
||||
|
||||
func newT(s string) MyError { return &T{s} }
|
||||
|
||||
func main() {
|
||||
t := newT("foo")
|
||||
fmt.Println(t.Code())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// code: foo
|
||||
14
_test/map18.go
Normal file
14
_test/map18.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var foo = make([]int, 1)
|
||||
|
||||
func main() {
|
||||
for _, v := range foo {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
@@ -13,3 +13,6 @@ func (*Hello) Hi() string {
|
||||
func main() {
|
||||
fmt.Println(&Hello{})
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{}
|
||||
|
||||
19
_test/method22.go
Normal file
19
_test/method22.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
func Bar() {
|
||||
s := Obj.Foo()
|
||||
println(s)
|
||||
}
|
||||
|
||||
var Obj = &T{}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (t *T) Foo() bool { return t != nil }
|
||||
|
||||
func main() {
|
||||
Bar()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
21
_test/method23.go
Normal file
21
_test/method23.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
func Bar() {
|
||||
s := Obj.Foo()
|
||||
println(s)
|
||||
}
|
||||
|
||||
var Obj = NewT()
|
||||
|
||||
func NewT() *T { return &T{} }
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (t *T) Foo() bool { return t != nil }
|
||||
|
||||
func main() {
|
||||
Bar()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
33
_test/method24.go
Normal file
33
_test/method24.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Pool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
func (p Pool) Get() *Buffer { return &Buffer{} }
|
||||
|
||||
func NewPool() Pool { return Pool{} }
|
||||
|
||||
type Buffer struct {
|
||||
bs []byte
|
||||
pool Pool
|
||||
}
|
||||
|
||||
var (
|
||||
_pool = NewPool()
|
||||
Get = _pool.Get
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(_pool)
|
||||
fmt.Println(Get())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {<nil>}
|
||||
// &{[] {<nil>}}
|
||||
33
_test/method25.go
Normal file
33
_test/method25.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func (p Pool) Get() *Buffer { return &Buffer{} }
|
||||
|
||||
func NewPool() Pool { return Pool{} }
|
||||
|
||||
type Buffer struct {
|
||||
bs []byte
|
||||
pool Pool
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
var (
|
||||
_pool = NewPool()
|
||||
Get = _pool.Get
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(_pool)
|
||||
fmt.Println(Get())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {<nil>}
|
||||
// &{[] {<nil>}}
|
||||
18
_test/method26.go
Normal file
18
_test/method26.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
func NewT(name string) *T { return &T{name} }
|
||||
|
||||
var C = NewT("test")
|
||||
|
||||
func (t *T) f() { println(t == C) }
|
||||
|
||||
type T struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
C.f()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
20
_test/method27.go
Normal file
20
_test/method27.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type AuthenticatedRequest struct {
|
||||
http.Request
|
||||
Username string
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := &AuthenticatedRequest{}
|
||||
fmt.Println("ua:", a.UserAgent())
|
||||
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ua:
|
||||
26
_test/ptr7.go
Normal file
26
_test/ptr7.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ipNetValue net.IPNet
|
||||
|
||||
func (ipnet *ipNetValue) Set(value string) error {
|
||||
_, n, err := net.ParseCIDR(strings.TrimSpace(value))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*ipnet = ipNetValue(*n)
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
v := ipNetValue{}
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {<nil> <nil>}
|
||||
13
_test/ptr_array0.go
Normal file
13
_test/ptr_array0.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
type T [2]int
|
||||
|
||||
func F0(t *T) int { return t[0] }
|
||||
|
||||
func main() {
|
||||
t := &T{1, 2}
|
||||
println(F0(t))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
19
_test/ptr_array1.go
Normal file
19
_test/ptr_array1.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
type T [3]int
|
||||
|
||||
func F0(t *T) {
|
||||
for i, v := range t {
|
||||
println(i, v)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{1, 2, 3}
|
||||
F0(t)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0 1
|
||||
// 1 2
|
||||
// 2 3
|
||||
16
_test/ptr_array2.go
Normal file
16
_test/ptr_array2.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type T [2]int
|
||||
|
||||
func F1(t *T) { t[0] = 1 }
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
F1(t)
|
||||
fmt.Println(t)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &[1 0]
|
||||
11
_test/ptr_array3.go
Normal file
11
_test/ptr_array3.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
a := &[...]int{1, 2, 3}
|
||||
fmt.Println(a[:])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [1 2 3]
|
||||
39
_test/recurse0.go
Normal file
39
_test/recurse0.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type T struct {
|
||||
a []T
|
||||
b []*T
|
||||
c map[string]T
|
||||
d map[string]*T
|
||||
e chan T
|
||||
f chan *T
|
||||
h *T
|
||||
i func(T) T
|
||||
j func(*T) *T
|
||||
U
|
||||
}
|
||||
|
||||
type U struct {
|
||||
k []T
|
||||
l []*T
|
||||
m map[string]T
|
||||
n map[string]*T
|
||||
o chan T
|
||||
p chan *T
|
||||
q *T
|
||||
r func(T) T
|
||||
s func(*T) *T
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := T{}
|
||||
u := U{}
|
||||
fmt.Println(t)
|
||||
fmt.Println(u)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {[] [] map[] map[] <nil> <nil> <nil> <nil> <nil> {[] [] map[] map[] <nil> <nil> <nil> <nil> <nil>}}
|
||||
// {[] [] map[] map[] <nil> <nil> <nil> <nil> <nil>}
|
||||
@@ -9,6 +9,9 @@ func forever() {
|
||||
|
||||
func main() {
|
||||
go forever()
|
||||
time.Sleep(1e9)
|
||||
time.Sleep(1e4)
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
|
||||
12
_test/shift1.go
Normal file
12
_test/shift1.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const a1 = 0x7f8 >> 3
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%T %v\n", a1, a1)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// int 255
|
||||
10
_test/shift2.go
Normal file
10
_test/shift2.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var u uint64
|
||||
var v uint32
|
||||
println(u << v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
10
_test/shift3.go
Normal file
10
_test/shift3.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
const a = 1.0
|
||||
|
||||
const b = a + 3
|
||||
|
||||
func main() { println(b << (1)) }
|
||||
|
||||
// Output:
|
||||
// 8
|
||||
@@ -13,3 +13,6 @@ type S2 struct {
|
||||
func main() {
|
||||
fmt.Println(S2{})
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {<nil>}
|
||||
|
||||
@@ -11,8 +11,8 @@ type Fromage struct {
|
||||
|
||||
func main() {
|
||||
a := Fromage{}
|
||||
fmt.Println(a.Server)
|
||||
fmt.Println(a.Server.WriteTimeout)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// { <nil> <nil> 0s 0s 0s 0s 0 map[] <nil> <nil> 0 0 {{0 0} 0} <nil> {0 0} map[] map[] <nil> []}
|
||||
// 0s
|
||||
|
||||
@@ -21,3 +21,6 @@ func (w GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool {
|
||||
func main() {
|
||||
fmt.Println("hello")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
|
||||
@@ -21,3 +21,6 @@ func main() {
|
||||
c := CreateConfig()
|
||||
fmt.Println(c)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{[] false }
|
||||
|
||||
12
_test/struct24.go
Normal file
12
_test/struct24.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
var a = &T{}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func main() {
|
||||
println(a != nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
14
_test/struct25.go
Normal file
14
_test/struct25.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var a = T{}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func main() {
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {}
|
||||
20
_test/struct26.go
Normal file
20
_test/struct26.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func newT2() *T2 { return &T2{} }
|
||||
|
||||
type T2 struct {
|
||||
T1
|
||||
}
|
||||
|
||||
type T1 struct {
|
||||
bs []byte
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(newT2())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{{[]}}
|
||||
18
_test/struct27.go
Normal file
18
_test/struct27.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (f *Foo) Boo() { fmt.Println(f.name, "Boo") }
|
||||
|
||||
type Foo struct {
|
||||
name string
|
||||
fun func(f *Foo)
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &Foo{name: "foo"}
|
||||
t.Boo()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// foo Boo
|
||||
19
_test/struct28.go
Normal file
19
_test/struct28.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type T1 struct {
|
||||
T2
|
||||
}
|
||||
|
||||
type T2 struct {
|
||||
*T1
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := T1{}
|
||||
fmt.Println(t)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {{<nil>}}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
t := time.Unix(1e9, 0)
|
||||
t := time.Unix(1e9, 0).In(time.UTC)
|
||||
fmt.Println(t.Minute())
|
||||
}
|
||||
|
||||
|
||||
17
_test/time8.go
Normal file
17
_test/time8.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type durationValue time.Duration
|
||||
|
||||
func (d *durationValue) String() string { return (*time.Duration)(d).String() }
|
||||
|
||||
func main() {
|
||||
var d durationValue
|
||||
println(d.String())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0s
|
||||
@@ -25,3 +25,6 @@ func main() {
|
||||
t := &T1{}
|
||||
println(t.Get())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// no name
|
||||
|
||||
12
_test/type13.go
Normal file
12
_test/type13.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
var a = &T{}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func main() {
|
||||
println(a != nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
14
_test/type14.go
Normal file
14
_test/type14.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var a = T{}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func main() {
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {}
|
||||
18
_test/var7.go
Normal file
18
_test/var7.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
var m = make(map[string]*T)
|
||||
|
||||
func main() {
|
||||
fmt.Println(m)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[]
|
||||
19
_test/var8.go
Normal file
19
_test/var8.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
|
||||
|
||||
func main() {
|
||||
fmt.Println(protoMessageType.Kind())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// struct
|
||||
10
_test/variadic4.go
Normal file
10
_test/variadic4.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func variadic(s ...string) {}
|
||||
|
||||
func f(s string) { println(s + "bar") }
|
||||
|
||||
func main() { f("foo") }
|
||||
|
||||
// Output:
|
||||
// foobar
|
||||
22
_test/variadic5.go
Normal file
22
_test/variadic5.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type A struct {
|
||||
}
|
||||
|
||||
func (a A) f(vals ...bool) {
|
||||
for _, v := range vals {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := A{}
|
||||
a.f(true)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
@@ -19,7 +19,9 @@ The GOOS and GOARCH environment variables set the desired target.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/format"
|
||||
@@ -35,12 +37,14 @@ import (
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const model = `// +build {{.BuildTags}}
|
||||
const model = `// Code generated by 'goexports {{.PkgName}}'. DO NOT EDIT.
|
||||
|
||||
{{.License}}
|
||||
|
||||
{{if .BuildTags}}// +build {{.BuildTags}}{{end}}
|
||||
|
||||
package {{.Dest}}
|
||||
|
||||
// Code generated by 'goexports {{.PkgName}}'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
{{- range $key, $value := .Imports }}
|
||||
{{- if $value}}
|
||||
@@ -53,6 +57,7 @@ import (
|
||||
|
||||
func init() {
|
||||
Symbols["{{.PkgName}}"] = map[string]reflect.Value{
|
||||
{{- if .Val}}
|
||||
// function, constant and variable definitions
|
||||
{{range $key, $value := .Val -}}
|
||||
{{- if $value.Addr -}}
|
||||
@@ -62,15 +67,20 @@ func init() {
|
||||
{{end -}}
|
||||
{{end}}
|
||||
|
||||
{{- end}}
|
||||
{{- if .Typ}}
|
||||
// type definitions
|
||||
{{range $key, $value := .Typ -}}
|
||||
"{{$key}}": reflect.ValueOf((*{{$value}})(nil)),
|
||||
{{end}}
|
||||
|
||||
{{- end}}
|
||||
{{- if .Wrap}}
|
||||
// interface wrapper definitions
|
||||
{{range $key, $value := .Wrap -}}
|
||||
"_{{$key}}": reflect.ValueOf((*{{$value.Name}})(nil)),
|
||||
{{end}}
|
||||
{{- end}}
|
||||
}
|
||||
}
|
||||
{{range $key, $value := .Wrap -}}
|
||||
@@ -103,7 +113,7 @@ type Wrap struct {
|
||||
Method []Method
|
||||
}
|
||||
|
||||
func genContent(dest, pkgName string) ([]byte, error) {
|
||||
func genContent(dest, pkgName, license string) ([]byte, error) {
|
||||
p, err := importer.For("source", nil).Import(pkgName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -184,14 +194,23 @@ func genContent(dest, pkgName string) ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
parts := strings.Split(runtime.Version(), ".")
|
||||
currentGoVersion := parts[0] + "." + parts[1]
|
||||
var buildTags string
|
||||
if runtime.Version() != "devel" {
|
||||
parts := strings.Split(runtime.Version(), ".")
|
||||
|
||||
minor, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse version: %v", err)
|
||||
minorRaw := getMinor(parts[1])
|
||||
|
||||
currentGoVersion := parts[0] + "." + minorRaw
|
||||
|
||||
minor, errParse := strconv.Atoi(minorRaw)
|
||||
if errParse != nil {
|
||||
return nil, fmt.Errorf("failed to parse version: %v", errParse)
|
||||
}
|
||||
|
||||
nextGoVersion := parts[0] + "." + strconv.Itoa(minor+1)
|
||||
|
||||
buildTags = currentGoVersion + ",!" + nextGoVersion
|
||||
}
|
||||
nextGoVersion := parts[0] + "." + strconv.Itoa(minor+1)
|
||||
|
||||
base := template.New("goexports")
|
||||
parse, err := base.Parse(model)
|
||||
@@ -199,12 +218,11 @@ func genContent(dest, pkgName string) ([]byte, error) {
|
||||
return nil, fmt.Errorf("template parsing error: %v", err)
|
||||
}
|
||||
|
||||
buildTags := currentGoVersion + ",!" + nextGoVersion
|
||||
if pkgName == "log/syslog" {
|
||||
buildTags += ",!windows,!nacl,!plan9"
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
b := new(bytes.Buffer)
|
||||
data := map[string]interface{}{
|
||||
"Dest": dest,
|
||||
"Imports": imports,
|
||||
@@ -213,6 +231,7 @@ func genContent(dest, pkgName string) ([]byte, error) {
|
||||
"Typ": typ,
|
||||
"Wrap": wrap,
|
||||
"BuildTags": buildTags,
|
||||
"License": license,
|
||||
}
|
||||
err = parse.Execute(b, data)
|
||||
if err != nil {
|
||||
@@ -250,17 +269,61 @@ func fixConst(name string, val constant.Value) string {
|
||||
return name
|
||||
}
|
||||
|
||||
// genLicense generates the correct LICENSE header text from the provided
|
||||
// path to a LICENSE file.
|
||||
func genLicense(fname string) (string, error) {
|
||||
if fname == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not open LICENSE file: %v", err)
|
||||
}
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
license := new(strings.Builder)
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
txt := sc.Text()
|
||||
if txt != "" {
|
||||
txt = " " + txt
|
||||
}
|
||||
license.WriteString("//" + txt + "\n")
|
||||
}
|
||||
if sc.Err() != nil {
|
||||
return "", fmt.Errorf("could not scan LICENSE file: %v", err)
|
||||
}
|
||||
|
||||
return license.String(), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
licenseFlag := flag.String("license", "", "path to a LICENSE file")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() == 0 {
|
||||
flag.Usage()
|
||||
log.Fatalf("missing package path")
|
||||
}
|
||||
|
||||
license, err := genLicense(*licenseFlag)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
dest := path.Base(dir)
|
||||
|
||||
for _, pkg := range os.Args[1:] {
|
||||
content, err := genContent(dest, pkg)
|
||||
for _, pkg := range flag.Args() {
|
||||
content, err := genContent(dest, pkg, license)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
var oFile string
|
||||
@@ -271,11 +334,29 @@ func main() {
|
||||
oFile = strings.Replace(pkg, "/", "_", -1) + ".go"
|
||||
}
|
||||
|
||||
parts := strings.Split(runtime.Version(), ".")
|
||||
prefix := runtime.Version()
|
||||
if runtime.Version() != "devel" {
|
||||
parts := strings.Split(runtime.Version(), ".")
|
||||
|
||||
err = ioutil.WriteFile(parts[0]+"_"+parts[1]+"_"+oFile, content, 0666)
|
||||
prefix = parts[0] + "_" + getMinor(parts[1])
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(prefix+"_"+oFile, content, 0666)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getMinor(part string) string {
|
||||
minor := part
|
||||
index := strings.Index(minor, "beta")
|
||||
if index < 0 {
|
||||
index = strings.Index(minor, "rc")
|
||||
}
|
||||
if index > 0 {
|
||||
minor = minor[:index]
|
||||
}
|
||||
|
||||
return minor
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ func TestPackagesError(t *testing.T) {
|
||||
{
|
||||
desc: "different packages in the same directory",
|
||||
goPath: "./_pkg9/",
|
||||
expected: "found packages pkg and pkgfalse in _pkg9/src/github.com/foo/pkg",
|
||||
expected: "1:21: import \"github.com/foo/pkg\" error: found packages pkg and pkgfalse in _pkg9/src/github.com/foo/pkg",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -318,23 +318,26 @@ func (interp *Interpreter) firstToken(src string) token.Token {
|
||||
// ast parses src string containing Go code and generates the corresponding AST.
|
||||
// The package name and the AST root node are returned.
|
||||
func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
||||
inRepl := name == ""
|
||||
var inFunc bool
|
||||
|
||||
// Allow incremental parsing of declarations or statements, by inserting
|
||||
// them in a pseudo file package or function. Those statements or
|
||||
// declarations will be always evaluated in the global scope
|
||||
switch interp.firstToken(src) {
|
||||
case token.PACKAGE:
|
||||
// nothing to do
|
||||
case token.CONST, token.FUNC, token.IMPORT, token.TYPE, token.VAR:
|
||||
src = "package main;" + src
|
||||
default:
|
||||
inFunc = true
|
||||
src = "package main; func main() {" + src + "}"
|
||||
if inRepl {
|
||||
switch interp.firstToken(src) {
|
||||
case token.PACKAGE:
|
||||
// nothing to do
|
||||
case token.CONST, token.FUNC, token.IMPORT, token.TYPE, token.VAR:
|
||||
src = "package main;" + src
|
||||
default:
|
||||
inFunc = true
|
||||
src = "package main; func main() {" + src + "}"
|
||||
}
|
||||
}
|
||||
|
||||
if !interp.buildOk(name, src) {
|
||||
return "", nil, nil // skip source not matching build constraints
|
||||
if ok, err := interp.buildOk(interp.context, name, src); !ok || err != nil {
|
||||
return "", nil, err // skip source not matching build constraints
|
||||
}
|
||||
|
||||
f, err := parser.ParseFile(interp.fset, name, src, 0)
|
||||
@@ -696,7 +699,13 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
||||
case *ast.RangeStmt:
|
||||
// Insert a missing ForRangeStmt for AST correctness
|
||||
n := addChild(&root, anc, pos, forRangeStmt, aNop)
|
||||
st.push(addChild(&root, astNode{n, nod}, pos, rangeStmt, aRange), nod)
|
||||
r := addChild(&root, astNode{n, nod}, pos, rangeStmt, aRange)
|
||||
st.push(r, nod)
|
||||
if a.Key == nil {
|
||||
// range not in an assign expression: insert a "_" key variable to store iteration index
|
||||
k := addChild(&root, astNode{r, nod}, pos, identExpr, aNop)
|
||||
k.ident = "_"
|
||||
}
|
||||
|
||||
case *ast.ReturnStmt:
|
||||
st.push(addChild(&root, anc, pos, returnStmt, aReturn), nod)
|
||||
|
||||
@@ -1,42 +1,43 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// buildOk returns true if a file or script matches build constraints
|
||||
// as specified in https://golang.org/pkg/go/build/#hdr-Build_Constraints
|
||||
func (interp *Interpreter) buildOk(name, src string) bool {
|
||||
// as specified in https://golang.org/pkg/go/build/#hdr-Build_Constraints.
|
||||
// An error from parser is returned as well.
|
||||
func (interp *Interpreter) buildOk(ctx build.Context, name, src string) (bool, error) {
|
||||
// Extract comments before the first clause
|
||||
f, err := parser.ParseFile(interp.fset, name, src, parser.PackageClauseOnly|parser.ParseComments)
|
||||
if err != nil {
|
||||
return false
|
||||
return false, err
|
||||
}
|
||||
for _, g := range f.Comments {
|
||||
// in file, evaluate the AND of multiple line build constraints
|
||||
for _, line := range strings.Split(strings.TrimSpace(g.Text()), "\n") {
|
||||
if !buildLineOk(line) {
|
||||
return false
|
||||
if !buildLineOk(ctx, line) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// buildLineOk returns true if line is not a build constraint or
|
||||
// if build constraint is satisfied
|
||||
func buildLineOk(line string) (ok bool) {
|
||||
func buildLineOk(ctx build.Context, line string) (ok bool) {
|
||||
if len(line) < 7 || line[:7] != "+build " {
|
||||
return true
|
||||
}
|
||||
// In line, evaluate the OR of space-separated options
|
||||
options := strings.Split(strings.TrimSpace(line[6:]), " ")
|
||||
for _, o := range options {
|
||||
if ok = buildOptionOk(o); ok {
|
||||
if ok = buildOptionOk(ctx, o); ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -44,39 +45,35 @@ func buildLineOk(line string) (ok bool) {
|
||||
}
|
||||
|
||||
// buildOptionOk return true if all comma separated tags match, false otherwise
|
||||
func buildOptionOk(tag string) bool {
|
||||
func buildOptionOk(ctx build.Context, tag string) bool {
|
||||
// in option, evaluate the AND of individual tags
|
||||
for _, t := range strings.Split(tag, ",") {
|
||||
if !buildTagOk(t) {
|
||||
if !buildTagOk(ctx, t) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var (
|
||||
goos = runtime.GOOS
|
||||
goarch = runtime.GOARCH
|
||||
goversion = goNumVersion()
|
||||
)
|
||||
|
||||
// buildTagOk returns true if a build tag matches, false otherwise
|
||||
// if first character is !, result is negated
|
||||
func buildTagOk(s string) (r bool) {
|
||||
func buildTagOk(ctx build.Context, s string) (r bool) {
|
||||
not := s[0] == '!'
|
||||
if not {
|
||||
s = s[1:]
|
||||
}
|
||||
switch {
|
||||
case s == goos:
|
||||
case contains(ctx.BuildTags, s):
|
||||
r = true
|
||||
case s == goarch:
|
||||
case s == ctx.GOOS:
|
||||
r = true
|
||||
case s == ctx.GOARCH:
|
||||
r = true
|
||||
case len(s) > 4 && s[:4] == "go1.":
|
||||
if n, err := strconv.Atoi(s[4:]); err != nil {
|
||||
r = false
|
||||
} else {
|
||||
r = goversion >= n
|
||||
r = goMinorVersion(ctx) >= n
|
||||
}
|
||||
}
|
||||
if not {
|
||||
@@ -85,15 +82,33 @@ func buildTagOk(s string) (r bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// goNumVersion returns the go minor version number
|
||||
func goNumVersion() int {
|
||||
v := strings.Split(runtime.Version(), ".")
|
||||
n, _ := strconv.Atoi(v[1])
|
||||
return n
|
||||
func contains(tags []string, tag string) bool {
|
||||
for _, t := range tags {
|
||||
if t == tag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// goMinorVersion returns the go minor version number
|
||||
func goMinorVersion(ctx build.Context) int {
|
||||
current := ctx.ReleaseTags[len(ctx.ReleaseTags)-1]
|
||||
|
||||
v := strings.Split(current, ".")
|
||||
if len(v) < 2 {
|
||||
panic("unsupported Go version: " + current)
|
||||
}
|
||||
|
||||
m, err := strconv.Atoi(v[1])
|
||||
if err != nil {
|
||||
panic("unsupported Go version: " + current)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// skipFile returns true if file should be skipped
|
||||
func skipFile(p string) bool {
|
||||
func skipFile(ctx build.Context, p string) bool {
|
||||
if !strings.HasSuffix(p, ".go") {
|
||||
return true
|
||||
}
|
||||
@@ -107,10 +122,10 @@ func skipFile(p string) bool {
|
||||
}
|
||||
a := strings.Split(p[i+1:], "_")
|
||||
last := len(a) - 1
|
||||
if last1 := last - 1; last1 >= 0 && a[last1] == goos && a[last] == goarch {
|
||||
if last1 := last - 1; last1 >= 0 && a[last1] == ctx.GOOS && a[last] == ctx.GOARCH {
|
||||
return false
|
||||
}
|
||||
if s := a[last]; s != goos && s != goarch && knownOs[s] || knownArch[s] {
|
||||
if s := a[last]; s != ctx.GOOS && s != ctx.GOARCH && knownOs[s] || knownArch[s] {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -11,18 +12,21 @@ type testBuild struct {
|
||||
|
||||
func TestBuildTag(t *testing.T) {
|
||||
// Assume a specific OS, arch and go version no matter the real underlying system
|
||||
oo, oa, ov := goos, goarch, goversion
|
||||
goos, goarch, goversion = "linux", "amd64", 11
|
||||
defer func() { goos, goarch, goversion = oo, oa, ov }()
|
||||
ctx := build.Context{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "linux",
|
||||
BuildTags: []string{"foo"},
|
||||
ReleaseTags: []string{"go1.11"},
|
||||
}
|
||||
|
||||
tests := []testBuild{
|
||||
{"// +build linux", true},
|
||||
{"// +build windows", false},
|
||||
{"// +build go1.9", true},
|
||||
{"// +build go1.11", true},
|
||||
{"// +build !go1.12", true},
|
||||
{"// +build go1.12", false},
|
||||
{"// +build !go1.10", false},
|
||||
{"// +build go1.9", true},
|
||||
{"// +build !go1.12", true},
|
||||
{"// +build ignore", false},
|
||||
{"// +build linux,amd64", true},
|
||||
{"// +build linux,i386", false},
|
||||
@@ -30,14 +34,17 @@ func TestBuildTag(t *testing.T) {
|
||||
{"// +build linux\n// +build amd64", true},
|
||||
{"// +build linux\n\n\n// +build amd64", true},
|
||||
{"// +build linux\n// +build i386", false},
|
||||
{"// +build foo", true},
|
||||
{"// +build !foo", false},
|
||||
{"// +build bar", false},
|
||||
}
|
||||
|
||||
i := New(Options{})
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
src := test.src + "\npackage x"
|
||||
t.Run("", func(t *testing.T) {
|
||||
if r := i.buildOk("", src); r != test.res {
|
||||
t.Run(test.src, func(t *testing.T) {
|
||||
if r, _ := i.buildOk(ctx, "", src); r != test.res {
|
||||
t.Errorf("got %v, want %v", r, test.res)
|
||||
}
|
||||
})
|
||||
@@ -46,9 +53,10 @@ func TestBuildTag(t *testing.T) {
|
||||
|
||||
func TestBuildFile(t *testing.T) {
|
||||
// Assume a specific OS, arch and go pattern no matter the real underlying system
|
||||
oo, oa := goos, goarch
|
||||
goos, goarch = "linux", "amd64"
|
||||
defer func() { goos, goarch = oo, oa }()
|
||||
ctx := build.Context{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "linux",
|
||||
}
|
||||
|
||||
tests := []testBuild{
|
||||
{"foo/bar_linux_amd64.go", false},
|
||||
@@ -66,9 +74,43 @@ func TestBuildFile(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.src, func(t *testing.T) {
|
||||
if r := skipFile(test.src); r != test.res {
|
||||
if r := skipFile(ctx, test.src); r != test.res {
|
||||
t.Errorf("got %v, want %v", r, test.res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_goMinorVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
context build.Context
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
desc: "stable",
|
||||
context: build.Context{ReleaseTags: []string{
|
||||
"go1.1", "go1.2", "go1.3", "go1.4", "go1.5", "go1.6", "go1.7", "go1.8", "go1.9", "go1.10", "go1.11", "go1.12",
|
||||
}},
|
||||
expected: 12,
|
||||
},
|
||||
{
|
||||
desc: "devel/beta/rc",
|
||||
context: build.Context{ReleaseTags: []string{
|
||||
"go1.1", "go1.2", "go1.3", "go1.4", "go1.5", "go1.6", "go1.7", "go1.8", "go1.9", "go1.10", "go1.11", "go1.12", "go1.13",
|
||||
}},
|
||||
expected: 13,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
minor := goMinorVersion(test.context)
|
||||
|
||||
if minor != test.expected {
|
||||
t.Errorf("got %v, want %v", minor, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
283
interp/cfg.go
283
interp/cfg.go
@@ -3,6 +3,7 @@ package interp
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"path"
|
||||
"reflect"
|
||||
"unicode"
|
||||
@@ -77,7 +78,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
vtyp = &itype{cat: valueT, rtype: typ.Elem()}
|
||||
case reflect.String:
|
||||
ktyp = sc.getType("int")
|
||||
vtyp = sc.getType("byte")
|
||||
vtyp = sc.getType("rune")
|
||||
case reflect.Array, reflect.Slice:
|
||||
ktyp = sc.getType("int")
|
||||
vtyp = &itype{cat: valueT, rtype: typ.Elem()}
|
||||
@@ -86,10 +87,18 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
n.anc.gen = rangeMap
|
||||
ktyp = o.typ.key
|
||||
vtyp = o.typ.val
|
||||
case ptrT:
|
||||
ktyp = sc.getType("int")
|
||||
vtyp = o.typ.val
|
||||
if vtyp.cat == valueT {
|
||||
vtyp = &itype{cat: valueT, rtype: vtyp.rtype.Elem()}
|
||||
} else {
|
||||
vtyp = vtyp.val
|
||||
}
|
||||
case stringT:
|
||||
ktyp = sc.getType("int")
|
||||
vtyp = sc.getType("byte")
|
||||
case arrayT:
|
||||
vtyp = sc.getType("rune")
|
||||
case arrayT, variadicT:
|
||||
ktyp = sc.getType("int")
|
||||
vtyp = o.typ.val
|
||||
}
|
||||
@@ -183,7 +192,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
}
|
||||
|
||||
case compositeLitExpr:
|
||||
if n.child[0].isType(sc) {
|
||||
if len(n.child) > 0 && n.child[0].isType(sc) {
|
||||
// Get type from 1st child
|
||||
if n.typ, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return false
|
||||
@@ -195,7 +204,10 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
} else if n.anc.typ != nil {
|
||||
n.typ = n.anc.typ.val
|
||||
}
|
||||
// FIXME n.typ can be nil.
|
||||
if n.typ == nil {
|
||||
err = n.cfgErrorf("undefined type")
|
||||
return false
|
||||
}
|
||||
n.typ.untyped = true
|
||||
}
|
||||
// Propagate type to children, to handle implicit types
|
||||
@@ -221,6 +233,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
|
||||
case funcDecl:
|
||||
n.val = n
|
||||
// Compute function type before entering local scope to avoid
|
||||
// possible collisions with function argument names.
|
||||
n.child[2].typ, err = nodeType(interp, sc, n.child[2])
|
||||
// Add a frame indirection level as we enter in a func
|
||||
sc = sc.pushFunc()
|
||||
sc.def = n
|
||||
@@ -257,9 +272,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if typ, err = nodeType(interp, sc, c.lastChild()); err != nil {
|
||||
return false
|
||||
}
|
||||
if typ.variadic {
|
||||
typ = &itype{cat: arrayT, val: typ}
|
||||
}
|
||||
for _, cc := range c.child[:len(c.child)-1] {
|
||||
sc.sym[cc.ident] = &symbol{index: sc.add(typ), kind: varSym, typ: typ}
|
||||
}
|
||||
@@ -290,14 +302,32 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
name = path.Base(ipath)
|
||||
}
|
||||
if interp.binPkg[ipath] != nil && name != "." {
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT}, path: ipath}
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath}}
|
||||
} else {
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT}, path: ipath}
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: ipath}}
|
||||
}
|
||||
return false
|
||||
|
||||
case typeSpec:
|
||||
// processing already done in GTA pass
|
||||
// processing already done in GTA pass for global types, only parses inlined types
|
||||
if sc.def != nil {
|
||||
typeName := n.child[0].ident
|
||||
var typ *itype
|
||||
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||
return false
|
||||
}
|
||||
if typ.incomplete {
|
||||
err = n.cfgErrorf("invalid type declaration")
|
||||
return false
|
||||
}
|
||||
if n.child[1].kind == identExpr {
|
||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName}
|
||||
} else {
|
||||
n.typ = typ
|
||||
n.typ.name = typeName
|
||||
}
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, typ: n.typ}
|
||||
}
|
||||
return false
|
||||
|
||||
case arrayType, basicLit, chanType, funcType, mapType, structType:
|
||||
@@ -310,6 +340,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// Display the exact location in input source which triggered the panic
|
||||
panic(n.cfgErrorf("CFG post-order panic: %v", r))
|
||||
}
|
||||
}()
|
||||
|
||||
switch n.kind {
|
||||
case addressExpr:
|
||||
wireChild(n)
|
||||
@@ -344,13 +382,12 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
var sym *symbol
|
||||
var level int
|
||||
if n.kind == defineStmt || (n.kind == assignStmt && dest.ident == "_") {
|
||||
if src.typ != nil && src.typ.cat == nilT {
|
||||
err = src.cfgErrorf("use of untyped nil")
|
||||
break
|
||||
}
|
||||
if atyp != nil {
|
||||
dest.typ = atyp
|
||||
} else {
|
||||
if src.typ, err = nodeType(interp, sc, src); err != nil {
|
||||
return
|
||||
}
|
||||
dest.typ = src.typ
|
||||
}
|
||||
if dest.typ.sizedef {
|
||||
@@ -385,7 +422,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
case aShlAssign, aShrAssign:
|
||||
if !(isInt(t0) && isUint(t1)) {
|
||||
if !(dest.isInteger() && src.isNatural()) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
default:
|
||||
@@ -483,7 +520,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
nilSym := interp.universe.sym["nil"]
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
if !c0.typ.untyped && !c1.typ.untyped && c0.typ.id() != c1.typ.id() {
|
||||
// Shift operator type is inherited from first parameter only
|
||||
// All other binary operators require both parameter types to be the same
|
||||
if !isShiftNode(n) && !c0.typ.untyped && !c1.typ.untyped && c0.typ.id() != c1.typ.id() {
|
||||
err = n.cfgErrorf("mismatched types %s and %s", c0.typ.id(), c1.typ.id())
|
||||
break
|
||||
}
|
||||
@@ -501,7 +540,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
case aShl, aShr:
|
||||
if !(isInt(t0) && isUint(t1)) {
|
||||
if !(c0.isInteger() && c1.isNatural()) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
n.typ = c0.typ
|
||||
@@ -562,20 +601,34 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
wireChild(n)
|
||||
t := n.child[0].typ
|
||||
switch t.cat {
|
||||
case valueT:
|
||||
n.typ = &itype{cat: valueT, rtype: t.rtype.Elem()}
|
||||
case ptrT:
|
||||
n.typ = t.val
|
||||
if t.val.cat == valueT {
|
||||
n.typ = &itype{cat: valueT, rtype: t.val.rtype.Elem()}
|
||||
} else {
|
||||
n.typ = t.val.val
|
||||
}
|
||||
case stringT:
|
||||
n.typ = sc.getType("byte")
|
||||
case valueT:
|
||||
n.typ = &itype{cat: valueT, rtype: t.rtype.Elem()}
|
||||
default:
|
||||
n.typ = t.val
|
||||
}
|
||||
n.findex = sc.add(n.typ)
|
||||
n.recv = &receiver{node: n}
|
||||
switch k := t.TypeOf().Kind(); k {
|
||||
typ := t.TypeOf()
|
||||
switch k := typ.Kind(); k {
|
||||
case reflect.Map:
|
||||
n.gen = getIndexMap
|
||||
case reflect.Array, reflect.Slice, reflect.String:
|
||||
n.gen = getIndexArray
|
||||
case reflect.Ptr:
|
||||
if typ2 := typ.Elem(); typ2.Kind() == reflect.Array {
|
||||
n.gen = getIndexArray
|
||||
} else {
|
||||
err = n.cfgErrorf("type %v does not support indexing", typ)
|
||||
}
|
||||
default:
|
||||
err = n.cfgErrorf("type is not an array, slice, string or map: %v", t.id())
|
||||
}
|
||||
@@ -633,73 +686,17 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
case callExpr:
|
||||
wireChild(n)
|
||||
switch {
|
||||
case isBuiltinCall(n):
|
||||
case interp.isBuiltinCall(n):
|
||||
n.gen = n.child[0].sym.builtin
|
||||
n.child[0].typ = &itype{cat: builtinT}
|
||||
switch n.child[0].ident {
|
||||
case "append":
|
||||
c1, c2 := n.child[1], n.child[2]
|
||||
if n.typ = sc.getType(c1.ident); n.typ == nil {
|
||||
if n.typ, err = nodeType(interp, sc, c1); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(n.child) == 3 {
|
||||
if c2.typ.cat == arrayT && c2.typ.val.id() == n.typ.val.id() ||
|
||||
isByteArray(c1.typ.TypeOf()) && isString(c2.typ.TypeOf()) {
|
||||
n.gen = appendSlice
|
||||
}
|
||||
}
|
||||
case "cap", "copy", "len":
|
||||
n.typ = sc.getType("int")
|
||||
case "complex":
|
||||
c0, c1 := n.child[1], n.child[2]
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isFloat32(t0) && isFloat32(t1):
|
||||
n.typ = sc.getType("complex64")
|
||||
case isFloat64(t0) && isFloat64(t1):
|
||||
n.typ = sc.getType("complex128")
|
||||
case c0.typ.untyped && isNumber(t0) && c1.typ.untyped && isNumber(t1):
|
||||
n.typ = &itype{cat: valueT, rtype: complexType}
|
||||
case c0.typ.untyped && isFloat32(t1) || c1.typ.untyped && isFloat32(t0):
|
||||
n.typ = sc.getType("complex64")
|
||||
case c0.typ.untyped && isFloat64(t1) || c1.typ.untyped && isFloat64(t0):
|
||||
n.typ = sc.getType("complex128")
|
||||
default:
|
||||
err = n.cfgErrorf("invalid types %s and %s", t0.Kind(), t1.Kind())
|
||||
}
|
||||
case "real", "imag":
|
||||
switch k := n.child[1].typ.TypeOf().Kind(); {
|
||||
case k == reflect.Complex64:
|
||||
n.typ = sc.getType("float32")
|
||||
case k == reflect.Complex128:
|
||||
n.typ = sc.getType("float64")
|
||||
case n.child[1].typ.untyped && isNumber(n.child[1].typ.TypeOf()):
|
||||
n.typ = &itype{cat: valueT, rtype: floatType}
|
||||
default:
|
||||
err = n.cfgErrorf("invalid complex type %s", k)
|
||||
}
|
||||
case "make":
|
||||
if n.typ = sc.getType(n.child[1].ident); n.typ == nil {
|
||||
if n.typ, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
n.child[1].val = n.typ
|
||||
n.child[1].kind = basicLit
|
||||
case "new":
|
||||
if n.typ, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||
return
|
||||
}
|
||||
n.typ = &itype{cat: ptrT, val: n.typ}
|
||||
case "recover":
|
||||
n.typ = sc.getType("interface{}")
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
return
|
||||
}
|
||||
if n.typ != nil {
|
||||
n.findex = sc.add(n.typ)
|
||||
} else {
|
||||
if n.typ.cat == builtinT {
|
||||
n.findex = -1
|
||||
n.val = nil
|
||||
} else {
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
case n.child[0].isType(sc):
|
||||
// Type conversion expression
|
||||
@@ -1066,11 +1063,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
}
|
||||
} else if n.typ.cat == ptrT && (n.typ.val.cat == valueT || n.typ.val.cat == errorT) {
|
||||
// Handle pointer on object defined in runtime
|
||||
if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok {
|
||||
n.typ = &itype{cat: valueT, rtype: field.Type}
|
||||
n.val = field.Index
|
||||
n.gen = getPtrIndexSeq
|
||||
} else if method, ok := n.typ.val.rtype.MethodByName(n.child[1].ident); ok {
|
||||
if method, ok := n.typ.val.rtype.MethodByName(n.child[1].ident); ok {
|
||||
n.val = method.Index
|
||||
n.typ = &itype{cat: valueT, rtype: method.Type}
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
@@ -1080,13 +1073,18 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
n.gen = getIndexBinMethod
|
||||
n.typ = &itype{cat: valueT, rtype: method.Type}
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
} else if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok {
|
||||
n.typ = &itype{cat: valueT, rtype: field.Type}
|
||||
n.val = field.Index
|
||||
n.gen = getPtrIndexSeq
|
||||
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident)
|
||||
}
|
||||
} else if n.typ.cat == binPkgT {
|
||||
// Resolve binary package symbol: a type or a value
|
||||
name := n.child[1].ident
|
||||
pkg := n.child[0].sym.path
|
||||
pkg := n.child[0].sym.typ.path
|
||||
if s, ok := interp.binPkg[pkg][name]; ok {
|
||||
if isBinType(s) {
|
||||
n.kind = rtypeExpr
|
||||
@@ -1101,16 +1099,16 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
err = n.cfgErrorf("package %s \"%s\" has no symbol %s", n.child[0].ident, pkg, name)
|
||||
}
|
||||
} else if n.typ.cat == srcPkgT {
|
||||
pkg, name := n.child[0].ident, n.child[1].ident
|
||||
pkg, name := n.child[0].sym.typ.path, n.child[1].ident
|
||||
// Resolve source package symbol
|
||||
if sym, ok := interp.scopes[pkg].sym[name]; ok {
|
||||
if sym, ok := interp.srcPkg[pkg][name]; ok {
|
||||
n.findex = sym.index
|
||||
n.val = sym.node
|
||||
n.gen = nop
|
||||
n.typ = sym.typ
|
||||
n.sym = sym
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident)
|
||||
err = n.cfgErrorf("undefined selector: %s.%s", pkg, name)
|
||||
}
|
||||
} else if m, lind := n.typ.lookupMethod(n.child[1].ident); m != nil {
|
||||
if n.child[0].isType(sc) {
|
||||
@@ -1128,19 +1126,23 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
n.typ = m.typ
|
||||
n.recv = &receiver{node: n.child[0], index: lind}
|
||||
}
|
||||
} else if m, lind, ok := n.typ.lookupBinMethod(n.child[1].ident); ok {
|
||||
n.gen = getIndexSeqMethod
|
||||
} else if m, lind, isPtr, ok := n.typ.lookupBinMethod(n.child[1].ident); ok {
|
||||
if isPtr {
|
||||
n.gen = getIndexSeqPtrMethod
|
||||
} else {
|
||||
n.gen = getIndexSeqMethod
|
||||
}
|
||||
n.val = append([]int{m.Index}, lind...)
|
||||
n.typ = &itype{cat: valueT, rtype: m.Type}
|
||||
} else if ti := n.typ.lookupField(n.child[1].ident); len(ti) > 0 {
|
||||
// Handle struct field
|
||||
n.val = ti
|
||||
switch n.typ.cat {
|
||||
case interfaceT:
|
||||
switch {
|
||||
case isInterfaceSrc(n.typ):
|
||||
n.typ = n.typ.fieldSeq(ti)
|
||||
n.gen = getMethodByName
|
||||
n.action = aMethod
|
||||
case ptrT:
|
||||
case n.typ.cat == ptrT:
|
||||
n.typ = n.typ.fieldSeq(ti)
|
||||
n.gen = getPtrIndexSeq
|
||||
if n.typ.cat == funcT {
|
||||
@@ -1194,7 +1196,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
default:
|
||||
// dereference expression
|
||||
wireChild(n)
|
||||
n.typ = n.child[0].typ.val
|
||||
if c0 := n.child[0]; c0.typ.cat == valueT {
|
||||
n.typ = &itype{cat: valueT, rtype: c0.typ.rtype.Elem()}
|
||||
} else {
|
||||
n.typ = c0.typ.val
|
||||
}
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
|
||||
@@ -1297,7 +1303,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
|
||||
case sliceExpr:
|
||||
wireChild(n)
|
||||
if ctyp := n.child[0].typ; ctyp.size != 0 {
|
||||
ctyp := n.child[0].typ
|
||||
if ctyp.cat == ptrT {
|
||||
ctyp = ctyp.val
|
||||
}
|
||||
if ctyp.size != 0 {
|
||||
// Create a slice type from an array type
|
||||
n.typ = &itype{}
|
||||
*n.typ = *ctyp
|
||||
@@ -1459,10 +1469,11 @@ func (n *node) isType(sc *scope) bool {
|
||||
case selectorExpr:
|
||||
pkg, name := n.child[0].ident, n.child[1].ident
|
||||
if sym, _, ok := sc.lookup(pkg); ok {
|
||||
if p, ok := n.interp.binPkg[sym.path]; ok && isBinType(p[name]) {
|
||||
path := sym.typ.path
|
||||
if p, ok := n.interp.binPkg[path]; ok && isBinType(p[name]) {
|
||||
return true // Imported binary type
|
||||
}
|
||||
if p, ok := n.interp.scopes[pkg]; ok && p.sym[name] != nil && p.sym[name].kind == typeSym {
|
||||
if p, ok := n.interp.srcPkg[path]; ok && p[name] != nil && p[name].kind == typeSym {
|
||||
return true // Imported source type
|
||||
}
|
||||
}
|
||||
@@ -1514,7 +1525,57 @@ func wireChild(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
// last returns the last child of a node
|
||||
// isInteger returns true if node type is integer, false otherwise
|
||||
func (n *node) isInteger() bool {
|
||||
if isInt(n.typ.TypeOf()) {
|
||||
return true
|
||||
}
|
||||
if n.typ.untyped && n.rval.IsValid() {
|
||||
t := n.rval.Type()
|
||||
if isInt(t) {
|
||||
return true
|
||||
}
|
||||
if isFloat(t) {
|
||||
// untyped float constant with null decimal part is ok
|
||||
f := n.rval.Float()
|
||||
if f == math.Round(f) {
|
||||
n.rval = reflect.ValueOf(int(f))
|
||||
n.typ.rtype = n.rval.Type()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isNatural returns true if node type is natural, false otherwise
|
||||
func (n *node) isNatural() bool {
|
||||
if isUint(n.typ.TypeOf()) {
|
||||
return true
|
||||
}
|
||||
if n.typ.untyped && n.rval.IsValid() {
|
||||
t := n.rval.Type()
|
||||
if isUint(t) {
|
||||
return true
|
||||
}
|
||||
if isInt(t) && n.rval.Int() >= 0 {
|
||||
// positive untyped integer constant is ok
|
||||
return true
|
||||
}
|
||||
if isFloat(t) {
|
||||
// positive untyped float constant with null decimal part is ok
|
||||
f := n.rval.Float()
|
||||
if f == math.Round(f) && f >= 0 {
|
||||
n.rval = reflect.ValueOf(uint(f))
|
||||
n.typ.rtype = n.rval.Type()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// lastChild returns the last child of a node
|
||||
func (n *node) lastChild() *node { return n.child[len(n.child)-1] }
|
||||
|
||||
func isKey(n *node) bool {
|
||||
@@ -1552,10 +1613,6 @@ func isMapEntry(n *node) bool {
|
||||
return n.action == aGetIndex && n.child[0].typ.cat == mapT
|
||||
}
|
||||
|
||||
func isBuiltinCall(n *node) bool {
|
||||
return n.kind == callExpr && n.child[0].sym != nil && n.child[0].sym.kind == bltnSym
|
||||
}
|
||||
|
||||
func isBinCall(n *node) bool {
|
||||
return n.kind == callExpr && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
|
||||
}
|
||||
@@ -1569,7 +1626,7 @@ func variadicPos(n *node) int {
|
||||
return -1
|
||||
}
|
||||
last := len(n.child[0].typ.arg) - 1
|
||||
if n.child[0].typ.arg[last].variadic {
|
||||
if n.child[0].typ.arg[last].cat == variadicT {
|
||||
return last
|
||||
}
|
||||
return -1
|
||||
@@ -1592,6 +1649,13 @@ func getExec(n *node) bltn {
|
||||
return n.exec
|
||||
}
|
||||
|
||||
func fileNode(n *node) *node {
|
||||
if n == nil || n.kind == fileStmt {
|
||||
return n
|
||||
}
|
||||
return fileNode(n.anc)
|
||||
}
|
||||
|
||||
// setExec recursively sets the node exec builtin function by walking the CFG
|
||||
// from the entry point (first node to exec).
|
||||
func setExec(n *node) {
|
||||
@@ -1644,7 +1708,8 @@ func gotoLabel(s *symbol) {
|
||||
|
||||
func compositeGenerator(n *node) (gen bltnGenerator) {
|
||||
switch n.typ.cat {
|
||||
case aliasT:
|
||||
case aliasT, ptrT:
|
||||
n.typ.val.untyped = n.typ.untyped
|
||||
n.typ = n.typ.val
|
||||
gen = compositeGenerator(n)
|
||||
case arrayT:
|
||||
@@ -1652,7 +1717,7 @@ func compositeGenerator(n *node) (gen bltnGenerator) {
|
||||
case mapT:
|
||||
gen = mapLit
|
||||
case structT:
|
||||
if n.lastChild().kind == keyValueExpr {
|
||||
if len(n.child) > 0 && n.lastChild().kind == keyValueExpr {
|
||||
gen = compositeSparse
|
||||
} else {
|
||||
gen = compositeLit
|
||||
|
||||
@@ -9,10 +9,11 @@ import (
|
||||
// variables and functions symbols at package level, prior to CFG.
|
||||
// All function bodies are skipped. GTA is necessary to handle out of
|
||||
// order declarations and multiple source files packages.
|
||||
func (interp *Interpreter) gta(root *node, rpath string) error {
|
||||
func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
|
||||
sc, _ := interp.initScopePkg(root)
|
||||
var err error
|
||||
var iotaValue int
|
||||
var revisit []*node
|
||||
|
||||
root.Walk(func(n *node) bool {
|
||||
if err != nil {
|
||||
@@ -42,23 +43,24 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
|
||||
|
||||
for i := 0; i < n.nleft; i++ {
|
||||
dest, src := n.child[i], n.child[sbase+i]
|
||||
typ := atyp
|
||||
val := reflect.ValueOf(iotaValue)
|
||||
typ := atyp
|
||||
if typ == nil {
|
||||
if typ, err = nodeType(interp, sc, src); err != nil {
|
||||
return false
|
||||
}
|
||||
val = src.rval
|
||||
}
|
||||
var index int
|
||||
if !typ.incomplete {
|
||||
if typ.cat == nilT {
|
||||
err = n.cfgErrorf("use of untyped nil")
|
||||
return false
|
||||
}
|
||||
index = sc.add(typ)
|
||||
if typ.incomplete {
|
||||
// Come back when type is known
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: index, typ: typ, rval: val}
|
||||
if typ.cat == nilT {
|
||||
err = n.cfgErrorf("use of untyped nil")
|
||||
return false
|
||||
}
|
||||
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val}
|
||||
if n.anc.kind == constDecl {
|
||||
sc.sym[dest.ident].kind = constSym
|
||||
iotaValue++
|
||||
@@ -70,48 +72,50 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
|
||||
err = compDefineX(sc, n)
|
||||
|
||||
case valueSpec:
|
||||
// TODO: handle global ValueSpec
|
||||
//err = n.cfgError("global ValueSpec not implemented")
|
||||
l := len(n.child) - 1
|
||||
if n.typ = n.child[l].typ; n.typ == nil {
|
||||
if n.typ, err = nodeType(interp, sc, n.child[l]); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, c := range n.child[:l] {
|
||||
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ}
|
||||
}
|
||||
|
||||
case funcDecl:
|
||||
if n.typ, err = nodeType(interp, sc, n.child[2]); err != nil {
|
||||
return false
|
||||
}
|
||||
if !isMethod(n) {
|
||||
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
|
||||
}
|
||||
if len(n.child[0].child) > 0 {
|
||||
// function is a method, add it to the related type
|
||||
if isMethod(n) {
|
||||
// Add a method symbol in the receiver type name space
|
||||
var rcvrtype *itype
|
||||
var typeName string
|
||||
n.ident = n.child[1].ident
|
||||
rcvr := n.child[0].child[0]
|
||||
if len(rcvr.child) < 2 {
|
||||
// Receiver var name is skipped in method declaration (fix that in AST ?)
|
||||
typeName = rcvr.child[0].ident
|
||||
} else {
|
||||
typeName = rcvr.child[1].ident
|
||||
}
|
||||
rtn := rcvr.lastChild()
|
||||
typeName := rtn.ident
|
||||
if typeName == "" {
|
||||
// The receiver is a pointer, retrieve typeName from indirection
|
||||
typeName = rcvr.lastChild().child[0].ident
|
||||
typeName = rtn.child[0].ident
|
||||
elementType := sc.getType(typeName)
|
||||
if elementType == nil {
|
||||
// Add type if necessary, so method can be registered
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath}}
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, path: rpath, incomplete: true, node: rtn.child[0], scope: sc}}
|
||||
elementType = sc.sym[typeName].typ
|
||||
}
|
||||
rcvrtype = &itype{cat: ptrT, val: elementType}
|
||||
rcvrtype = &itype{cat: ptrT, val: elementType, incomplete: elementType.incomplete, node: rtn, scope: sc}
|
||||
elementType.method = append(elementType.method, n)
|
||||
} else {
|
||||
rcvrtype = sc.getType(typeName)
|
||||
if rcvrtype == nil {
|
||||
// Add type if necessary, so method can be registered
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath}}
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, path: rpath, incomplete: true, node: rtn, scope: sc}}
|
||||
rcvrtype = sc.sym[typeName].typ
|
||||
}
|
||||
}
|
||||
rcvrtype.method = append(rcvrtype.method, n)
|
||||
} else {
|
||||
// Add a function symbol in the package name space
|
||||
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -124,8 +128,11 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
|
||||
ipath = n.child[0].rval.String()
|
||||
name = path.Base(ipath)
|
||||
}
|
||||
// Try to import a binary package first, or a source package
|
||||
if interp.binPkg[ipath] != nil {
|
||||
if name == "." {
|
||||
switch name {
|
||||
case "_": // no import of symbols
|
||||
case ".": // import symbols in current scope
|
||||
for n, v := range interp.binPkg[ipath] {
|
||||
typ := v.Type()
|
||||
if isBinType(v) {
|
||||
@@ -133,14 +140,24 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
|
||||
}
|
||||
sc.sym[n] = &symbol{kind: binSym, typ: &itype{cat: valueT, rtype: typ}, rval: v}
|
||||
}
|
||||
} else {
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT}, path: ipath}
|
||||
default: // import symbols in package namespace
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath}}
|
||||
}
|
||||
} else if err = interp.importSrc(rpath, ipath, name); err == nil {
|
||||
sc.types = interp.universe.types
|
||||
switch name {
|
||||
case "_": // no import of symbols
|
||||
case ".": // import symbols in current namespace
|
||||
for k, v := range interp.srcPkg[ipath] {
|
||||
if canExport(k) {
|
||||
sc.sym[k] = v
|
||||
}
|
||||
}
|
||||
default: // import symbols in package namespace
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: ipath}}
|
||||
}
|
||||
} else {
|
||||
// TODO: make sure we do not import a src package more than once
|
||||
err = interp.importSrcFile(rpath, ipath, name)
|
||||
sc.types = interp.universe.types
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT}, path: ipath}
|
||||
err = n.cfgErrorf("import %q error: %v", ipath, err)
|
||||
}
|
||||
|
||||
case typeSpec:
|
||||
@@ -150,19 +167,23 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
|
||||
return false
|
||||
}
|
||||
if n.child[1].kind == identExpr {
|
||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName, pkgPath: rpath}
|
||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName, path: rpath, field: typ.field, incomplete: typ.incomplete}
|
||||
copy(n.typ.method, typ.method)
|
||||
} else {
|
||||
n.typ = typ
|
||||
n.typ.name = typeName
|
||||
n.typ.pkgPath = rpath
|
||||
n.typ.path = rpath
|
||||
}
|
||||
// Type may already be declared for a receiver in a method function
|
||||
// Type may be already declared for a receiver in a method function
|
||||
if sc.sym[typeName] == nil {
|
||||
sc.sym[typeName] = &symbol{kind: typeSym}
|
||||
} else {
|
||||
n.typ.method = append(n.typ.method, sc.sym[typeName].typ.method...)
|
||||
}
|
||||
sc.sym[typeName].typ = n.typ
|
||||
if n.typ.incomplete {
|
||||
revisit = append(revisit, n)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -171,5 +192,5 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
|
||||
if sc != interp.universe {
|
||||
sc.pop()
|
||||
}
|
||||
return err
|
||||
return revisit, err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package interp
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"os"
|
||||
@@ -53,15 +54,18 @@ type frame struct {
|
||||
recovered interface{} // to handle panic recover
|
||||
}
|
||||
|
||||
// Exports stores the map of external values per package
|
||||
// Exports stores the map of binary packages per package path
|
||||
type Exports map[string]map[string]reflect.Value
|
||||
|
||||
// imports stores the map of source packages per package path
|
||||
type imports map[string]map[string]*symbol
|
||||
|
||||
// opt stores interpreter options
|
||||
type opt struct {
|
||||
astDot bool // display AST graph (debug)
|
||||
cfgDot bool // display CFG graph (debug)
|
||||
noRun bool // compile, but do not run
|
||||
goPath string // custom GOPATH
|
||||
astDot bool // display AST graph (debug)
|
||||
cfgDot bool // display CFG graph (debug)
|
||||
noRun bool // compile, but do not run
|
||||
context build.Context // build context: GOPATH, build constraints
|
||||
}
|
||||
|
||||
// Interpreter contains global resources and state
|
||||
@@ -73,7 +77,9 @@ type Interpreter struct {
|
||||
fset *token.FileSet // fileset to locate node in source code
|
||||
universe *scope // interpreter global level scope
|
||||
scopes map[string]*scope // package level scopes, indexed by package name
|
||||
binPkg Exports // runtime binary values used in interpreter
|
||||
binPkg Exports // binary packages used in interpreter, indexed by path
|
||||
srcPkg imports // source packages used in interpreter, indexed by path
|
||||
rdir map[string]bool // for src import cycle detection
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -118,17 +124,26 @@ func (n *node) Walk(in func(n *node) bool, out func(n *node)) {
|
||||
type Options struct {
|
||||
// GoPath sets GOPATH for the interpreter
|
||||
GoPath string
|
||||
// BuildTags sets build constraints for the interpreter
|
||||
BuildTags []string
|
||||
}
|
||||
|
||||
// New returns a new interpreter
|
||||
func New(options Options) *Interpreter {
|
||||
i := Interpreter{
|
||||
opt: opt{goPath: options.GoPath},
|
||||
opt: opt{context: build.Default},
|
||||
frame: &frame{data: []reflect.Value{}},
|
||||
fset: token.NewFileSet(),
|
||||
universe: initUniverse(),
|
||||
scopes: map[string]*scope{},
|
||||
binPkg: Exports{"": map[string]reflect.Value{"_error": reflect.ValueOf((*_error)(nil))}},
|
||||
frame: &frame{data: []reflect.Value{}},
|
||||
srcPkg: imports{},
|
||||
rdir: map[string]bool{},
|
||||
}
|
||||
|
||||
i.opt.context.GOPATH = options.GoPath
|
||||
if len(options.BuildTags) > 0 {
|
||||
i.opt.context.BuildTags = options.BuildTags
|
||||
}
|
||||
|
||||
// AstDot activates AST graph display for the interpreter
|
||||
@@ -237,9 +252,15 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
|
||||
}
|
||||
|
||||
// Global type analysis
|
||||
if err = interp.gta(root, pkgName); err != nil {
|
||||
revisit, err := interp.gta(root, pkgName)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
for _, n := range revisit {
|
||||
if _, err = interp.gta(n, pkgName); err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
// Annotate AST with CFG infos
|
||||
initNodes, err := interp.cfg(root)
|
||||
@@ -258,7 +279,8 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
|
||||
}
|
||||
if interp.universe.sym[pkgName] == nil {
|
||||
// Make the package visible under a path identical to its name
|
||||
interp.universe.sym[pkgName] = &symbol{typ: &itype{cat: srcPkgT}, path: pkgName}
|
||||
interp.srcPkg[pkgName] = interp.scopes[pkgName].sym
|
||||
interp.universe.sym[pkgName] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: pkgName}}
|
||||
}
|
||||
|
||||
if interp.cfgDot {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package interp_test
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -31,8 +32,10 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
|
||||
for _, file := range files {
|
||||
if filepath.Ext(file.Name()) != ".go" ||
|
||||
file.Name() == "bad0.go" || // expect error
|
||||
file.Name() == "export1.go" || // non-main package
|
||||
file.Name() == "export0.go" || // non-main package
|
||||
file.Name() == "import6.go" || // expect error
|
||||
file.Name() == "io0.go" || // use random number
|
||||
file.Name() == "op1.go" || // expect error
|
||||
file.Name() == "bltn0.go" || // expect error
|
||||
@@ -77,7 +80,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
i := interp.New(interp.Options{})
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
|
||||
i.Name = filePath
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(interp.Symbols)
|
||||
@@ -128,6 +131,11 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
expectedInterp string
|
||||
expectedExec string
|
||||
}{
|
||||
{
|
||||
fileName: "bad0.go",
|
||||
expectedInterp: "1:1: expected 'package', found println",
|
||||
expectedExec: "1:1: expected 'package', found println",
|
||||
},
|
||||
{
|
||||
fileName: "op1.go",
|
||||
expectedInterp: "5:2: illegal operand types for '+=' operator",
|
||||
@@ -137,6 +145,11 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
fileName: "bltn0.go",
|
||||
expectedInterp: "4:7: use of builtin println not in function call",
|
||||
},
|
||||
{
|
||||
fileName: "import6.go",
|
||||
expectedInterp: "import cycle not allowed",
|
||||
expectedExec: "import cycle not allowed",
|
||||
},
|
||||
{
|
||||
fileName: "switch8.go",
|
||||
expectedInterp: "5:2: fallthrough statement out of place",
|
||||
@@ -172,7 +185,7 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
i := interp.New(interp.Options{})
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
|
||||
i.Name = filePath
|
||||
i.Use(stdlib.Symbols)
|
||||
|
||||
|
||||
@@ -44,11 +44,13 @@ func TestEvalArithmetic(t *testing.T) {
|
||||
{desc: "rem_FI", src: "8.0 % 4", err: "1:28: illegal operand types for '%' operator"},
|
||||
{desc: "shl_II", src: "1 << 8", res: "256"},
|
||||
{desc: "shl_IN", src: "1 << -1", err: "1:28: illegal operand types for '<<' operator"},
|
||||
{desc: "shl_IF", src: "1 << 1.0", err: "1:28: illegal operand types for '<<' operator"},
|
||||
{desc: "shl_IF", src: "1.0 << 1", err: "1:28: illegal operand types for '<<' operator"},
|
||||
{desc: "shl_IF", src: "1 << 1.0", res: "2"},
|
||||
{desc: "shl_IF1", src: "1 << 1.1", err: "1:28: illegal operand types for '<<' operator"},
|
||||
{desc: "shl_IF", src: "1.0 << 1", res: "2"},
|
||||
{desc: "shr_II", src: "1 >> 8", res: "0"},
|
||||
{desc: "shr_IN", src: "1 >> -1", err: "1:28: illegal operand types for '>>' operator"},
|
||||
{desc: "shr_IF", src: "1 >> 1.0", err: "1:28: illegal operand types for '>>' operator"},
|
||||
{desc: "shr_IF", src: "1 >> 1.0", res: "0"},
|
||||
{desc: "shr_IF1", src: "1 >> 1.1", err: "1:28: illegal operand types for '>>' operator"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -112,7 +114,7 @@ func TestEvalNil(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(stdlib.Symbols)
|
||||
runTests(t, i, []testCase{
|
||||
{desc: "assign nil", src: "a := nil", err: "1:28: use of untyped nil"},
|
||||
{desc: "assign nil", src: "a := nil", err: "1:33: use of untyped nil"},
|
||||
{desc: "return nil", pre: func() { eval(t, i, "func getNil() error {return nil}") }, src: "getNil()", res: "<nil>"},
|
||||
{
|
||||
desc: "return func which return error",
|
||||
@@ -368,6 +370,32 @@ func TestEvalChan(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalMissingSymbol(t *testing.T) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r != nil {
|
||||
t.Errorf("unexpected panic: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
type S2 struct{}
|
||||
type S1 struct {
|
||||
F S2
|
||||
}
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(interp.Exports{"p": map[string]reflect.Value{
|
||||
"S1": reflect.Zero(reflect.TypeOf(&S1{})),
|
||||
}})
|
||||
_, err := i.Eval(`import "p"`)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to import package: %v", err)
|
||||
}
|
||||
_, err = i.Eval(`p.S1{F: p.S2{}}`)
|
||||
if err == nil {
|
||||
t.Error("unexpected nil error for expression with undefined type")
|
||||
}
|
||||
}
|
||||
|
||||
func runTests(t *testing.T, i *interp.Interpreter, tests []testCase) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package interp_test
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
@@ -51,7 +52,7 @@ func runCheck(t *testing.T, p string) {
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
i := interp.New(interp.Options{})
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
|
||||
i.Name = p
|
||||
i.Use(interp.Symbols)
|
||||
i.Use(stdlib.Symbols)
|
||||
|
||||
103
interp/run.go
103
interp/run.go
@@ -198,12 +198,12 @@ func convert(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func isRecursiveStruct(t *itype) bool {
|
||||
if t.cat == structT && t.rtype.Kind() == reflect.Interface {
|
||||
func isRecursiveStruct(t *itype, rtype reflect.Type) bool {
|
||||
if t.cat == structT && rtype.Kind() == reflect.Interface {
|
||||
return true
|
||||
}
|
||||
if t.cat == ptrT {
|
||||
return isRecursiveStruct(t.val)
|
||||
return isRecursiveStruct(t.val, t.rtype.Elem())
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -230,7 +230,7 @@ func assign(n *node) {
|
||||
case src.kind == basicLit && src.val == nil:
|
||||
t := dest.typ.TypeOf()
|
||||
svalue[i] = func(*frame) reflect.Value { return reflect.New(t).Elem() }
|
||||
case isRecursiveStruct(dest.typ):
|
||||
case isRecursiveStruct(dest.typ, dest.typ.rtype):
|
||||
svalue[i] = genValueInterfacePtr(src)
|
||||
default:
|
||||
svalue[i] = genValue(src)
|
||||
@@ -502,7 +502,7 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
methods[i], indexes[i] = n.typ.lookupMethod(names[i])
|
||||
if methods[i] == nil && n.typ.cat != nilT {
|
||||
// interpreted method not found, look for binary method, possibly embedded
|
||||
_, indexes[i], _ = n.typ.lookupBinMethod(names[i])
|
||||
_, indexes[i], _, _ = n.typ.lookupBinMethod(names[i])
|
||||
}
|
||||
}
|
||||
wrap := n.interp.getWrapper(typ)
|
||||
@@ -614,7 +614,7 @@ func call(n *node) {
|
||||
if c.kind == basicLit {
|
||||
var argType reflect.Type
|
||||
if variadic >= 0 && i >= variadic {
|
||||
argType = n.child[0].typ.arg[variadic].TypeOf()
|
||||
argType = n.child[0].typ.arg[variadic].val.TypeOf()
|
||||
} else {
|
||||
argType = n.child[0].typ.arg[i].TypeOf()
|
||||
}
|
||||
@@ -671,7 +671,11 @@ func call(n *node) {
|
||||
|
||||
// Init variadic argument vector
|
||||
if variadic >= 0 {
|
||||
vararg = nf.data[numRet+variadic]
|
||||
if method {
|
||||
vararg = nf.data[numRet+variadic+1]
|
||||
} else {
|
||||
vararg = nf.data[numRet+variadic]
|
||||
}
|
||||
}
|
||||
|
||||
// Copy input parameters from caller
|
||||
@@ -887,7 +891,7 @@ func getIndexBinPtrMethod(n *node) {
|
||||
// getIndexArray returns array value from index
|
||||
func getIndexArray(n *node) {
|
||||
tnext := getExec(n.tnext)
|
||||
value0 := genValue(n.child[0]) // array
|
||||
value0 := genValueArray(n.child[0]) // array
|
||||
|
||||
if n.child[1].rval.IsValid() { // constant array index
|
||||
ai := int(vInt(n.child[1].rval))
|
||||
@@ -1087,7 +1091,7 @@ func getPtrIndexSeq(n *node) {
|
||||
index := n.val.([]int)
|
||||
tnext := getExec(n.tnext)
|
||||
var value func(*frame) reflect.Value
|
||||
if isRecursiveStruct(n.child[0].typ) {
|
||||
if isRecursiveStruct(n.child[0].typ, n.child[0].typ.rtype) {
|
||||
v := genValue(n.child[0])
|
||||
value = func(f *frame) reflect.Value { return v(f).Elem().Elem() }
|
||||
} else {
|
||||
@@ -1130,6 +1134,27 @@ func getIndexSeqField(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func getIndexSeqPtrMethod(n *node) {
|
||||
value := genValue(n.child[0])
|
||||
index := n.val.([]int)
|
||||
fi := index[1:]
|
||||
mi := index[0]
|
||||
i := n.findex
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if n.child[0].typ.TypeOf().Kind() == reflect.Ptr {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem().FieldByIndex(fi).Addr().Method(mi)
|
||||
return next
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(fi).Addr().Method(mi)
|
||||
return next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getIndexSeqMethod(n *node) {
|
||||
value := genValue(n.child[0])
|
||||
index := n.val.([]int)
|
||||
@@ -1253,6 +1278,12 @@ func _return(n *node) {
|
||||
switch t := def.typ.ret[i]; t.cat {
|
||||
case errorT:
|
||||
values[i] = genInterfaceWrapper(c, t.TypeOf())
|
||||
case aliasT:
|
||||
if isInterfaceSrc(t) {
|
||||
values[i] = genValueInterface(c)
|
||||
} else {
|
||||
values[i] = genValue(c)
|
||||
}
|
||||
case interfaceT:
|
||||
values[i] = genValueInterface(c)
|
||||
default:
|
||||
@@ -1453,7 +1484,11 @@ func compositeLit(n *node) {
|
||||
for i, v := range values {
|
||||
a.Field(i).Set(v(f))
|
||||
}
|
||||
value(f).Set(a)
|
||||
if d := value(f); d.Type().Kind() == reflect.Ptr {
|
||||
d.Set(a.Addr())
|
||||
} else {
|
||||
d.Set(a)
|
||||
}
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -1484,21 +1519,33 @@ func compositeSparse(n *node) {
|
||||
for i, v := range values {
|
||||
a.Field(i).Set(v(f))
|
||||
}
|
||||
value(f).Set(a)
|
||||
if d := value(f); d.Type().Kind() == reflect.Ptr {
|
||||
d.Set(a.Addr())
|
||||
} else {
|
||||
d.Set(a)
|
||||
}
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
func empty(n *node) {}
|
||||
|
||||
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
|
||||
fnext := getExec(n.fnext)
|
||||
tnext := getExec(n.tnext)
|
||||
|
||||
var value func(*frame) reflect.Value
|
||||
if len(n.child) == 4 {
|
||||
index1 := n.child[1].findex // array value location in frame
|
||||
value := genValue(n.child[2]) // array
|
||||
an := n.child[2]
|
||||
index1 := n.child[1].findex // array value location in frame
|
||||
if isString(an.typ.TypeOf()) {
|
||||
value = genValueAs(an, rat) // range on string iterates over runes
|
||||
} else {
|
||||
value = genValueArray(an)
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
a := value(f)
|
||||
v0 := f.data[index0]
|
||||
@@ -1511,7 +1558,12 @@ func _range(n *node) {
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
value := genValue(n.child[1]) // array
|
||||
an := n.child[1]
|
||||
if isString(an.typ.TypeOf()) {
|
||||
value = genValueAs(an, rat) // range on string iterates over runes
|
||||
} else {
|
||||
value = genValueArray(an)
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
a := value(f)
|
||||
v0 := f.data[index0]
|
||||
@@ -1709,6 +1761,11 @@ func appendSlice(n *node) {
|
||||
}
|
||||
|
||||
func _append(n *node) {
|
||||
if c1, c2 := n.child[1], n.child[2]; len(n.child) == 3 && c2.typ.cat == arrayT && c2.typ.val.id() == n.typ.val.id() ||
|
||||
isByteArray(c1.typ.TypeOf()) && isString(c2.typ.TypeOf()) {
|
||||
appendSlice(n)
|
||||
return
|
||||
}
|
||||
dest := genValue(n)
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
@@ -1719,7 +1776,7 @@ func _append(n *node) {
|
||||
values := make([]func(*frame) reflect.Value, l)
|
||||
for i, arg := range args {
|
||||
switch {
|
||||
case isRecursiveStruct(n.typ.val):
|
||||
case isRecursiveStruct(n.typ.val, n.typ.val.rtype):
|
||||
values[i] = genValueInterfacePtr(arg)
|
||||
case arg.typ.untyped:
|
||||
values[i] = genValueAs(arg, n.child[1].typ.TypeOf().Elem())
|
||||
@@ -1739,7 +1796,7 @@ func _append(n *node) {
|
||||
} else {
|
||||
var value0 func(*frame) reflect.Value
|
||||
switch {
|
||||
case isRecursiveStruct(n.typ.val):
|
||||
case isRecursiveStruct(n.typ.val, n.typ.val.rtype):
|
||||
value0 = genValueInterfacePtr(n.child[2])
|
||||
case n.child[2].typ.untyped:
|
||||
value0 = genValueAs(n.child[2], n.child[1].typ.TypeOf().Elem())
|
||||
@@ -1767,7 +1824,7 @@ func _cap(n *node) {
|
||||
|
||||
func _copy(n *node) {
|
||||
dest := genValue(n)
|
||||
value0 := genValue(n.child[1])
|
||||
value0 := genValueArray(n.child[1])
|
||||
value1 := genValue(n.child[2])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
@@ -1921,7 +1978,7 @@ func reset(n *node) {
|
||||
|
||||
switch l := len(n.child) - 1; l {
|
||||
case 1:
|
||||
typ := n.child[0].typ.TypeOf()
|
||||
typ := n.child[0].typ.frameType()
|
||||
i := n.child[0].findex
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = reflect.New(typ).Elem()
|
||||
@@ -1930,7 +1987,7 @@ func reset(n *node) {
|
||||
case 2:
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
i0, i1 := c0.findex, c1.findex
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
t0, t1 := c0.typ.frameType(), c1.typ.frameType()
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i0] = reflect.New(t0).Elem()
|
||||
f.data[i1] = reflect.New(t1).Elem()
|
||||
@@ -1941,7 +1998,7 @@ func reset(n *node) {
|
||||
index := make([]int, l)
|
||||
for i, c := range n.child[:l] {
|
||||
index[i] = c.findex
|
||||
types[i] = c.typ.TypeOf()
|
||||
types[i] = c.typ.frameType()
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
for i, ind := range index {
|
||||
@@ -2096,8 +2153,8 @@ func _select(n *node) {
|
||||
func slice(n *node) {
|
||||
i := n.findex
|
||||
next := getExec(n.tnext)
|
||||
value0 := genValue(n.child[0]) // array
|
||||
value1 := genValue(n.child[1]) // low (if 2 or 3 args) or high (if 1 arg)
|
||||
value0 := genValueArray(n.child[0]) // array
|
||||
value1 := genValue(n.child[1]) // low (if 2 or 3 args) or high (if 1 arg)
|
||||
|
||||
switch len(n.child) {
|
||||
case 2:
|
||||
@@ -2130,7 +2187,7 @@ func slice(n *node) {
|
||||
func slice0(n *node) {
|
||||
i := n.findex
|
||||
next := getExec(n.tnext)
|
||||
value0 := genValue(n.child[0])
|
||||
value0 := genValueArray(n.child[0])
|
||||
|
||||
switch len(n.child) {
|
||||
case 1:
|
||||
|
||||
@@ -44,17 +44,15 @@ func (k sKind) String() string {
|
||||
// A symbol represents an interpreter object such as type, constant, var, func,
|
||||
// label, builtin or binary object. Symbols are defined within a scope.
|
||||
type symbol struct {
|
||||
kind sKind
|
||||
typ *itype // Type of value
|
||||
node *node // Node value if index is negative
|
||||
from []*node // list of nodes jumping to node if kind is label, or nil
|
||||
recv *receiver // receiver node value, if sym refers to a method
|
||||
index int // index of value in frame or -1
|
||||
rval reflect.Value // default value (used for constants)
|
||||
path string // package path if typ.cat is SrcPkgT or BinPkgT
|
||||
builtin bltnGenerator // Builtin function or nil
|
||||
global bool // true if symbol is defined in global space
|
||||
recursive bool // true if symbol is a recursive type definition
|
||||
kind sKind
|
||||
typ *itype // Type of value
|
||||
node *node // Node value if index is negative
|
||||
from []*node // list of nodes jumping to node if kind is label, or nil
|
||||
recv *receiver // receiver node value, if sym refers to a method
|
||||
index int // index of value in frame or -1
|
||||
rval reflect.Value // default value (used for constants)
|
||||
builtin bltnGenerator // Builtin function or nil
|
||||
global bool // true if symbol is defined in global space
|
||||
// TODO: implement constant checking
|
||||
//constant bool // true if symbol value is constant
|
||||
}
|
||||
@@ -162,8 +160,8 @@ func (interp *Interpreter) initScopePkg(n *node) (*scope, string) {
|
||||
sc := interp.universe
|
||||
pkgName := mainID
|
||||
|
||||
if n.kind == fileStmt {
|
||||
pkgName = n.child[0].ident
|
||||
if p := fileNode(n); p != nil {
|
||||
pkgName = p.child[0].ident
|
||||
}
|
||||
|
||||
if _, ok := interp.scopes[pkgName]; !ok {
|
||||
|
||||
@@ -8,10 +8,14 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (interp *Interpreter) importSrcFile(rPath, path, alias string) error {
|
||||
func (interp *Interpreter) importSrc(rPath, path, alias string) error {
|
||||
var dir string
|
||||
var err error
|
||||
|
||||
if interp.srcPkg[path] != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// For relative import paths in the form "./xxx" or "../xxx", the initial
|
||||
// base path is the directory of the interpreter input file, or "." if no file
|
||||
// was provided.
|
||||
@@ -22,9 +26,13 @@ func (interp *Interpreter) importSrcFile(rPath, path, alias string) error {
|
||||
rPath = "."
|
||||
}
|
||||
dir = filepath.Join(filepath.Dir(interp.Name), rPath, path)
|
||||
} else if dir, rPath, err = pkgDir(interp.goPath, rPath, path); err != nil {
|
||||
} else if dir, rPath, err = pkgDir(interp.context.GOPATH, rPath, path); err != nil {
|
||||
return err
|
||||
}
|
||||
if interp.rdir[path] {
|
||||
return fmt.Errorf("import cycle not allowed\n\timports %s", path)
|
||||
}
|
||||
interp.rdir[path] = true
|
||||
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
@@ -33,6 +41,7 @@ func (interp *Interpreter) importSrcFile(rPath, path, alias string) error {
|
||||
|
||||
var initNodes []*node
|
||||
var rootNodes []*node
|
||||
revisit := make(map[string][]*node)
|
||||
|
||||
var root *node
|
||||
var pkgName string
|
||||
@@ -40,7 +49,7 @@ func (interp *Interpreter) importSrcFile(rPath, path, alias string) error {
|
||||
// Parse source files
|
||||
for _, file := range files {
|
||||
name := file.Name()
|
||||
if skipFile(name) {
|
||||
if skipFile(interp.context, name) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -65,9 +74,21 @@ func (interp *Interpreter) importSrcFile(rPath, path, alias string) error {
|
||||
rootNodes = append(rootNodes, root)
|
||||
|
||||
subRPath := effectivePkg(rPath, path)
|
||||
if err = interp.gta(root, subRPath); err != nil {
|
||||
var list []*node
|
||||
list, err = interp.gta(root, subRPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
revisit[subRPath] = append(revisit[subRPath], list...)
|
||||
}
|
||||
|
||||
// revisit incomplete nodes where GTA could not complete
|
||||
for pkg, nodes := range revisit {
|
||||
for _, n := range nodes {
|
||||
if _, err = interp.gta(n, pkg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate control flow graphs
|
||||
@@ -79,6 +100,10 @@ func (interp *Interpreter) importSrcFile(rPath, path, alias string) error {
|
||||
initNodes = append(initNodes, nodes...)
|
||||
}
|
||||
|
||||
// Register source package in the interpreter. The package contains only
|
||||
// the global symbols in the package scope.
|
||||
interp.srcPkg[path] = interp.scopes[pkgName].sym
|
||||
|
||||
// Rename imported pkgName to alias if they are different
|
||||
if pkgName != alias {
|
||||
interp.scopes[alias] = interp.scopes[pkgName]
|
||||
|
||||
350
interp/type.go
350
interp/type.go
@@ -44,6 +44,7 @@ const (
|
||||
uint64T
|
||||
uintptrT
|
||||
valueT
|
||||
variadicT
|
||||
maxT
|
||||
)
|
||||
|
||||
@@ -82,6 +83,7 @@ var cats = [...]string{
|
||||
uint64T: "uint64T",
|
||||
uintptrT: "uintptrT",
|
||||
valueT: "valueT",
|
||||
variadicT: "variadicT",
|
||||
}
|
||||
|
||||
func (c tcat) String() string {
|
||||
@@ -102,17 +104,16 @@ type structField struct {
|
||||
// itype defines the internal representation of types in the interpreter
|
||||
type itype struct {
|
||||
cat tcat // Type category
|
||||
field []structField // Array of struct fields if StrucT or InterfaceT
|
||||
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, MapT, PtrT, AliasT or ArrayT
|
||||
arg []*itype // Argument types if FuncT or nil
|
||||
ret []*itype // Return types if FuncT or nil
|
||||
val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT
|
||||
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
|
||||
pkgPath string // for a defined type, the package import path
|
||||
path string // for a defined type, the package import path
|
||||
size int // Size of array if ArrayT
|
||||
rtype reflect.Type // Reflection type if ValueT, or nil
|
||||
variadic bool // true if type is variadic
|
||||
incomplete bool // true if type must be parsed again (out of order declarations)
|
||||
untyped bool // true for a literal value (string or number)
|
||||
sizedef bool // true if array size is computed from type definition
|
||||
@@ -130,6 +131,16 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
|
||||
var t = &itype{node: n, scope: sc}
|
||||
|
||||
if n.anc.kind == typeSpec {
|
||||
name := n.anc.child[0].ident
|
||||
if sym := sc.sym[name]; sym != nil {
|
||||
// recover previously declared methods
|
||||
t.method = sym.typ.method
|
||||
t.path = sym.typ.path
|
||||
t.name = name
|
||||
}
|
||||
}
|
||||
|
||||
switch n.kind {
|
||||
case addressExpr, starExpr:
|
||||
t.cat = ptrT
|
||||
@@ -204,14 +215,12 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
t.name = "float64"
|
||||
t.untyped = true
|
||||
case int:
|
||||
if isShiftOperand(n) && v >= 0 {
|
||||
t.cat = uintT
|
||||
t.name = "uint"
|
||||
n.rval = reflect.ValueOf(uint(v))
|
||||
} else {
|
||||
t.cat = intT
|
||||
t.name = "int"
|
||||
}
|
||||
t.cat = intT
|
||||
t.name = "int"
|
||||
t.untyped = true
|
||||
case uint:
|
||||
t.cat = uintT
|
||||
t.name = "uint"
|
||||
t.untyped = true
|
||||
case rune:
|
||||
t.cat = runeT
|
||||
@@ -235,7 +244,9 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
if t, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t.untyped {
|
||||
// Shift operator type is inherited from first parameter only
|
||||
// For other operators, infer type in from 2nd parameter in case of untyped first
|
||||
if t.untyped && !isShiftNode(n) {
|
||||
var t1 *itype
|
||||
t1, err = nodeType(interp, sc, n.child[1])
|
||||
if !(t1.untyped && isInt(t1.TypeOf()) && isFloat(t.TypeOf())) {
|
||||
@@ -245,17 +256,78 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
}
|
||||
|
||||
case callExpr:
|
||||
if t, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch t.cat {
|
||||
case valueT:
|
||||
if t.rtype.NumOut() == 1 {
|
||||
t = &itype{cat: valueT, rtype: t.rtype.Out(0)}
|
||||
if interp.isBuiltinCall(n) {
|
||||
// builtin types are special and may depend from their call arguments
|
||||
t.cat = builtinT
|
||||
switch n.child[0].ident {
|
||||
case "complex":
|
||||
var nt0, nt1 *itype
|
||||
if nt0, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if nt1, err = nodeType(interp, sc, n.child[2]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if nt0.incomplete || nt1.incomplete {
|
||||
t.incomplete = true
|
||||
} else {
|
||||
switch t0, t1 := nt0.TypeOf(), nt1.TypeOf(); {
|
||||
case isFloat32(t0) && isFloat32(t1):
|
||||
t = sc.getType("complex64")
|
||||
case isFloat64(t0) && isFloat64(t1):
|
||||
t = sc.getType("complex128")
|
||||
case nt0.untyped && isNumber(t0) && nt1.untyped && isNumber(t1):
|
||||
t = &itype{cat: valueT, rtype: complexType}
|
||||
case nt0.untyped && isFloat32(t1) || nt1.untyped && isFloat32(t0):
|
||||
t = sc.getType("complex64")
|
||||
case nt0.untyped && isFloat64(t1) || nt1.untyped && isFloat64(t0):
|
||||
t = sc.getType("complex128")
|
||||
default:
|
||||
err = n.cfgErrorf("invalid types %s and %s", t0.Kind(), t1.Kind())
|
||||
}
|
||||
}
|
||||
case "real", "imag":
|
||||
if t, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !t.incomplete {
|
||||
switch k := t.TypeOf().Kind(); {
|
||||
case k == reflect.Complex64:
|
||||
t = sc.getType("float32")
|
||||
case k == reflect.Complex128:
|
||||
t = sc.getType("float64")
|
||||
case t.untyped && isNumber(t.TypeOf()):
|
||||
t = &itype{cat: valueT, rtype: floatType}
|
||||
default:
|
||||
err = n.cfgErrorf("invalid complex type %s", k)
|
||||
}
|
||||
}
|
||||
case "cap", "copy", "len":
|
||||
t = sc.getType("int")
|
||||
case "append", "make":
|
||||
t, err = nodeType(interp, sc, n.child[1])
|
||||
case "new":
|
||||
t, err = nodeType(interp, sc, n.child[1])
|
||||
t = &itype{cat: ptrT, val: t, incomplete: t.incomplete}
|
||||
case "recover":
|
||||
t = sc.getType("interface{}")
|
||||
}
|
||||
default:
|
||||
if len(t.ret) == 1 {
|
||||
t = t.ret[0]
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if t, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch t.cat {
|
||||
case valueT:
|
||||
if t.rtype.NumOut() == 1 {
|
||||
t = &itype{cat: valueT, rtype: t.rtype.Out(0)}
|
||||
}
|
||||
default:
|
||||
if len(t.ret) == 1 {
|
||||
t = t.ret[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,10 +342,11 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
t.incomplete = t.val.incomplete
|
||||
|
||||
case ellipsisExpr:
|
||||
if t, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
t.cat = variadicT
|
||||
if t.val, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.variadic = true
|
||||
t.incomplete = t.val.incomplete
|
||||
|
||||
case funcLit:
|
||||
t, err = nodeType(interp, sc, n.child[2])
|
||||
@@ -314,11 +387,6 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
case identExpr:
|
||||
if sym, _, found := sc.lookup(n.ident); found {
|
||||
t = sym.typ
|
||||
if sym.recursive && t.incomplete {
|
||||
t.incomplete = false
|
||||
t.rtype = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
sym.typ = t
|
||||
}
|
||||
if t.incomplete && t.node != n {
|
||||
m := t.method
|
||||
if t, err = nodeType(interp, sc, t.node); err != nil {
|
||||
@@ -334,6 +402,11 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
|
||||
case interfaceType:
|
||||
t.cat = interfaceT
|
||||
if sname := typeName(n); sname != "" {
|
||||
if sym, _, found := sc.lookup(sname); found && sym.kind == typeSym {
|
||||
sym.typ = t
|
||||
}
|
||||
}
|
||||
for _, field := range n.child[0].child {
|
||||
if len(field.child) == 1 {
|
||||
typ, err := nodeType(interp, sc, field.child[0])
|
||||
@@ -366,44 +439,56 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
t, err = nodeType(interp, sc, n.child[0])
|
||||
|
||||
case selectorExpr:
|
||||
pkg, name := n.child[0].ident, n.child[1].ident
|
||||
if sym, _, found := sc.lookup(pkg); found {
|
||||
if sym.typ == nil {
|
||||
t.incomplete = true
|
||||
break
|
||||
}
|
||||
switch sym.typ.cat {
|
||||
case binPkgT:
|
||||
pkg := interp.binPkg[sym.path]
|
||||
if v, ok := pkg[name]; ok {
|
||||
t.cat = valueT
|
||||
t.rtype = v.Type()
|
||||
if isBinType(v) {
|
||||
t.rtype = t.rtype.Elem()
|
||||
}
|
||||
} else {
|
||||
t.incomplete = true
|
||||
}
|
||||
|
||||
case srcPkgT:
|
||||
spkg := interp.scopes[pkg]
|
||||
if st, ok := spkg.sym[name]; ok {
|
||||
t = st.typ
|
||||
} else {
|
||||
t.incomplete = true
|
||||
// Resolve the left part of selector, then lookup the right part on it
|
||||
var lt *itype
|
||||
if lt, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lt.incomplete {
|
||||
t.incomplete = true
|
||||
break
|
||||
}
|
||||
name := n.child[1].ident
|
||||
switch lt.cat {
|
||||
case binPkgT:
|
||||
pkg := interp.binPkg[lt.path]
|
||||
if v, ok := pkg[name]; ok {
|
||||
t.cat = valueT
|
||||
t.rtype = v.Type()
|
||||
if isBinType(v) { // a bin type is encoded as a pointer on nil value
|
||||
t.rtype = t.rtype.Elem()
|
||||
}
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined selector %s.%s", lt.path, name)
|
||||
panic(err)
|
||||
}
|
||||
case srcPkgT:
|
||||
pkg := interp.srcPkg[lt.path]
|
||||
if s, ok := pkg[name]; ok {
|
||||
t = s.typ
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined selector %s.%s", lt.path, name)
|
||||
}
|
||||
default:
|
||||
if m, _ := lt.lookupMethod(name); m != nil {
|
||||
t, err = nodeType(interp, sc, m.child[2])
|
||||
} else if bm, _, _, ok := lt.lookupBinMethod(name); ok {
|
||||
t = &itype{cat: valueT, rtype: bm.Type}
|
||||
} else if ti := lt.lookupField(name); len(ti) > 0 {
|
||||
t = lt.fieldSeq(ti)
|
||||
} else if bs, _, ok := lt.lookupBinField(name); ok {
|
||||
t = &itype{cat: valueT, rtype: bs.Type}
|
||||
} else {
|
||||
err = lt.node.cfgErrorf("undefined selector %s", name)
|
||||
}
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined package: %s", pkg)
|
||||
}
|
||||
|
||||
case structType:
|
||||
t.cat = structT
|
||||
var incomplete, found bool
|
||||
var sym *symbol
|
||||
if sname := structName(n); sname != "" {
|
||||
if sym, _, found = sc.lookup(sname); found && sym.kind == typeSym {
|
||||
sym.recursive = true
|
||||
var incomplete bool
|
||||
if sname := typeName(n); sname != "" {
|
||||
if sym, _, found := sc.lookup(sname); found && sym.kind == typeSym {
|
||||
sym.typ = t
|
||||
}
|
||||
}
|
||||
for _, c := range n.child[0].child {
|
||||
@@ -446,11 +531,23 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
err = n.cfgErrorf("type definition not implemented: %s", n.kind)
|
||||
}
|
||||
|
||||
if err == nil && t.cat == nilT && !t.incomplete {
|
||||
err = n.cfgErrorf("use of untyped nil %s", t.name)
|
||||
}
|
||||
|
||||
return t, err
|
||||
}
|
||||
|
||||
func (interp *Interpreter) isBuiltinCall(n *node) bool {
|
||||
if n.kind != callExpr {
|
||||
return false
|
||||
}
|
||||
s := interp.universe.sym[n.child[0].ident]
|
||||
return s != nil && s.kind == bltnSym
|
||||
}
|
||||
|
||||
// struct name returns the name of a struct type
|
||||
func structName(n *node) string {
|
||||
func typeName(n *node) string {
|
||||
if n.anc.kind == typeSpec {
|
||||
return n.anc.child[0].ident
|
||||
}
|
||||
@@ -500,15 +597,23 @@ func init() {
|
||||
func (t *itype) finalize() (*itype, error) {
|
||||
var err cfgError
|
||||
if t.incomplete {
|
||||
sym, _, found := t.scope.lookup(t.name)
|
||||
if found && !sym.typ.incomplete {
|
||||
sym.typ.method = append(sym.typ.method, t.method...)
|
||||
return sym.typ, nil
|
||||
}
|
||||
m := t.method
|
||||
if t, err = nodeType(t.node.interp, t.scope, t.node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t.incomplete {
|
||||
return nil, t.node.cfgErrorf("incomplete type")
|
||||
return nil, t.node.cfgErrorf("incomplete type %s", t.name)
|
||||
}
|
||||
t.method = m
|
||||
t.node.typ = t
|
||||
if sym != nil {
|
||||
sym.typ = t
|
||||
}
|
||||
}
|
||||
return t, err
|
||||
}
|
||||
@@ -524,7 +629,7 @@ func (t *itype) id() string {
|
||||
case ptrT:
|
||||
res = "*" + t.val.id()
|
||||
default:
|
||||
res = t.pkgPath + "." + t.name
|
||||
res = t.path + "." + t.name
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -583,7 +688,7 @@ func (t *itype) lookupField(name string) []int {
|
||||
|
||||
for i, f := range t.field {
|
||||
switch f.typ.cat {
|
||||
case ptrT, structT:
|
||||
case ptrT, structT, interfaceT, aliasT:
|
||||
if index2 := f.typ.lookupField(name); len(index2) > 0 {
|
||||
return append([]int{i}, index2...)
|
||||
}
|
||||
@@ -594,12 +699,14 @@ func (t *itype) lookupField(name string) []int {
|
||||
}
|
||||
|
||||
// lookupBinField returns a structfield and a path to access an embedded binary field in a struct object
|
||||
func (t *itype) lookupBinField(name string) (reflect.StructField, []int, bool) {
|
||||
func (t *itype) lookupBinField(name string) (s reflect.StructField, index []int, ok bool) {
|
||||
if t.cat == ptrT {
|
||||
return t.val.lookupBinField(name)
|
||||
}
|
||||
var index []int
|
||||
s, ok := t.TypeOf().FieldByName(name)
|
||||
if !isStruct(t) {
|
||||
return
|
||||
}
|
||||
s, ok = t.TypeOf().FieldByName(name)
|
||||
if !ok {
|
||||
for i, f := range t.field {
|
||||
if f.embed {
|
||||
@@ -645,23 +752,28 @@ func (t *itype) lookupMethod(name string) (*node, []int) {
|
||||
}
|
||||
|
||||
// lookupBinMethod returns a method and a path to access a field in a struct object (the receiver)
|
||||
func (t *itype) lookupBinMethod(name string) (reflect.Method, []int, bool) {
|
||||
func (t *itype) lookupBinMethod(name string) (reflect.Method, []int, bool, bool) {
|
||||
var isPtr bool
|
||||
if t.cat == ptrT {
|
||||
return t.val.lookupBinMethod(name)
|
||||
}
|
||||
var index []int
|
||||
m, ok := t.TypeOf().MethodByName(name)
|
||||
if !ok {
|
||||
m, ok = reflect.PtrTo(t.TypeOf()).MethodByName(name)
|
||||
isPtr = ok
|
||||
}
|
||||
if !ok {
|
||||
for i, f := range t.field {
|
||||
if f.embed {
|
||||
if m2, index2, ok2 := f.typ.lookupBinMethod(name); ok2 {
|
||||
if m2, index2, isPtr2, ok2 := f.typ.lookupBinMethod(name); ok2 {
|
||||
index = append([]int{i}, index2...)
|
||||
return m2, index, ok2
|
||||
return m2, index, isPtr2, ok2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return m, index, ok
|
||||
return m, index, isPtr, ok
|
||||
}
|
||||
|
||||
func exportName(s string) string {
|
||||
@@ -671,50 +783,64 @@ func exportName(s string) string {
|
||||
return "X" + s
|
||||
}
|
||||
|
||||
// TypeOf returns the reflection type of dynamic interpreter type t.
|
||||
func (t *itype) TypeOf() reflect.Type {
|
||||
var interf = reflect.TypeOf(new(interface{})).Elem()
|
||||
|
||||
func (t *itype) refType(defined map[string]bool) reflect.Type {
|
||||
if t.rtype != nil {
|
||||
return t.rtype
|
||||
}
|
||||
|
||||
if t.incomplete {
|
||||
if t.incomplete || t.cat == nilT {
|
||||
var err error
|
||||
if t, err = t.finalize(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
if t.val != nil && defined[t.val.name] {
|
||||
t.val.rtype = interf
|
||||
}
|
||||
switch t.cat {
|
||||
case arrayT:
|
||||
case aliasT:
|
||||
t.rtype = t.val.refType(defined)
|
||||
case arrayT, variadicT:
|
||||
if t.size > 0 {
|
||||
t.rtype = reflect.ArrayOf(t.size, t.val.TypeOf())
|
||||
t.rtype = reflect.ArrayOf(t.size, t.val.refType(defined))
|
||||
} else {
|
||||
t.rtype = reflect.SliceOf(t.val.TypeOf())
|
||||
t.rtype = reflect.SliceOf(t.val.refType(defined))
|
||||
}
|
||||
case chanT:
|
||||
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.TypeOf())
|
||||
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.refType(defined))
|
||||
case errorT:
|
||||
t.rtype = reflect.TypeOf(new(error)).Elem()
|
||||
case funcT:
|
||||
in := make([]reflect.Type, len(t.arg))
|
||||
out := make([]reflect.Type, len(t.ret))
|
||||
for i, v := range t.arg {
|
||||
in[i] = v.TypeOf()
|
||||
if defined[v.name] {
|
||||
v.rtype = interf
|
||||
}
|
||||
in[i] = v.refType(defined)
|
||||
}
|
||||
for i, v := range t.ret {
|
||||
out[i] = v.TypeOf()
|
||||
if defined[v.name] {
|
||||
v.rtype = interf
|
||||
}
|
||||
out[i] = v.refType(defined)
|
||||
}
|
||||
t.rtype = reflect.FuncOf(in, out, false)
|
||||
case interfaceT:
|
||||
t.rtype = reflect.TypeOf(new(interface{})).Elem()
|
||||
t.rtype = interf
|
||||
case mapT:
|
||||
t.rtype = reflect.MapOf(t.key.TypeOf(), t.val.TypeOf())
|
||||
case ptrT:
|
||||
t.rtype = reflect.PtrTo(t.val.TypeOf())
|
||||
t.rtype = reflect.PtrTo(t.val.refType(defined))
|
||||
case structT:
|
||||
if t.name != "" {
|
||||
defined[t.name] = true
|
||||
}
|
||||
var fields []reflect.StructField
|
||||
for _, f := range t.field {
|
||||
field := reflect.StructField{Name: exportName(f.name), Type: f.typ.TypeOf(), Tag: reflect.StructTag(f.tag)}
|
||||
field := reflect.StructField{Name: exportName(f.name), Type: f.typ.refType(defined), Tag: reflect.StructTag(f.tag)}
|
||||
fields = append(fields, field)
|
||||
}
|
||||
t.rtype = reflect.StructOf(fields)
|
||||
@@ -726,38 +852,30 @@ func (t *itype) TypeOf() reflect.Type {
|
||||
return t.rtype
|
||||
}
|
||||
|
||||
func (t *itype) frameType() reflect.Type {
|
||||
var r reflect.Type
|
||||
// TypeOf returns the reflection type of dynamic interpreter type t.
|
||||
func (t *itype) TypeOf() reflect.Type {
|
||||
return t.refType(map[string]bool{})
|
||||
}
|
||||
|
||||
func (t *itype) frameType() (r reflect.Type) {
|
||||
var err error
|
||||
if t, err = t.finalize(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
switch t.cat {
|
||||
case arrayT:
|
||||
case aliasT:
|
||||
r = t.val.frameType()
|
||||
case arrayT, variadicT:
|
||||
if t.size > 0 {
|
||||
r = reflect.ArrayOf(t.size, t.val.frameType())
|
||||
} else {
|
||||
r = reflect.SliceOf(t.val.frameType())
|
||||
}
|
||||
//case ChanT:
|
||||
// r = reflect.ChanOf(reflect.BothDir, t.val.frameType())
|
||||
//case ErrorT:
|
||||
// r = reflect.TypeOf(new(error)).Elem()
|
||||
case funcT:
|
||||
r = reflect.TypeOf((*node)(nil))
|
||||
case interfaceT:
|
||||
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())
|
||||
//case StructT:
|
||||
// var fields []reflect.StructField
|
||||
// for _, f := range t.field {
|
||||
// field := reflect.StructField{Name: exportName(f.name), Type: f.typ.frameType()}
|
||||
// fields = append(fields, field)
|
||||
// }
|
||||
// r = reflect.StructOf(fields)
|
||||
default:
|
||||
// if z, _ := t.zero(); z.IsValid() {
|
||||
// r = z.Type()
|
||||
// }
|
||||
r = t.TypeOf()
|
||||
}
|
||||
return r
|
||||
@@ -781,15 +899,21 @@ func defRecvType(n *node) *itype {
|
||||
return nil
|
||||
}
|
||||
|
||||
func isShiftOperand(n *node) bool {
|
||||
switch n.anc.action {
|
||||
func isShiftNode(n *node) bool {
|
||||
switch n.action {
|
||||
case aShl, aShr, aShlAssign, aShrAssign:
|
||||
return n.anc.lastChild() == n
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isInterface(t *itype) bool { return t.cat == interfaceT || t.TypeOf().Kind() == reflect.Interface }
|
||||
func isInterfaceSrc(t *itype) bool {
|
||||
return t.cat == interfaceT || (t.cat == aliasT && isInterfaceSrc(t.val))
|
||||
}
|
||||
|
||||
func isInterface(t *itype) bool {
|
||||
return isInterfaceSrc(t) || t.TypeOf().Kind() == reflect.Interface
|
||||
}
|
||||
|
||||
func isStruct(t *itype) bool { return t.TypeOf().Kind() == reflect.Struct }
|
||||
|
||||
|
||||
@@ -107,6 +107,17 @@ func genValue(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueArray(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
// dereference array pointer, to support array operations on array pointer
|
||||
if n.typ.TypeOf().Kind() == reflect.Ptr {
|
||||
return func(f *frame) reflect.Value {
|
||||
return value(f).Elem()
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func genValueInterfacePtr(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
it := reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports archive/tar'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports archive/tar'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"reflect"
|
||||
@@ -43,8 +43,5 @@ func init() {
|
||||
"Header": reflect.ValueOf((*tar.Header)(nil)),
|
||||
"Reader": reflect.ValueOf((*tar.Reader)(nil)),
|
||||
"Writer": reflect.ValueOf((*tar.Writer)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports archive/zip'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports archive/zip'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"reflect"
|
||||
@@ -32,8 +32,5 @@ func init() {
|
||||
"ReadCloser": reflect.ValueOf((*zip.ReadCloser)(nil)),
|
||||
"Reader": reflect.ValueOf((*zip.Reader)(nil)),
|
||||
"Writer": reflect.ValueOf((*zip.Writer)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports bufio'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports bufio'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"reflect"
|
||||
@@ -38,8 +38,5 @@ func init() {
|
||||
"Scanner": reflect.ValueOf((*bufio.Scanner)(nil)),
|
||||
"SplitFunc": reflect.ValueOf((*bufio.SplitFunc)(nil)),
|
||||
"Writer": reflect.ValueOf((*bufio.Writer)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports bytes'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports bytes'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
@@ -66,8 +66,5 @@ func init() {
|
||||
// type definitions
|
||||
"Buffer": reflect.ValueOf((*bytes.Buffer)(nil)),
|
||||
"Reader": reflect.ValueOf((*bytes.Reader)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports compress/bzip2'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports compress/bzip2'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"compress/bzip2"
|
||||
"reflect"
|
||||
@@ -16,8 +16,5 @@ func init() {
|
||||
|
||||
// type definitions
|
||||
"StructuralError": reflect.ValueOf((*bzip2.StructuralError)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports compress/flate'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports compress/flate'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"io"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports compress/gzip'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports compress/gzip'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"reflect"
|
||||
@@ -27,8 +27,5 @@ func init() {
|
||||
"Header": reflect.ValueOf((*gzip.Header)(nil)),
|
||||
"Reader": reflect.ValueOf((*gzip.Reader)(nil)),
|
||||
"Writer": reflect.ValueOf((*gzip.Writer)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports compress/lzw'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports compress/lzw'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"compress/lzw"
|
||||
"reflect"
|
||||
@@ -19,8 +19,5 @@ func init() {
|
||||
|
||||
// type definitions
|
||||
"Order": reflect.ValueOf((*lzw.Order)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports compress/zlib'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports compress/zlib'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"compress/zlib"
|
||||
"io"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports container/heap'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports container/heap'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"reflect"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports container/list'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports container/list'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"reflect"
|
||||
@@ -17,8 +17,5 @@ func init() {
|
||||
// type definitions
|
||||
"Element": reflect.ValueOf((*list.Element)(nil)),
|
||||
"List": reflect.ValueOf((*list.List)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports container/ring'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports container/ring'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"container/ring"
|
||||
"reflect"
|
||||
@@ -16,8 +16,5 @@ func init() {
|
||||
|
||||
// type definitions
|
||||
"Ring": reflect.ValueOf((*ring.Ring)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports context'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports context'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by 'goexports crypto'. DO NOT EDIT.
|
||||
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package stdlib
|
||||
|
||||
// Code generated by 'goexports crypto'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"io"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user