From b6629fb6bdf3cf619469cc8d10b35e3921af84b5 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 29 Jan 2020 10:08:24 +0300 Subject: [PATCH] compiler: refactor argument handling for builtins It is more convenient to have all unusual logic in one place. --- pkg/compiler/analysis.go | 10 -------- pkg/compiler/codegen.go | 53 ++++++++++++++++++++++++---------------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index 974ba34d2..ad46b8b93 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -187,16 +187,6 @@ func isBuiltin(expr ast.Expr) bool { return false } -func isAppCall(expr ast.Expr) bool { - t, ok := expr.(*ast.SelectorExpr) - return ok && t.Sel.Name == "AppCall" -} - -func isFromAddress(expr ast.Expr) bool { - t, ok := expr.(*ast.SelectorExpr) - return ok && t.Sel.Name == "FromAddress" -} - func isByteArray(lit *ast.CompositeLit, tInfo *types.Info) bool { if len(lit.Elts) == 0 { return false diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 8a9650d43..47711bd86 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -523,17 +523,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { return nil } - args := n.Args - isAppCall := isAppCall(n.Fun) - isFromAddress := isFromAddress(n.Fun) - // There are 2 special cases: - // 1. When using APPCALL, script hash is a part of the instruction so - // script hash should be emitted after APPCALL. - // 2. With FromAddress, parameter conversion is happening at compile-time - // so there is no need to push parameters on stack and perform an actual call - if isAppCall || isFromAddress { - args = n.Args[1:] - } + args := transformArgs(n.Fun, n.Args) // Handle the arguments for _, arg := range args { @@ -560,16 +550,6 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { // Use the ident to check, builtins are not in func scopes. // We can be sure builtins are of type *ast.Ident. c.convertBuiltin(n) - - if isAppCall { - buf := c.getByteArray(n.Args[0]) - if len(buf) != 20 { - c.prog.Err = errors.New("invalid script hash") - return nil - } - - c.prog.WriteBytes(buf) - } case isSyscall(f): c.convertSyscall(f.selector.Name, f.name) default: @@ -776,6 +756,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { emitOpcode(c.prog.BinWriter, opcode.DROP) emitOpcode(c.prog.BinWriter, opcode.THROW) } else if isStringType(c.typeInfo.Types[arg].Type) { + ast.Walk(c, arg) emitSyscall(c.prog.BinWriter, "Neo.Runtime.Log") emitOpcode(c.prog.BinWriter, opcode.THROW) } else { @@ -793,6 +774,12 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { emitOpcode(c.prog.BinWriter, opcode.VERIFY) case "AppCall": emitOpcode(c.prog.BinWriter, opcode.APPCALL) + buf := c.getByteArray(expr.Args[0]) + if len(buf) != 20 { + c.prog.Err = errors.New("invalid script hash") + } + + c.prog.WriteBytes(buf) case "Equals": emitOpcode(c.prog.BinWriter, opcode.EQUAL) case "FromAddress": @@ -811,6 +798,30 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { } } +// transformArgs returns a list of function arguments +// which should be put on stack. +// There are special cases for builtins: +// 1. When using AppCall, script hash is a part of the instruction so +// it should be emitted after APPCALL. +// 2. With FromAddress, parameter conversion is happening at compile-time +// so there is no need to push parameters on stack and perform an actual call +// 3. With panic, generated code depends on if argument was nil or a string so +// it should be handled accordingly. +func transformArgs(fun ast.Expr, args []ast.Expr) []ast.Expr { + switch f := fun.(type) { + case *ast.SelectorExpr: + if f.Sel.Name == "AppCall" || f.Sel.Name == "FromAddress" { + return args[1:] + } + case *ast.Ident: + if f.Name == "panic" { + return args[1:] + } + } + + return args +} + func (c *codegen) convertByteArray(lit *ast.CompositeLit) { buf := make([]byte, len(lit.Elts)) for i := 0; i < len(lit.Elts); i++ {