diff --git a/examples/events/events.go b/examples/events/events.go new file mode 100644 index 000000000..28254ee4a --- /dev/null +++ b/examples/events/events.go @@ -0,0 +1,30 @@ +package events + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" +) + +// NotifySomeBytes emits notification with ByteArray. +func NotifySomeBytes(arg []byte) { + runtime.Notify("SomeBytes", arg) +} + +// NotifySomeInteger emits notification with Integer. +func NotifySomeInteger(arg int) { + runtime.Notify("SomeInteger", arg) +} + +// NotifySomeString emits notification with String. +func NotifySomeString(arg string) { + runtime.Notify("SomeString", arg) +} + +// NotifySomeMap emits notification with Map. +func NotifySomeMap(arg map[string]int) { + runtime.Notify("SomeMap", arg) +} + +// NotifySomeArray emits notification with Array. +func NotifySomeArray(arg []int) { + runtime.Notify("SomeArray", arg) +} diff --git a/examples/events/events.yml b/examples/events/events.yml new file mode 100644 index 000000000..51d25cdde --- /dev/null +++ b/examples/events/events.yml @@ -0,0 +1,23 @@ +name: "Event types example" +supportedstandards: [] +events: + - name: SomeBytes + parameters: + - name: bytes + type: ByteArray + - name: SomeInteger + parameters: + - name: int + type: Integer + - name: SomeString + parameters: + - name: str + type: String + - name: SomeMap + parameters: + - name: m + type: Map + - name: SomeArray + parameters: + - name: a + type: Array \ No newline at end of file diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 06548e535..a8f2a925e 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -877,7 +877,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { tv := c.typeAndValueOf(n.Args[0]) params := make([]string, 0, len(n.Args[1:])) for _, p := range n.Args[1:] { - params = append(params, c.scTypeFromExpr(p)) + st, _ := c.scAndVMTypeFromExpr(p) + params = append(params, st.String()) } // Sometimes event name is stored in a var. // Skip in this case. diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index 42cdf88c3..1676a0a3e 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -13,6 +13,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) // DebugInfo represents smart-contract debug information. @@ -42,8 +43,10 @@ type MethodDebugInfo struct { // Parameters is a list of method's parameters. Parameters []DebugParam `json:"params"` // ReturnType is method's return type. - ReturnType string `json:"return"` - Variables []string `json:"variables"` + ReturnType string `json:"return"` + // ReturnTypeSC is return type to use in manifest. + ReturnTypeSC smartcontract.ParamType `json:"-"` + Variables []string `json:"variables"` // SeqPoints is a map between source lines and byte-code instruction offsets. SeqPoints []DebugSeqPoint `json:"sequence-points"` } @@ -86,8 +89,9 @@ type DebugRange struct { // DebugParam represents variables's name and type. type DebugParam struct { - Name string `json:"name"` - Type string `json:"type"` + Name string `json:"name"` + Type string `json:"type"` + TypeSC smartcontract.ParamType `json:"-"` } func (c *codegen) saveSequencePoint(n ast.Node) { @@ -128,8 +132,9 @@ func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo { Start: 0, End: uint16(c.initEndOffset), }, - ReturnType: "Void", - SeqPoints: c.sequencePoints["init"], + ReturnType: "Void", + ReturnTypeSC: smartcontract.VoidType, + SeqPoints: c.sequencePoints["init"], }) } if c.deployEndOffset >= 0 { @@ -146,11 +151,13 @@ func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo { End: uint16(c.deployEndOffset), }, Parameters: []DebugParam{{ - Name: "isUpdate", - Type: "Boolean", + Name: "isUpdate", + Type: "Boolean", + TypeSC: smartcontract.BoolType, }}, - ReturnType: "Void", - SeqPoints: c.sequencePoints[manifest.MethodDeploy], + ReturnType: "Void", + ReturnTypeSC: smartcontract.VoidType, + SeqPoints: c.sequencePoints[manifest.MethodDeploy], }) } for name, scope := range c.funcs { @@ -169,8 +176,8 @@ func (c *codegen) registerDebugVariable(name string, expr ast.Expr) { // do not save globals for now return } - typ := c.scTypeFromExpr(expr) - c.scope.variables = append(c.scope.variables, name+","+typ) + _, vt := c.scAndVMTypeFromExpr(expr) + c.scope.variables = append(c.scope.variables, name+","+vt.String()) } func (c *codegen) methodInfoFromScope(name string, scope *funcScope) *MethodDebugInfo { @@ -178,48 +185,53 @@ func (c *codegen) methodInfoFromScope(name string, scope *funcScope) *MethodDebu params := make([]DebugParam, 0, ps.NumFields()) for i := range ps.List { for j := range ps.List[i].Names { + st, vt := c.scAndVMTypeFromExpr(ps.List[i].Type) params = append(params, DebugParam{ - Name: ps.List[i].Names[j].Name, - Type: c.scTypeFromExpr(ps.List[i].Type), + Name: ps.List[i].Names[j].Name, + Type: vt.String(), + TypeSC: st, }) } } ss := strings.Split(name, ".") name = ss[len(ss)-1] r, n := utf8.DecodeRuneInString(name) + st, vt := c.scAndVMReturnTypeFromScope(scope) return &MethodDebugInfo{ ID: name, Name: DebugMethodName{ Name: string(unicode.ToLower(r)) + name[n:], Namespace: scope.pkg.Name(), }, - IsExported: scope.decl.Name.IsExported(), - IsFunction: scope.decl.Recv == nil, - Range: scope.rng, - Parameters: params, - ReturnType: c.scReturnTypeFromScope(scope), - SeqPoints: c.sequencePoints[name], - Variables: scope.variables, + IsExported: scope.decl.Name.IsExported(), + IsFunction: scope.decl.Recv == nil, + Range: scope.rng, + Parameters: params, + ReturnType: vt, + ReturnTypeSC: st, + SeqPoints: c.sequencePoints[name], + Variables: scope.variables, } } -func (c *codegen) scReturnTypeFromScope(scope *funcScope) string { +func (c *codegen) scAndVMReturnTypeFromScope(scope *funcScope) (smartcontract.ParamType, string) { results := scope.decl.Type.Results switch results.NumFields() { case 0: - return "Void" + return smartcontract.VoidType, "Void" case 1: - return c.scTypeFromExpr(results.List[0].Type) + st, vt := c.scAndVMTypeFromExpr(results.List[0].Type) + return st, vt.String() default: // multiple return values are not supported in debugger - return "Any" + return smartcontract.AnyType, "Any" } } -func (c *codegen) scTypeFromExpr(typ ast.Expr) string { +func (c *codegen) scAndVMTypeFromExpr(typ ast.Expr) (smartcontract.ParamType, stackitem.Type) { t := c.typeOf(typ) if c.typeOf(typ) == nil { - return "Any" + return smartcontract.AnyType, stackitem.AnyT } if named, ok := t.(*types.Named); ok { if isInteropPath(named.String()) { @@ -227,13 +239,22 @@ func (c *codegen) scTypeFromExpr(typ ast.Expr) string { pkg := named.Obj().Pkg().Name() switch pkg { case "blockchain", "contract": - return "Array" // Block, Transaction, Contract + return smartcontract.ArrayType, stackitem.ArrayT // Block, Transaction, Contract case "interop": if name != "Interface" { - return name + switch name { + case "Hash160": + return smartcontract.Hash160Type, stackitem.ByteArrayT + case "Hash256": + return smartcontract.Hash256Type, stackitem.ByteArrayT + case "PublicKey": + return smartcontract.PublicKeyType, stackitem.ByteArrayT + case "Signature": + return smartcontract.SignatureType, stackitem.ByteArrayT + } } } - return "InteropInterface" + return smartcontract.InteropInterfaceType, stackitem.InteropT } } switch t := t.Underlying().(type) { @@ -241,25 +262,25 @@ func (c *codegen) scTypeFromExpr(typ ast.Expr) string { info := t.Info() switch { case info&types.IsInteger != 0: - return "Integer" + return smartcontract.IntegerType, stackitem.IntegerT case info&types.IsBoolean != 0: - return "Boolean" + return smartcontract.BoolType, stackitem.BooleanT case info&types.IsString != 0: - return "String" + return smartcontract.StringType, stackitem.ByteArrayT default: - return "Any" + return smartcontract.AnyType, stackitem.AnyT } case *types.Map: - return "Map" + return smartcontract.MapType, stackitem.MapT case *types.Struct: - return "Struct" + return smartcontract.ArrayType, stackitem.StructT case *types.Slice: if isByte(t.Elem()) { - return "ByteString" + return smartcontract.ByteArrayType, stackitem.ByteArrayT } - return "Array" + return smartcontract.ArrayType, stackitem.ArrayT default: - return "Any" + return smartcontract.AnyType, stackitem.AnyT } } @@ -310,13 +331,9 @@ func (d *DebugParam) UnmarshalJSON(data []byte) error { // ToManifestParameter converts DebugParam to manifest.Parameter func (d *DebugParam) ToManifestParameter() (manifest.Parameter, error) { - pType, err := smartcontract.ParseParamType(d.Type) - if err != nil { - return manifest.Parameter{}, err - } return manifest.Parameter{ Name: d.Name, - Type: pType, + Type: d.TypeSC, }, nil } @@ -333,14 +350,10 @@ func (m *MethodDebugInfo) ToManifestMethod() (manifest.Method, error) { return result, err } } - returnType, err := smartcontract.ParseParamType(m.ReturnType) - if err != nil { - return result, err - } result.Name = m.Name.Name result.Offset = int(m.Range.Start) result.Parameters = parameters - result.ReturnType = returnType + result.ReturnType = m.ReturnTypeSC return result, nil } diff --git a/pkg/compiler/debug_test.go b/pkg/compiler/debug_test.go index 858af186c..f7c5a6211 100644 --- a/pkg/compiler/debug_test.go +++ b/pkg/compiler/debug_test.go @@ -71,8 +71,8 @@ func _deploy(isUpdate bool) {} t.Run("return types", func(t *testing.T) { returnTypes := map[string]string{ "MethodInt": "Integer", - "MethodConcat": "String", - "MethodString": "String", "MethodByteArray": "ByteString", + "MethodConcat": "ByteString", + "MethodString": "ByteString", "MethodByteArray": "ByteString", "MethodArray": "Array", "MethodStruct": "Struct", "Main": "Boolean", "unexportedMethod": "Integer", @@ -89,7 +89,7 @@ func _deploy(isUpdate bool) {} t.Run("variables", func(t *testing.T) { vars := map[string][]string{ - "Main": {"s,String", "res,Integer"}, + "Main": {"s,ByteString", "res,Integer"}, } for i := range d.Methods { v, ok := vars[d.Methods[i].ID] @@ -102,30 +102,36 @@ func _deploy(isUpdate bool) {} t.Run("param types", func(t *testing.T) { paramTypes := map[string][]DebugParam{ "_deploy": {{ - Name: "isUpdate", - Type: "Boolean", + Name: "isUpdate", + Type: "Boolean", + TypeSC: smartcontract.BoolType, }}, "MethodInt": {{ - Name: "a", - Type: "String", + Name: "a", + Type: "ByteString", + TypeSC: smartcontract.StringType, }}, "MethodConcat": { { - Name: "a", - Type: "String", + Name: "a", + Type: "ByteString", + TypeSC: smartcontract.StringType, }, { - Name: "b", - Type: "String", + Name: "b", + Type: "ByteString", + TypeSC: smartcontract.StringType, }, { - Name: "c", - Type: "String", + Name: "c", + Type: "ByteString", + TypeSC: smartcontract.StringType, }, }, "Main": {{ - Name: "op", - Type: "String", + Name: "op", + Type: "ByteString", + TypeSC: smartcontract.StringType, }}, } for i := range d.Methods { @@ -307,8 +313,8 @@ func TestDebugInfo_MarshalJSON(t *testing.T) { }, Range: DebugRange{Start: 10, End: 20}, Parameters: []DebugParam{ - {"param1", "Integer"}, - {"ok", "Boolean"}, + {Name: "param1", Type: "Integer"}, + {Name: "ok", Type: "Boolean"}, }, ReturnType: "ByteString", Variables: []string{},