compiler: make ForEachFile accept a package

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2020-07-28 18:40:41 +03:00
parent ac040a6f22
commit b8d7e93459
3 changed files with 37 additions and 48 deletions

View file

@ -8,7 +8,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"golang.org/x/tools/go/loader"
) )
var ( var (
@ -30,7 +29,7 @@ func (c *codegen) newGlobal(name string) {
// and returns number of variables initialized. // and returns number of variables initialized.
func (c *codegen) traverseGlobals() int { func (c *codegen) traverseGlobals() int {
var n int var n int
c.ForEachFile(func(f *ast.File) { c.ForEachFile(func(f *ast.File, _ *types.Package) {
n += countGlobals(f) n += countGlobals(f)
}) })
if n != 0 { if n != 0 {
@ -95,31 +94,29 @@ func lastStmtIsReturn(decl *ast.FuncDecl) (b bool) {
return false return false
} }
func analyzeFuncUsage(mainPkg *loader.PackageInfo, pkgs map[*types.Package]*loader.PackageInfo) funcUsage { func (c *codegen) analyzeFuncUsage() funcUsage {
usage := funcUsage{} usage := funcUsage{}
for _, pkg := range pkgs { c.ForEachFile(func(f *ast.File, pkg *types.Package) {
isMain := pkg == mainPkg isMain := pkg == c.mainPkg.Pkg
for _, f := range pkg.Files { ast.Inspect(f, func(node ast.Node) bool {
ast.Inspect(f, func(node ast.Node) bool { switch n := node.(type) {
switch n := node.(type) { case *ast.CallExpr:
case *ast.CallExpr: switch t := n.Fun.(type) {
switch t := n.Fun.(type) { case *ast.Ident:
case *ast.Ident: usage[t.Name] = true
usage[t.Name] = true case *ast.SelectorExpr:
case *ast.SelectorExpr: usage[t.Sel.Name] = true
usage[t.Sel.Name] = true
}
case *ast.FuncDecl:
// exported functions are always assumed to be used
if isMain && n.Name.IsExported() {
usage[n.Name.Name] = true
}
} }
return true case *ast.FuncDecl:
}) // exported functions are always assumed to be used
} if isMain && n.Name.IsExported() {
} usage[n.Name.Name] = true
}
}
return true
})
})
return usage return usage
} }

View file

@ -252,7 +252,7 @@ func (c *codegen) emitDefault(t types.Type) {
// convertGlobals traverses the AST and only converts global declarations. // convertGlobals traverses the AST and only converts global declarations.
// If we call this in convertFuncDecl then it will load all global variables // If we call this in convertFuncDecl then it will load all global variables
// into the scope of the function. // into the scope of the function.
func (c *codegen) convertGlobals(f *ast.File) { func (c *codegen) convertGlobals(f *ast.File, _ *types.Package) {
ast.Inspect(f, func(node ast.Node) bool { ast.Inspect(f, func(node ast.Node) bool {
switch n := node.(type) { switch n := node.(type) {
case *ast.FuncDecl: case *ast.FuncDecl:
@ -1415,16 +1415,12 @@ func (c *codegen) newLambda(u uint16, lit *ast.FuncLit) {
} }
func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error { func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error {
funUsage := analyzeFuncUsage(pkg, info.program.AllPackages) c.mainPkg = pkg
funUsage := c.analyzeFuncUsage()
// Bring all imported functions into scope. // Bring all imported functions into scope.
for _, pkg := range info.program.AllPackages { c.ForEachFile(c.resolveFuncDecls)
for _, f := range pkg.Files {
c.resolveFuncDecls(f, pkg.Pkg)
}
}
c.mainPkg = pkg
n := c.traverseGlobals() n := c.traverseGlobals()
if n > 0 { if n > 0 {
emit.Opcode(c.prog.BinWriter, opcode.RET) emit.Opcode(c.prog.BinWriter, opcode.RET)
@ -1439,23 +1435,18 @@ func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error {
sort.Slice(keys, func(i, j int) bool { return keys[i].Path() < keys[j].Path() }) sort.Slice(keys, func(i, j int) bool { return keys[i].Path() < keys[j].Path() })
// Generate the code for the program. // Generate the code for the program.
for _, k := range keys { c.ForEachFile(func(f *ast.File, pkg *types.Package) {
pkg := info.program.AllPackages[k] for _, decl := range f.Decls {
c.typeInfo = &pkg.Info switch n := decl.(type) {
case *ast.FuncDecl:
for _, f := range pkg.Files { // Don't convert the function if it's not used. This will save a lot
for _, decl := range f.Decls { // of bytecode space.
switch n := decl.(type) { if funUsage.funcUsed(n.Name.Name) {
case *ast.FuncDecl: c.convertFuncDecl(f, n, pkg)
// Don't convert the function if it's not used. This will save a lot
// of bytecode space.
if funUsage.funcUsed(n.Name.Name) {
c.convertFuncDecl(f, n, k)
}
} }
} }
} }
} })
return c.prog.Err return c.prog.Err
} }

View file

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/parser" "go/parser"
"go/types"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
@ -44,11 +45,11 @@ type buildInfo struct {
} }
// ForEachFile executes fn on each file used in current program. // ForEachFile executes fn on each file used in current program.
func (c *codegen) ForEachFile(fn func(*ast.File)) { func (c *codegen) ForEachFile(fn func(*ast.File, *types.Package)) {
for _, pkg := range c.buildInfo.program.AllPackages { for _, pkg := range c.buildInfo.program.AllPackages {
c.typeInfo = &pkg.Info c.typeInfo = &pkg.Info
for _, f := range pkg.Files { for _, f := range pkg.Files {
fn(f) fn(f, pkg.Pkg)
} }
} }
} }