Switch to exports

License: MIT
Signed-off-by: Jakub Sztandera <kubuxu@protonmail.ch>
This commit is contained in:
Jakub Sztandera 2019-07-11 18:15:44 +02:00
parent 219fc093b1
commit 9f8572018f
2 changed files with 46 additions and 46 deletions

View File

@ -4,8 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
"strconv"
"strings"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
) )
@ -42,7 +40,7 @@ func (inv *invoker) Invoke(act *Actor, vmctx *VMContext, method uint64, params [
} }
func (inv *invoker) register(c cid.Cid, instance interface{}) { func (inv *invoker) register(c cid.Cid, instance Invokee) {
code, err := inv.transform(instance) code, err := inv.transform(instance)
if err != nil { if err != nil {
panic(err) panic(err)
@ -54,51 +52,45 @@ type unmarshalCBOR interface {
UnmarshalCBOR([]byte) (int, error) UnmarshalCBOR([]byte) (int, error)
} }
type Invokee interface {
Exports() []interface{}
}
var tUnmarhsalCBOR = reflect.TypeOf((*unmarshalCBOR)(nil)).Elem() var tUnmarhsalCBOR = reflect.TypeOf((*unmarshalCBOR)(nil)).Elem()
var tError = reflect.TypeOf((*error)(nil)).Elem() var tError = reflect.TypeOf((*error)(nil)).Elem()
func (*invoker) transform(instance interface{}) (nativeCode, error) { func (*invoker) transform(instance Invokee) (nativeCode, error) {
itype := reflect.TypeOf(instance) itype := reflect.TypeOf(instance)
newErr := func(str string) error { exports := instance.Exports()
return fmt.Errorf("transform(%s): %s", itype.Name(), str) for i, m := range exports {
i := i
newErr = func(str string) error {
return fmt.Errorf("transform(%s) export(%d): %s", itype.Name(), i, str)
} }
var maxn uint64 if m == nil {
invokes := make(map[uint64]reflect.Method)
for i := 0; i < itype.NumMethod(); i++ {
meth := itype.Method(i)
if !strings.HasPrefix(meth.Name, "Invoke") {
continue continue
} }
sid := strings.TrimLeftFunc(meth.Name, func(r rune) bool { meth := reflect.ValueOf(m)
return r < '0' || r > '9' t := meth.Type()
}) if t.Kind() != reflect.Func {
return nil, newErr("is not a function")
id, err := strconv.ParseUint(sid, 10, 64)
if err != nil {
return nil, err
} }
if t.NumIn() != 3 {
t := meth.Type
if t.NumIn() != 4 {
return nil, newErr("wrong number of inputs should be: " + return nil, newErr("wrong number of inputs should be: " +
"*Actor, *VMContext, <type of parameter>") "*Actor, *VMContext, <type of parameter>")
} }
if t.In(0) != itype { if t.In(0) != reflect.TypeOf(&Actor{}) {
return nil, newErr("passed instance is not struct")
}
if t.In(1) != reflect.TypeOf(&Actor{}) {
return nil, newErr("first arguemnt should be *Actor") return nil, newErr("first arguemnt should be *Actor")
} }
if t.In(2) != reflect.TypeOf(&VMContext{}) { if t.In(1) != reflect.TypeOf(&VMContext{}) {
return nil, newErr("second argument should be *VMContext") return nil, newErr("second argument should be *VMContext")
} }
if !t.In(3).Implements(tUnmarhsalCBOR) { if !t.In(2).Implements(tUnmarhsalCBOR) {
return nil, newErr("parameter doesn't implement UnmarshalCBOR") return nil, newErr("parameter doesn't implement UnmarshalCBOR")
} }
if t.In(3).Kind() != reflect.Ptr { if t.In(2).Kind() != reflect.Ptr {
return nil, newErr("parameter has to be a pointer") return nil, newErr("parameter has to be a pointer")
} }
@ -113,21 +105,13 @@ func (*invoker) transform(instance interface{}) (nativeCode, error) {
return nil, newErr("second output should be error type") return nil, newErr("second output should be error type")
} }
if id > maxn {
maxn = id
} }
if _, has := invokes[id]; has { code := make(nativeCode, len(exports))
return nil, newErr(fmt.Sprintf("repeated method=%s id: %d", meth.Name, id)) for id, m := range exports {
} meth := reflect.ValueOf(m)
invokes[id] = meth
}
code := make(nativeCode, maxn+1)
_ = code
for id, meth := range invokes {
meth := meth
code[id] = reflect.MakeFunc(reflect.TypeOf((invokeFunc)(nil)), code[id] = reflect.MakeFunc(reflect.TypeOf((invokeFunc)(nil)),
func(in []reflect.Value) []reflect.Value { func(in []reflect.Value) []reflect.Value {
paramT := meth.Type.In(3).Elem() paramT := meth.Type().In(2).Elem()
param := reflect.New(paramT) param := reflect.New(paramT)
inBytes := in[2].Interface().([]byte) inBytes := in[2].Interface().([]byte)
@ -138,8 +122,8 @@ func (*invoker) transform(instance interface{}) (nativeCode, error) {
reflect.ValueOf(err), reflect.ValueOf(err),
} }
} }
return meth.Func.Call([]reflect.Value{ return meth.Call([]reflect.Value{
reflect.ValueOf(instance), in[0], in[1], param, in[0], in[1], param,
}) })
}).Interface().(invokeFunc) }).Interface().(invokeFunc)

View File

@ -16,6 +16,22 @@ func (b *basicParams) UnmarshalCBOR(in []byte) (int, error) {
return 1, nil return 1, nil
} }
func (b basicContract) Exports() []interface{} {
return []interface{}{
b.InvokeSomething0,
nil,
nil,
nil,
nil,
nil,
nil,
nil,
nil,
nil,
b.InvokeSomething10,
}
}
func (basicContract) InvokeSomething0(act *Actor, vmctx *VMContext, func (basicContract) InvokeSomething0(act *Actor, vmctx *VMContext,
params *basicParams) (InvokeRet, error) { params *basicParams) (InvokeRet, error) {
return InvokeRet{ return InvokeRet{
@ -37,7 +53,7 @@ func TestInvokerBasic(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, byte(1), ret.returnCode, "return code should be 1") assert.Equal(t, byte(1), ret.returnCode, "return code should be 1")
ret, err = code[10](nil, nil, []byte{1}) ret, err = code[10](nil, nil, []byte{2})
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, byte(11), ret.returnCode, "return code should be 1") assert.Equal(t, byte(12), ret.returnCode, "return code should be 1")
} }