From 1d6d7206e993599176553cabfcdc7fbbd4a0804c Mon Sep 17 00:00:00 2001 From: Evgeniy Stratonikov Date: Tue, 25 May 2021 16:17:40 +0300 Subject: [PATCH] compiler: inline expressions with type conversions, fix #1879 Don't count `string(data)` or `[]byte(data)` as function calls. --- pkg/compiler/codegen.go | 17 +++++++++++------ pkg/compiler/inline.go | 19 ++++++++++++++++--- pkg/compiler/inline_test.go | 18 ++++++++++++++++++ pkg/compiler/testdata/foo/foo.go | 3 +++ 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 5abf00ec0..b7dd4611b 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -869,12 +869,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { switch fun := n.Fun.(type) { case *ast.Ident: - var pkgName string - if len(c.pkgInfoInline) != 0 { - pkgName = c.pkgInfoInline[len(c.pkgInfoInline)-1].Pkg.Path() - } - f, ok = c.funcs[c.getIdentName(pkgName, fun.Name)] - + f, ok = c.getFuncFromIdent(fun) isBuiltin = isGoBuiltin(fun.Name) if !ok && !isBuiltin { name = fun.Name @@ -1956,6 +1951,16 @@ func (c *codegen) newFunc(decl *ast.FuncDecl) *funcScope { return f } +func (c *codegen) getFuncFromIdent(fun *ast.Ident) (*funcScope, bool) { + var pkgName string + if len(c.pkgInfoInline) != 0 { + pkgName = c.pkgInfoInline[len(c.pkgInfoInline)-1].Pkg.Path() + } + + f, ok := c.funcs[c.getIdentName(pkgName, fun.Name)] + return f, ok +} + // getFuncNameFromSelector returns fully-qualified function name from the selector expression. // Second return value is true iff this was a method call, not foreign package call. func (c *codegen) getFuncNameFromSelector(e *ast.SelectorExpr) (string, bool) { diff --git a/pkg/compiler/inline.go b/pkg/compiler/inline.go index e339dad2d..819b44af1 100644 --- a/pkg/compiler/inline.go +++ b/pkg/compiler/inline.go @@ -140,9 +140,22 @@ func (c *codegen) processNotify(f *funcScope, args []ast.Expr) { func (c *codegen) hasCalls(expr ast.Expr) bool { var has bool ast.Inspect(expr, func(n ast.Node) bool { - _, ok := n.(*ast.CallExpr) - if ok { - has = true + ce, ok := n.(*ast.CallExpr) + if !has && ok { + isFunc := true + fun, ok := ce.Fun.(*ast.Ident) + if ok { + _, isFunc = c.getFuncFromIdent(fun) + } else { + var sel *ast.SelectorExpr + sel, ok = ce.Fun.(*ast.SelectorExpr) + if ok { + name, _ := c.getFuncNameFromSelector(sel) + _, isFunc = c.funcs[name] + fun = sel.Sel + } + } + has = isFunc || fun.Obj != nil && (fun.Obj.Kind == ast.Var || fun.Obj.Kind == ast.Fun) } return !has }) diff --git a/pkg/compiler/inline_test.go b/pkg/compiler/inline_test.go index 87ab89197..2b67d46c4 100644 --- a/pkg/compiler/inline_test.go +++ b/pkg/compiler/inline_test.go @@ -48,6 +48,8 @@ func checkCallCount(t *testing.T, src string, expectedCall, expectedInitSlot, ex func TestInline(t *testing.T) { srcTmpl := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/inline" + import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/foo" + var _ = foo.Dummy type pair struct { a, b int } type triple struct { a int @@ -157,6 +159,22 @@ func TestInline(t *testing.T) { checkCallCount(t, src, 0, 1, 2) eval(t, src, big.NewInt(7)) }) + t.Run("foreign package call", func(t *testing.T) { + src := fmt.Sprintf(srcTmpl, `return inline.Sum(foo.Bar(), foo.Dummy+1)`) + checkCallCount(t, src, 1, 1, 1) + eval(t, src, big.NewInt(3)) + }) +} + +func TestIssue1879(t *testing.T) { + src := `package foo + import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + func Main() int { + data := "main is called" + runtime.Log("log " + string(data)) + return 42 + }` + checkCallCount(t, src, 0, 1, 1) } func TestInlineInLoop(t *testing.T) { diff --git a/pkg/compiler/testdata/foo/foo.go b/pkg/compiler/testdata/foo/foo.go index ca98105ac..ee4aef007 100644 --- a/pkg/compiler/testdata/foo/foo.go +++ b/pkg/compiler/testdata/foo/foo.go @@ -5,6 +5,9 @@ func NewBar() int { return 10 } +// Dummy is dummy constant. +var Dummy = 1 + // Foo is a type. type Foo struct{}