github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/compiler/debug.go (about)

     1  package compiler
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"go/ast"
     8  	"go/types"
     9  	"sort"
    10  	"strconv"
    11  	"strings"
    12  	"unicode"
    13  	"unicode/utf8"
    14  
    15  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
    16  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    17  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/binding"
    18  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
    19  	"github.com/nspcc-dev/neo-go/pkg/util"
    20  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    21  )
    22  
    23  // DebugInfo represents smart-contract debug information.
    24  type DebugInfo struct {
    25  	MainPkg   string            `json:"-"`
    26  	Hash      util.Uint160      `json:"hash"`
    27  	Documents []string          `json:"documents"`
    28  	Methods   []MethodDebugInfo `json:"methods"`
    29  	// NamedTypes are exported structured types that have some name (even
    30  	// if the original structure doesn't) and a number of internal fields.
    31  	NamedTypes map[string]binding.ExtendedType `json:"-"`
    32  	// Events are the events that contract is allowed to emit and that have to
    33  	// be presented in the resulting contract manifest and debug info file.
    34  	Events []EventDebugInfo `json:"events"`
    35  	// EmittedEvents contains events occurring in code, i.e. events emitted
    36  	// via runtime.Notify(...) call in the contract code if they have constant
    37  	// names and doesn't have ellipsis arguments. EmittedEvents are not related
    38  	// to the debug info and are aimed to serve bindings generation.
    39  	EmittedEvents map[string][]EmittedEventInfo `json:"-"`
    40  	// InvokedContracts contains foreign contract invocations.
    41  	InvokedContracts map[util.Uint160][]string `json:"-"`
    42  	// StaticVariables contains a list of static variable names and types.
    43  	StaticVariables []string `json:"static-variables"`
    44  }
    45  
    46  // MethodDebugInfo represents smart-contract's method debug information.
    47  type MethodDebugInfo struct {
    48  	// ID is the actual name of the method.
    49  	ID string `json:"id"`
    50  	// Name is the name of the method with the first letter in a lowercase
    51  	// together with the namespace it belongs to. We need to keep the first letter
    52  	// lowercased to match manifest standards.
    53  	Name DebugMethodName `json:"name"`
    54  	// IsExported defines whether the method is exported.
    55  	IsExported bool `json:"-"`
    56  	// IsFunction defines whether the method has no receiver.
    57  	IsFunction bool `json:"-"`
    58  	// Range is the range of smart-contract's opcodes corresponding to the method.
    59  	Range DebugRange `json:"range"`
    60  	// Parameters is a list of the method's parameters.
    61  	Parameters []DebugParam `json:"params"`
    62  	// ReturnType is the method's return type.
    63  	ReturnType string `json:"return"`
    64  	// ReturnTypeReal is the method's return type as specified in Go code.
    65  	ReturnTypeReal binding.Override `json:"-"`
    66  	// ReturnTypeExtended is the method's return type with additional data.
    67  	ReturnTypeExtended *binding.ExtendedType `json:"-"`
    68  	// ReturnTypeSC is a return type to use in manifest.
    69  	ReturnTypeSC smartcontract.ParamType `json:"-"`
    70  	Variables    []string                `json:"variables"`
    71  	// SeqPoints is a map between source lines and byte-code instruction offsets.
    72  	SeqPoints []DebugSeqPoint `json:"sequence-points"`
    73  }
    74  
    75  // DebugMethodName is a combination of a namespace and name.
    76  type DebugMethodName struct {
    77  	Namespace string
    78  	Name      string
    79  }
    80  
    81  // EventDebugInfo represents smart-contract's event debug information.
    82  type EventDebugInfo struct {
    83  	ID string `json:"id"`
    84  	// Name is a human-readable event name in a format "{namespace},{name}".
    85  	Name       string       `json:"name"`
    86  	Parameters []DebugParam `json:"params"`
    87  }
    88  
    89  // DebugSeqPoint represents break-point for debugger.
    90  type DebugSeqPoint struct {
    91  	// Opcode is an opcode's address.
    92  	Opcode int
    93  	// Document is an index of file where sequence point occurs.
    94  	Document int
    95  	// StartLine is the first line of the break-pointed statement.
    96  	StartLine int
    97  	// StartCol is the first column of the break-pointed statement.
    98  	StartCol int
    99  	// EndLine is the last line of the break-pointed statement.
   100  	EndLine int
   101  	// EndCol is the last column of the break-pointed statement.
   102  	EndCol int
   103  }
   104  
   105  // DebugRange represents the method's section in bytecode.
   106  type DebugRange struct {
   107  	Start uint16
   108  	End   uint16
   109  }
   110  
   111  // DebugParam represents the variable's name and type.
   112  type DebugParam struct {
   113  	Name         string                  `json:"name"`
   114  	Type         string                  `json:"type"`
   115  	RealType     binding.Override        `json:"-"`
   116  	ExtendedType *binding.ExtendedType   `json:"-"`
   117  	TypeSC       smartcontract.ParamType `json:"-"`
   118  }
   119  
   120  // EmittedEventInfo describes information about single emitted event got from
   121  // the contract code. It has the map of extended types used as the parameters to
   122  // runtime.Notify(...) call (if any) and the parameters info itself.
   123  type EmittedEventInfo struct {
   124  	ExtTypes map[string]binding.ExtendedType
   125  	Params   []DebugParam
   126  }
   127  
   128  func (c *codegen) saveSequencePoint(n ast.Node) {
   129  	name := "init"
   130  	if c.scope != nil {
   131  		name = c.scope.name
   132  	}
   133  
   134  	fset := c.buildInfo.config.Fset
   135  	start := fset.Position(n.Pos())
   136  	end := fset.Position(n.End())
   137  	c.sequencePoints[name] = append(c.sequencePoints[name], DebugSeqPoint{
   138  		Opcode:    c.prog.Len(),
   139  		Document:  c.docIndex[start.Filename],
   140  		StartLine: start.Line,
   141  		StartCol:  start.Column,
   142  		EndLine:   end.Line,
   143  		EndCol:    end.Column,
   144  	})
   145  }
   146  
   147  func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo {
   148  	d := &DebugInfo{
   149  		Hash:            hash.Hash160(contract),
   150  		MainPkg:         c.mainPkg.Name,
   151  		Events:          []EventDebugInfo{},
   152  		Documents:       c.documents,
   153  		StaticVariables: c.staticVariables,
   154  	}
   155  	if c.initEndOffset > 0 {
   156  		d.Methods = append(d.Methods, MethodDebugInfo{
   157  			ID: manifest.MethodInit,
   158  			Name: DebugMethodName{
   159  				Name:      manifest.MethodInit,
   160  				Namespace: c.mainPkg.Name,
   161  			},
   162  			IsExported: true,
   163  			IsFunction: true,
   164  			Range: DebugRange{
   165  				Start: 0,
   166  				End:   uint16(c.initEndOffset),
   167  			},
   168  			ReturnType:   "Void",
   169  			ReturnTypeSC: smartcontract.VoidType,
   170  			SeqPoints:    c.sequencePoints["init"],
   171  			Variables:    c.initVariables,
   172  		})
   173  	}
   174  	if c.deployEndOffset >= 0 {
   175  		d.Methods = append(d.Methods, MethodDebugInfo{
   176  			ID: manifest.MethodDeploy,
   177  			Name: DebugMethodName{
   178  				Name:      manifest.MethodDeploy,
   179  				Namespace: c.mainPkg.Name,
   180  			},
   181  			IsExported: true,
   182  			IsFunction: true,
   183  			Range: DebugRange{
   184  				Start: uint16(c.initEndOffset + 1),
   185  				End:   uint16(c.deployEndOffset),
   186  			},
   187  			Parameters: []DebugParam{
   188  				{
   189  					Name:   "data",
   190  					Type:   "Any",
   191  					TypeSC: smartcontract.AnyType,
   192  				},
   193  				{
   194  					Name:   "isUpdate",
   195  					Type:   "Boolean",
   196  					TypeSC: smartcontract.BoolType,
   197  				},
   198  			},
   199  			ReturnType:   "Void",
   200  			ReturnTypeSC: smartcontract.VoidType,
   201  			SeqPoints:    c.sequencePoints[manifest.MethodDeploy],
   202  			Variables:    c.deployVariables,
   203  		})
   204  	}
   205  
   206  	var fnames = make([]string, 0, len(c.funcs))
   207  	for name, scope := range c.funcs {
   208  		if scope.rng.Start == scope.rng.End {
   209  			continue
   210  		}
   211  		fnames = append(fnames, name)
   212  	}
   213  	sort.Strings(fnames)
   214  	d.NamedTypes = make(map[string]binding.ExtendedType)
   215  	for _, name := range fnames {
   216  		m := c.methodInfoFromScope(name, c.funcs[name], d.NamedTypes)
   217  		d.Methods = append(d.Methods, *m)
   218  	}
   219  	d.EmittedEvents = c.emittedEvents
   220  	d.InvokedContracts = c.invokedContracts
   221  	return d
   222  }
   223  
   224  func (c *codegen) registerDebugVariable(name string, expr ast.Expr) {
   225  	_, vt, _, _ := c.scAndVMTypeFromExpr(expr, nil)
   226  	if c.scope == nil {
   227  		c.staticVariables = append(c.staticVariables, name+","+vt.String())
   228  		return
   229  	}
   230  	c.scope.variables = append(c.scope.variables, name+","+vt.String())
   231  }
   232  
   233  func (c *codegen) methodInfoFromScope(name string, scope *funcScope, exts map[string]binding.ExtendedType) *MethodDebugInfo {
   234  	ps := scope.decl.Type.Params
   235  	params := make([]DebugParam, 0, ps.NumFields())
   236  	for i := range ps.List {
   237  		for j := range ps.List[i].Names {
   238  			st, vt, rt, et := c.scAndVMTypeFromExpr(ps.List[i].Type, exts)
   239  			params = append(params, DebugParam{
   240  				Name:         ps.List[i].Names[j].Name,
   241  				Type:         vt.String(),
   242  				ExtendedType: et,
   243  				RealType:     rt,
   244  				TypeSC:       st,
   245  			})
   246  		}
   247  	}
   248  	ss := strings.Split(name, ".")
   249  	name = ss[len(ss)-1]
   250  	r, n := utf8.DecodeRuneInString(name)
   251  	st, vt, rt, et := c.scAndVMReturnTypeFromScope(scope, exts)
   252  
   253  	return &MethodDebugInfo{
   254  		ID: name,
   255  		Name: DebugMethodName{
   256  			Name:      string(unicode.ToLower(r)) + name[n:],
   257  			Namespace: scope.pkg.Name(),
   258  		},
   259  		IsExported:         scope.decl.Name.IsExported(),
   260  		IsFunction:         scope.decl.Recv == nil,
   261  		Range:              scope.rng,
   262  		Parameters:         params,
   263  		ReturnType:         vt,
   264  		ReturnTypeExtended: et,
   265  		ReturnTypeReal:     rt,
   266  		ReturnTypeSC:       st,
   267  		SeqPoints:          c.sequencePoints[name],
   268  		Variables:          scope.variables,
   269  	}
   270  }
   271  
   272  func (c *codegen) scAndVMReturnTypeFromScope(scope *funcScope, exts map[string]binding.ExtendedType) (smartcontract.ParamType, string, binding.Override, *binding.ExtendedType) {
   273  	results := scope.decl.Type.Results
   274  	switch results.NumFields() {
   275  	case 0:
   276  		return smartcontract.VoidType, "Void", binding.Override{}, nil
   277  	case 1:
   278  		st, vt, s, et := c.scAndVMTypeFromExpr(results.List[0].Type, exts)
   279  		return st, vt.String(), s, et
   280  	default:
   281  		// multiple return values are not supported in debugger
   282  		return smartcontract.AnyType, "Any", binding.Override{}, nil
   283  	}
   284  }
   285  
   286  func scAndVMInteropTypeFromExpr(named *types.Named, isPointer bool) (smartcontract.ParamType, stackitem.Type, binding.Override, *binding.ExtendedType) {
   287  	name := named.Obj().Name()
   288  	pkg := named.Obj().Pkg().Name()
   289  	switch pkg {
   290  	case "ledger", "management":
   291  		switch name {
   292  		case "ParameterType", "SignerScope", "WitnessAction", "WitnessConditionType", "VMState":
   293  			return smartcontract.IntegerType, stackitem.IntegerT, binding.Override{TypeName: "int"}, nil
   294  		}
   295  		// Block, Transaction, Contract.
   296  		typeName := pkg + "." + name
   297  		et := &binding.ExtendedType{Base: smartcontract.ArrayType, Name: typeName}
   298  		if isPointer {
   299  			typeName = "*" + typeName
   300  		}
   301  		return smartcontract.ArrayType, stackitem.ArrayT, binding.Override{
   302  			Package:  named.Obj().Pkg().Path(),
   303  			TypeName: typeName,
   304  		}, et
   305  	case "interop":
   306  		if name != "Interface" {
   307  			over := binding.Override{
   308  				Package:  interopPrefix,
   309  				TypeName: "interop." + name,
   310  			}
   311  			switch name {
   312  			case "Hash160":
   313  				return smartcontract.Hash160Type, stackitem.ByteArrayT, over, nil
   314  			case "Hash256":
   315  				return smartcontract.Hash256Type, stackitem.ByteArrayT, over, nil
   316  			case "PublicKey":
   317  				return smartcontract.PublicKeyType, stackitem.ByteArrayT, over, nil
   318  			case "Signature":
   319  				return smartcontract.SignatureType, stackitem.ByteArrayT, over, nil
   320  			}
   321  		}
   322  	}
   323  	return smartcontract.InteropInterfaceType,
   324  		stackitem.InteropT,
   325  		binding.Override{TypeName: "any"},
   326  		&binding.ExtendedType{Base: smartcontract.InteropInterfaceType, Interface: "iterator"} // Temporarily all interops are iterators.
   327  }
   328  
   329  func (c *codegen) scAndVMTypeFromExpr(typ ast.Expr, exts map[string]binding.ExtendedType) (smartcontract.ParamType, stackitem.Type, binding.Override, *binding.ExtendedType) {
   330  	return c.scAndVMTypeFromType(c.typeOf(typ), exts)
   331  }
   332  
   333  func (c *codegen) scAndVMTypeFromType(t types.Type, exts map[string]binding.ExtendedType) (smartcontract.ParamType, stackitem.Type, binding.Override, *binding.ExtendedType) {
   334  	if t == nil {
   335  		return smartcontract.AnyType, stackitem.AnyT, binding.Override{TypeName: "any"}, nil
   336  	}
   337  
   338  	var isPtr bool
   339  
   340  	named, isNamed := t.(*types.Named)
   341  	if !isNamed {
   342  		var ptr *types.Pointer
   343  		if ptr, isPtr = t.(*types.Pointer); isPtr {
   344  			named, isNamed = ptr.Elem().(*types.Named)
   345  		}
   346  	}
   347  	if isNamed {
   348  		if isInteropPath(named.String()) {
   349  			st, vt, over, et := scAndVMInteropTypeFromExpr(named, isPtr)
   350  			if et != nil && et.Base == smartcontract.ArrayType && exts != nil && exts[et.Name].Name != et.Name {
   351  				_ = c.genStructExtended(named.Underlying().(*types.Struct), et.Name, exts)
   352  			}
   353  			return st, vt, over, et
   354  		}
   355  	}
   356  	if ptr, isPtr := t.(*types.Pointer); isPtr {
   357  		t = ptr.Elem()
   358  	}
   359  	var over binding.Override
   360  	switch t := t.Underlying().(type) {
   361  	case *types.Basic:
   362  		info := t.Info()
   363  		switch {
   364  		case info&types.IsInteger != 0:
   365  			over.TypeName = "int"
   366  			return smartcontract.IntegerType, stackitem.IntegerT, over, nil
   367  		case info&types.IsBoolean != 0:
   368  			over.TypeName = "bool"
   369  			return smartcontract.BoolType, stackitem.BooleanT, over, nil
   370  		case info&types.IsString != 0:
   371  			over.TypeName = "string"
   372  			return smartcontract.StringType, stackitem.ByteArrayT, over, nil
   373  		default:
   374  			over.TypeName = "any"
   375  			return smartcontract.AnyType, stackitem.AnyT, over, nil
   376  		}
   377  	case *types.Map:
   378  		et := &binding.ExtendedType{
   379  			Base: smartcontract.MapType,
   380  		}
   381  		et.Key, _, _, _ = c.scAndVMTypeFromType(t.Key(), exts)
   382  		vt, _, over, vet := c.scAndVMTypeFromType(t.Elem(), exts)
   383  		et.Value = vet
   384  		if et.Value == nil {
   385  			et.Value = &binding.ExtendedType{Base: vt}
   386  		}
   387  		over.TypeName = "map[" + t.Key().String() + "]" + over.TypeName
   388  		return smartcontract.MapType, stackitem.MapT, over, et
   389  	case *types.Struct:
   390  		var extName string
   391  		if isNamed {
   392  			over.Package = named.Obj().Pkg().Path()
   393  			over.TypeName = named.Obj().Pkg().Name() + "." + named.Obj().Name()
   394  			_ = c.genStructExtended(t, over.TypeName, exts)
   395  			extName = over.TypeName
   396  		} else {
   397  			name := "unnamed"
   398  			if exts != nil {
   399  				for exts[name].Name == name {
   400  					name = name + "X"
   401  				}
   402  				_ = c.genStructExtended(t, name, exts)
   403  			}
   404  			// For bindings configurator this structure becomes named in fact. Its name
   405  			// is "unnamed[X...X]".
   406  			extName = name
   407  		}
   408  		return smartcontract.ArrayType, stackitem.StructT, over,
   409  			&binding.ExtendedType{ // Value-less, refer to exts.
   410  				Base: smartcontract.ArrayType,
   411  				Name: extName,
   412  			}
   413  
   414  	case *types.Slice:
   415  		if isByte(t.Elem()) {
   416  			over.TypeName = "[]byte"
   417  			return smartcontract.ByteArrayType, stackitem.ByteArrayT, over, nil
   418  		}
   419  		et := &binding.ExtendedType{
   420  			Base: smartcontract.ArrayType,
   421  		}
   422  		vt, _, over, vet := c.scAndVMTypeFromType(t.Elem(), exts)
   423  		et.Value = vet
   424  		if et.Value == nil {
   425  			et.Value = &binding.ExtendedType{
   426  				Base: vt,
   427  			}
   428  		}
   429  		if over.TypeName != "" {
   430  			over.TypeName = "[]" + over.TypeName
   431  		}
   432  		return smartcontract.ArrayType, stackitem.ArrayT, over, et
   433  	default:
   434  		over.TypeName = "any"
   435  		return smartcontract.AnyType, stackitem.AnyT, over, nil
   436  	}
   437  }
   438  
   439  func (c *codegen) genStructExtended(t *types.Struct, name string, exts map[string]binding.ExtendedType) *binding.ExtendedType {
   440  	var et *binding.ExtendedType
   441  	if exts != nil {
   442  		if exts[name].Name != name {
   443  			et = &binding.ExtendedType{
   444  				Base:   smartcontract.ArrayType,
   445  				Name:   name,
   446  				Fields: make([]binding.FieldExtendedType, t.NumFields()),
   447  			}
   448  			exts[name] = *et // Prefill to solve recursive structures.
   449  			for i := range et.Fields {
   450  				field := t.Field(i)
   451  				ft, _, _, fet := c.scAndVMTypeFromType(field.Type(), exts)
   452  				if fet == nil {
   453  					et.Fields[i].ExtendedType.Base = ft
   454  				} else {
   455  					et.Fields[i].ExtendedType = *fet
   456  				}
   457  				et.Fields[i].Field = field.Name()
   458  			}
   459  			exts[name] = *et // Set real structure data.
   460  		} else {
   461  			et = new(binding.ExtendedType)
   462  			*et = exts[name]
   463  		}
   464  	}
   465  	return et
   466  }
   467  
   468  // MarshalJSON implements the json.Marshaler interface.
   469  func (d *DebugRange) MarshalJSON() ([]byte, error) {
   470  	return []byte(`"` + strconv.FormatUint(uint64(d.Start), 10) + `-` +
   471  		strconv.FormatUint(uint64(d.End), 10) + `"`), nil
   472  }
   473  
   474  // UnmarshalJSON implements the json.Unmarshaler interface.
   475  func (d *DebugRange) UnmarshalJSON(data []byte) error {
   476  	startS, endS, err := parsePairJSON(data, "-")
   477  	if err != nil {
   478  		return err
   479  	}
   480  	start, err := strconv.ParseUint(startS, 10, 16)
   481  	if err != nil {
   482  		return err
   483  	}
   484  	end, err := strconv.ParseUint(endS, 10, 16)
   485  	if err != nil {
   486  		return err
   487  	}
   488  
   489  	d.Start = uint16(start)
   490  	d.End = uint16(end)
   491  
   492  	return nil
   493  }
   494  
   495  // MarshalJSON implements the json.Marshaler interface.
   496  func (d *DebugParam) MarshalJSON() ([]byte, error) {
   497  	return []byte(`"` + d.Name + `,` + d.Type + `"`), nil
   498  }
   499  
   500  // UnmarshalJSON implements the json.Unmarshaler interface.
   501  func (d *DebugParam) UnmarshalJSON(data []byte) error {
   502  	startS, endS, err := parsePairJSON(data, ",")
   503  	if err != nil {
   504  		return err
   505  	}
   506  
   507  	d.Name = startS
   508  	d.Type = endS
   509  
   510  	return nil
   511  }
   512  
   513  // ToManifestParameter converts DebugParam to manifest.Parameter.
   514  func (d *DebugParam) ToManifestParameter() manifest.Parameter {
   515  	return manifest.Parameter{
   516  		Name: d.Name,
   517  		Type: d.TypeSC,
   518  	}
   519  }
   520  
   521  // ToManifestMethod converts MethodDebugInfo to manifest.Method.
   522  func (m *MethodDebugInfo) ToManifestMethod() manifest.Method {
   523  	var (
   524  		result manifest.Method
   525  	)
   526  	parameters := make([]manifest.Parameter, len(m.Parameters))
   527  	for i, p := range m.Parameters {
   528  		parameters[i] = p.ToManifestParameter()
   529  	}
   530  	result.Name = m.Name.Name
   531  	result.Offset = int(m.Range.Start)
   532  	result.Parameters = parameters
   533  	result.ReturnType = m.ReturnTypeSC
   534  	return result
   535  }
   536  
   537  // MarshalJSON implements the json.Marshaler interface.
   538  func (d *DebugMethodName) MarshalJSON() ([]byte, error) {
   539  	return []byte(`"` + d.Namespace + `,` + d.Name + `"`), nil
   540  }
   541  
   542  // UnmarshalJSON implements the json.Unmarshaler interface.
   543  func (d *DebugMethodName) UnmarshalJSON(data []byte) error {
   544  	startS, endS, err := parsePairJSON(data, ",")
   545  	if err != nil {
   546  		return err
   547  	}
   548  
   549  	d.Namespace = startS
   550  	d.Name = endS
   551  
   552  	return nil
   553  }
   554  
   555  // MarshalJSON implements the json.Marshaler interface.
   556  func (d *DebugSeqPoint) MarshalJSON() ([]byte, error) {
   557  	s := fmt.Sprintf("%d[%d]%d:%d-%d:%d", d.Opcode, d.Document,
   558  		d.StartLine, d.StartCol, d.EndLine, d.EndCol)
   559  	return []byte(`"` + s + `"`), nil
   560  }
   561  
   562  // UnmarshalJSON implements the json.Unmarshaler interface.
   563  func (d *DebugSeqPoint) UnmarshalJSON(data []byte) error {
   564  	_, err := fmt.Sscanf(string(data), `"%d[%d]%d:%d-%d:%d"`,
   565  		&d.Opcode, &d.Document, &d.StartLine, &d.StartCol, &d.EndLine, &d.EndCol)
   566  	return err
   567  }
   568  
   569  func parsePairJSON(data []byte, sep string) (string, string, error) {
   570  	var s string
   571  	if err := json.Unmarshal(data, &s); err != nil {
   572  		return "", "", err
   573  	}
   574  	ss := strings.SplitN(s, sep, 2)
   575  	if len(ss) != 2 {
   576  		return "", "", errors.New("invalid range format")
   577  	}
   578  	return ss[0], ss[1], nil
   579  }
   580  
   581  // ConvertToManifest converts a contract to the manifest.Manifest struct for debugger.
   582  // Note: manifest is taken from the external source, however it can be generated ad-hoc. See #1038.
   583  func (di *DebugInfo) ConvertToManifest(o *Options) (*manifest.Manifest, error) {
   584  	methods := make([]manifest.Method, 0)
   585  	for _, method := range di.Methods {
   586  		if method.IsExported && method.IsFunction && method.Name.Namespace == di.MainPkg {
   587  			mMethod := method.ToManifestMethod()
   588  			for i := range o.SafeMethods {
   589  				if mMethod.Name == o.SafeMethods[i] {
   590  					mMethod.Safe = true
   591  					break
   592  				}
   593  			}
   594  			methods = append(methods, mMethod)
   595  		}
   596  	}
   597  
   598  	result := manifest.NewManifest(o.Name)
   599  	if o.ContractSupportedStandards != nil {
   600  		result.SupportedStandards = o.ContractSupportedStandards
   601  	}
   602  	events := make([]manifest.Event, len(o.ContractEvents))
   603  	for i, e := range o.ContractEvents {
   604  		params := make([]manifest.Parameter, len(e.Parameters))
   605  		for j, p := range e.Parameters {
   606  			params[j] = p.Parameter
   607  		}
   608  		events[i] = manifest.Event{
   609  			Name:       o.ContractEvents[i].Name,
   610  			Parameters: params,
   611  		}
   612  	}
   613  	result.ABI = manifest.ABI{
   614  		Methods: methods,
   615  		Events:  events,
   616  	}
   617  	if result.ABI.Events == nil {
   618  		result.ABI.Events = make([]manifest.Event, 0)
   619  	}
   620  	result.Permissions = o.Permissions
   621  	for name, emitName := range o.Overloads {
   622  		m := result.ABI.GetMethod(name, -1)
   623  		if m == nil {
   624  			return nil, fmt.Errorf("overload for method %s was provided but it wasn't found", name)
   625  		}
   626  		if result.ABI.GetMethod(emitName, -1) == nil {
   627  			return nil, fmt.Errorf("overload with target method %s was provided but it wasn't found", emitName)
   628  		}
   629  
   630  		realM := result.ABI.GetMethod(emitName, len(m.Parameters))
   631  		if realM != nil {
   632  			return nil, fmt.Errorf("conflict overload for %s: "+
   633  				"multiple methods with the same number of parameters", name)
   634  		}
   635  		m.Name = emitName
   636  		// Check the resulting name against set of safe methods.
   637  		for i := range o.SafeMethods {
   638  			if m.Name == o.SafeMethods[i] {
   639  				m.Safe = true
   640  				break
   641  			}
   642  		}
   643  	}
   644  	return result, nil
   645  }