Merge pull request #2217 from nspcc-dev/compiler-overload
compiler: allow to overload methods in manifest
This commit is contained in:
commit
1d16016027
8 changed files with 128 additions and 0 deletions
|
@ -647,6 +647,22 @@ func TestCompileExamples(t *testing.T) {
|
|||
"--config", path.Join(examplePath, info.Name(), cfgName),
|
||||
}
|
||||
e.Run(t, opts...)
|
||||
|
||||
if info.Name() == "storage" {
|
||||
rawM, err := ioutil.ReadFile(manifestF)
|
||||
require.NoError(t, err)
|
||||
|
||||
m := new(manifest.Manifest)
|
||||
require.NoError(t, json.Unmarshal(rawM, m))
|
||||
|
||||
require.Nil(t, m.ABI.GetMethod("getDefault", 0))
|
||||
require.NotNil(t, m.ABI.GetMethod("get", 0))
|
||||
require.NotNil(t, m.ABI.GetMethod("get", 1))
|
||||
|
||||
require.Nil(t, m.ABI.GetMethod("putDefault", 1))
|
||||
require.NotNil(t, m.ABI.GetMethod("put", 1))
|
||||
require.NotNil(t, m.ABI.GetMethod("put", 2))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -498,6 +498,7 @@ func contractCompile(ctx *cli.Context) error {
|
|||
o.Permissions[i] = manifest.Permission(conf.Permissions[i])
|
||||
}
|
||||
o.SafeMethods = conf.SafeMethods
|
||||
o.Overloads = conf.Overloads
|
||||
}
|
||||
|
||||
result, err := compiler.CompileAndSave(src, o)
|
||||
|
@ -740,6 +741,7 @@ type ProjectConfig struct {
|
|||
SupportedStandards []string
|
||||
Events []manifest.Event
|
||||
Permissions []permission
|
||||
Overloads map[string]string `yaml:"overloads,omitempty"`
|
||||
}
|
||||
|
||||
func inspect(ctx *cli.Context) error {
|
||||
|
|
|
@ -233,6 +233,7 @@ Configuration file contains following options:
|
|||
| `supportedstandards` | List of standards this contract implements. For example, `NEP-11` or `NEP-17` token standard. This will enable additional checks in compiler. The check can be disabled with `--no-standards` flag. | `["NEP-17"]`
|
||||
| `events` | Notifications emitted by this contract. | See [Events](#Events). |
|
||||
| `permissions` | Foreign calls allowed for this contract. | See [Permissions](#Permissions). |
|
||||
| `overloads` | Custom method names for this contract. | See [Overloads](#Overloads). |
|
||||
|
||||
##### Events
|
||||
Each event must have a name and 0 or more parameters. Parameters are specified using their name and type.
|
||||
|
@ -318,6 +319,28 @@ Using either constant or literal for contract hash and method will allow compile
|
|||
to perform more extensive analysis.
|
||||
This check can be disabled with `--no-permissions` flag.
|
||||
|
||||
##### Overloads
|
||||
NeoVM allows a contract to have multiple methods with the same name
|
||||
but different parameters number. Go lacks this feature but this can be circumvented
|
||||
with `overloads` section. Essentially it is a mapping from default contract method names
|
||||
to the new ones.
|
||||
```
|
||||
- overloads:
|
||||
oldName1: newName
|
||||
oldName2: newName
|
||||
```
|
||||
Because the use-case for this is to provide multiple implementations with the same ABI name,
|
||||
`newName` is required to be already present in the compiled contract.
|
||||
|
||||
As an example consider [`NEP-11` standard](https://github.com/neo-project/proposals/blob/master/nep-11.mediawiki#transfer).
|
||||
It requires divisible NFT contract to have 2 `transfer` methods. To achieve this we might implement
|
||||
`Tranfer` and `TransferDivisible` and specify emitted name in config:
|
||||
```
|
||||
- overloads:
|
||||
transferDivisible:transfer
|
||||
```
|
||||
|
||||
|
||||
#### Manifest file
|
||||
Any contract can be included in a group identified by a public key which is used in [permissions](#Permissions).
|
||||
This is achieved with `manifest add-group` command.
|
||||
|
|
|
@ -8,6 +8,9 @@ import (
|
|||
// ctx holds storage context for contract methods
|
||||
var ctx storage.Context
|
||||
|
||||
// defaultKey represents the default key.
|
||||
var defaultKey = []byte("default")
|
||||
|
||||
// init inits storage context before any other contract method is called
|
||||
func init() {
|
||||
ctx = storage.GetContext()
|
||||
|
@ -19,11 +22,22 @@ func Put(key, value []byte) []byte {
|
|||
return key
|
||||
}
|
||||
|
||||
// PutDefault puts value to the default key.
|
||||
func PutDefault(value []byte) []byte {
|
||||
storage.Put(ctx, defaultKey, value)
|
||||
return defaultKey
|
||||
}
|
||||
|
||||
// Get returns the value at passed key.
|
||||
func Get(key []byte) interface{} {
|
||||
return storage.Get(ctx, key)
|
||||
}
|
||||
|
||||
// GetDefault returns the value at the default key.
|
||||
func GetDefault() interface{} {
|
||||
return storage.Get(ctx, defaultKey)
|
||||
}
|
||||
|
||||
// Delete deletes the value at passed key.
|
||||
func Delete(key []byte) bool {
|
||||
storage.Delete(ctx, key)
|
||||
|
|
|
@ -2,3 +2,6 @@ name: "Storage example"
|
|||
sourceurl: https://github.com/nspcc-dev/neo-go/
|
||||
supportedstandards: []
|
||||
events: []
|
||||
overloads:
|
||||
getDefault: get
|
||||
putDefault: put
|
||||
|
|
|
@ -64,6 +64,10 @@ type Options struct {
|
|||
// SafeMethods contains list of methods which will be marked as safe in manifest.
|
||||
SafeMethods []string
|
||||
|
||||
// Overloads contains mapping from compiled method name to the name emitted in manifest.
|
||||
// It can be used to provide method overloads as Go doesn't have such capability.
|
||||
Overloads map[string]string
|
||||
|
||||
// Permissions is a list of permissions for every contract method.
|
||||
Permissions []manifest.Permission
|
||||
}
|
||||
|
|
|
@ -446,5 +446,21 @@ func (di *DebugInfo) ConvertToManifest(o *Options) (*manifest.Manifest, error) {
|
|||
result.ABI.Events = make([]manifest.Event, 0)
|
||||
}
|
||||
result.Permissions = o.Permissions
|
||||
for name, emitName := range o.Overloads {
|
||||
m := result.ABI.GetMethod(name, -1)
|
||||
if m == nil {
|
||||
return nil, fmt.Errorf("overload for method %s was provided but it wasn't found", name)
|
||||
}
|
||||
if result.ABI.GetMethod(emitName, -1) == nil {
|
||||
return nil, fmt.Errorf("overload with target method %s was provided but it wasn't found", emitName)
|
||||
}
|
||||
|
||||
realM := result.ABI.GetMethod(emitName, len(m.Parameters))
|
||||
if realM != nil {
|
||||
return nil, fmt.Errorf("conflict overload for %s: "+
|
||||
"multiple methods with the same number of parameters", name)
|
||||
}
|
||||
m.Name = emitName
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package compiler
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||
|
@ -361,3 +362,52 @@ func TestDebugInfo_MarshalJSON(t *testing.T) {
|
|||
|
||||
testserdes.MarshalUnmarshalJSON(t, d, new(DebugInfo))
|
||||
}
|
||||
|
||||
func TestManifestOverload(t *testing.T) {
|
||||
src := `package foo
|
||||
func Main() int {
|
||||
return 1
|
||||
}
|
||||
func Add3() int {
|
||||
return Add3Aux(0)
|
||||
}
|
||||
func Add3Aux(a int) int {
|
||||
return a + 3
|
||||
}
|
||||
func Add3Aux2(b int) int {
|
||||
return b + 3
|
||||
}
|
||||
func Add4() int {
|
||||
return 4
|
||||
}`
|
||||
|
||||
_, di, err := CompileWithDebugInfo("foo", strings.NewReader(src))
|
||||
require.NoError(t, err)
|
||||
|
||||
m, err := di.ConvertToManifest(&Options{Overloads: map[string]string{"add3Aux": "add3"}})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, m.ABI.IsValid())
|
||||
require.NotNil(t, m.ABI.GetMethod("add3", 0))
|
||||
require.NotNil(t, m.ABI.GetMethod("add3", 1))
|
||||
require.Nil(t, m.ABI.GetMethod("add3Aux", 1))
|
||||
|
||||
t.Run("missing method", func(t *testing.T) {
|
||||
_, err := di.ConvertToManifest(&Options{Overloads: map[string]string{"miss": "add3"}})
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("parameter conflict", func(t *testing.T) {
|
||||
_, err := di.ConvertToManifest(&Options{Overloads: map[string]string{"add4": "add3"}})
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("parameter conflict, overload", func(t *testing.T) {
|
||||
_, err := di.ConvertToManifest(&Options{Overloads: map[string]string{
|
||||
"add3Aux": "add3",
|
||||
"add3Aux2": "add3",
|
||||
}})
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("missing target method", func(t *testing.T) {
|
||||
_, err := di.ConvertToManifest(&Options{Overloads: map[string]string{"add4": "add5"}})
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue