mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2025-05-07 23:10:32 +00:00
compiler: count local variables on the go
Create local variables as they are needed and remove `INITSLOT` if there were no locals. This helps to eliminate a whole class of bugs when calculated and real amount mismatched.
This commit is contained in:
parent
bfa2bafb04
commit
b693d54282
9 changed files with 181 additions and 230 deletions
|
@ -2,7 +2,6 @@ package compiler
|
|||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
|
@ -104,113 +103,6 @@ func (c *funcScope) analyzeVoidCalls(node ast.Node) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (c *codegen) countLocalsCall(n ast.Expr, pkg *types.Package) int {
|
||||
ce, ok := n.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
|
||||
var size int
|
||||
var name string
|
||||
switch fun := ce.Fun.(type) {
|
||||
case *ast.Ident:
|
||||
var pkgName string
|
||||
if pkg != nil {
|
||||
pkgName = pkg.Path()
|
||||
}
|
||||
name = c.getIdentName(pkgName, fun.Name)
|
||||
case *ast.SelectorExpr:
|
||||
name, _ = c.getFuncNameFromSelector(fun)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
if inner, ok := c.funcs[name]; ok && canInline(name) {
|
||||
sig, ok := c.typeOf(ce.Fun).(*types.Signature)
|
||||
if !ok {
|
||||
info := c.buildInfo.program.Package(pkg.Path())
|
||||
sig = info.Types[ce.Fun].Type.(*types.Signature)
|
||||
}
|
||||
for i := range ce.Args {
|
||||
switch ce.Args[i].(type) {
|
||||
case *ast.Ident:
|
||||
case *ast.BasicLit:
|
||||
default:
|
||||
size++
|
||||
}
|
||||
}
|
||||
// Variadic with direct var args.
|
||||
if sig.Variadic() && !ce.Ellipsis.IsValid() {
|
||||
size++
|
||||
}
|
||||
innerSz, _ := c.countLocalsInline(inner.decl, inner.pkg, inner)
|
||||
size += innerSz
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (c *codegen) countLocals(decl *ast.FuncDecl) (int, bool) {
|
||||
return c.countLocalsInline(decl, nil, nil)
|
||||
}
|
||||
|
||||
func (c *codegen) countLocalsInline(decl *ast.FuncDecl, pkg *types.Package, f *funcScope) (int, bool) {
|
||||
oldMap := c.importMap
|
||||
if pkg != nil {
|
||||
c.fillImportMap(f.file, pkg)
|
||||
}
|
||||
|
||||
size := 0
|
||||
hasDefer := false
|
||||
ast.Inspect(decl, func(n ast.Node) bool {
|
||||
switch n := n.(type) {
|
||||
case *ast.CallExpr:
|
||||
size += c.countLocalsCall(n, pkg)
|
||||
case *ast.FuncType:
|
||||
num := n.Results.NumFields()
|
||||
if num != 0 && len(n.Results.List[0].Names) != 0 {
|
||||
size += num
|
||||
}
|
||||
case *ast.AssignStmt:
|
||||
if n.Tok == token.DEFINE {
|
||||
size += len(n.Lhs)
|
||||
}
|
||||
case *ast.DeferStmt:
|
||||
hasDefer = true
|
||||
return false
|
||||
case *ast.ReturnStmt:
|
||||
if pkg == nil {
|
||||
size++
|
||||
}
|
||||
case *ast.IfStmt:
|
||||
size++
|
||||
// This handles the inline GenDecl like "var x = 2"
|
||||
case *ast.ValueSpec:
|
||||
size += len(n.Names)
|
||||
case *ast.RangeStmt:
|
||||
if n.Tok == token.DEFINE {
|
||||
if n.Key != nil {
|
||||
size++
|
||||
}
|
||||
if n.Value != nil {
|
||||
size++
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if pkg != nil {
|
||||
c.importMap = oldMap
|
||||
}
|
||||
return size, hasDefer
|
||||
}
|
||||
|
||||
func (c *codegen) countLocalsWithDefer(f *funcScope) int {
|
||||
size, hasDefer := c.countLocals(f.decl)
|
||||
if hasDefer {
|
||||
size++
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (c *funcScope) countArgs() int {
|
||||
n := c.decl.Type.Params.NumFields()
|
||||
if c.decl.Recv != nil {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue