From 5bdee683e6a613c5a6534953d974236d414a5a2c Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 2 Apr 2020 15:38:53 +0300 Subject: [PATCH] cli,compiler: support emitting debug info in a file --- cli/smartcontract/smart_contract.go | 6 +++++ pkg/compiler/codegen.go | 8 +++--- pkg/compiler/compiler.go | 39 ++++++++++++++++++++++------- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 5de59e69e..7891807e6 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -92,6 +92,10 @@ func NewCommands() []cli.Command { Name: "debug, d", Usage: "Debug mode will print out additional information after a compiling", }, + cli.StringFlag{ + Name: "emitdebug", + Usage: "Emit debug info in a separate file", + }, }, }, { @@ -348,6 +352,8 @@ func contractCompile(ctx *cli.Context) error { o := &compiler.Options{ Outfile: ctx.String("out"), Debug: ctx.Bool("debug"), + + DebugInfo: ctx.String("emitdebug"), } result, err := compiler.CompileAndSave(src, o) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 95752627e..612ae60de 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -1313,19 +1313,19 @@ func newCodegen(info *buildInfo, pkg *loader.PackageInfo) *codegen { } // CodeGen compiles the program to bytecode. -func CodeGen(info *buildInfo) ([]byte, error) { +func CodeGen(info *buildInfo) ([]byte, *DebugInfo, error) { pkg := info.program.Package(info.initialPackage) c := newCodegen(info, pkg) if err := c.compile(info, pkg); err != nil { - return nil, err + return nil, nil, err } buf := c.prog.Bytes() if err := c.writeJumps(buf); err != nil { - return nil, err + return nil, nil, err } - return buf, nil + return buf, c.emitDebugInfo(), nil } func (c *codegen) resolveFuncDecls(f *ast.File) { diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 40cae382d..cebfd3e1b 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -2,11 +2,13 @@ package compiler import ( "bytes" + "encoding/json" "fmt" "go/parser" "io" "io/ioutil" "os" + "path/filepath" "strings" "golang.org/x/tools/go/loader" @@ -22,6 +24,9 @@ type Options struct { // The name of the output file. Outfile string + // The name of the output for debug info. + DebugInfo string + // Debug outputs a hex encoded string of the generated bytecode. Debug bool } @@ -52,12 +57,7 @@ func getBuildInfo(src interface{}) (*buildInfo, error) { // Compile compiles a Go program into bytecode that can run on the NEO virtual machine. func Compile(r io.Reader) ([]byte, error) { - ctx, err := getBuildInfo(r) - if err != nil { - return nil, err - } - - buf, err := CodeGen(ctx) + buf, _, err := CompileWithDebugInfo(r) if err != nil { return nil, err } @@ -65,6 +65,15 @@ func Compile(r io.Reader) ([]byte, error) { return buf, nil } +// CompileWithDebugInfo compiles a Go program into bytecode and emits debug info. +func CompileWithDebugInfo(r io.Reader) ([]byte, *DebugInfo, error) { + ctx, err := getBuildInfo(r) + if err != nil { + return nil, nil, err + } + return CodeGen(ctx) +} + // CompileAndSave will compile and save the file to disk. func CompileAndSave(src string, o *Options) ([]byte, error) { if !strings.HasSuffix(src, ".go") { @@ -81,11 +90,23 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { if err != nil { return nil, err } - b, err = Compile(bytes.NewReader(b)) + b, di, err := CompileWithDebugInfo(bytes.NewReader(b)) if err != nil { return nil, fmt.Errorf("error while trying to compile smart contract file: %v", err) } - out := fmt.Sprintf("%s.%s", o.Outfile, o.Ext) - return b, ioutil.WriteFile(out, b, os.ModePerm) + err = ioutil.WriteFile(out, b, os.ModePerm) + if o.DebugInfo == "" { + return b, err + } + p, err := filepath.Abs(src) + if err != nil { + return b, err + } + di.Documents = append(di.Documents, p) + data, err := json.Marshal(di) + if err != nil { + return b, err + } + return b, ioutil.WriteFile(o.DebugInfo, data, os.ModePerm) }