aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMeng-Ying Yang <garfield@dexon.org>2019-05-03 11:00:36 +0800
committerMeng-Ying Yang <garfield@dexon.org>2019-05-06 17:36:32 +0800
commit28dfe769a5cab65ed3ea981a784e2598c818afc4 (patch)
tree38aaa2303ef7ec57e31f7e1c382324cca88c5956
parentc8fd7f412af66040d7e1664f292d83a10807cdc4 (diff)
downloaddexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.tar
dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.tar.gz
dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.tar.bz2
dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.tar.lz
dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.tar.xz
dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.tar.zst
dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.zip
core: vm: sqlvm: OpFunction support gas model
-rw-r--r--core/vm/sqlvm/errors/errors.go2
-rw-r--r--core/vm/sqlvm/runtime/gastable.go42
-rw-r--r--core/vm/sqlvm/runtime/instructions.go270
-rw-r--r--core/vm/sqlvm/runtime/instructions_test.go24
-rw-r--r--core/vm/sqlvm/runtime/jumptable.go143
-rw-r--r--core/vm/sqlvm/runtime/runtime.go8
6 files changed, 322 insertions, 167 deletions
diff --git a/core/vm/sqlvm/errors/errors.go b/core/vm/sqlvm/errors/errors.go
index c97b3e191..c448d7b43 100644
--- a/core/vm/sqlvm/errors/errors.go
+++ b/core/vm/sqlvm/errors/errors.go
@@ -123,6 +123,7 @@ const (
ErrorCodeMultipleEscapeByte
ErrorCodePendingEscapeByte
ErrorCodeNoSuchFunction
+ ErrorCodeOutOfGas
)
var errorCodeMap = [...]string{
@@ -157,6 +158,7 @@ var errorCodeMap = [...]string{
ErrorCodeMultipleEscapeByte: "multiple escape byte",
ErrorCodePendingEscapeByte: "pending escape byte",
ErrorCodeNoSuchFunction: "no such function",
+ ErrorCodeOutOfGas: "out of gas",
}
func (c ErrorCode) Error() string {
diff --git a/core/vm/sqlvm/runtime/gastable.go b/core/vm/sqlvm/runtime/gastable.go
new file mode 100644
index 000000000..5bffd7392
--- /dev/null
+++ b/core/vm/sqlvm/runtime/gastable.go
@@ -0,0 +1,42 @@
+package runtime
+
+import (
+ "github.com/dexon-foundation/dexon/core/vm/sqlvm/common"
+ se "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors"
+)
+
+// GasFunction accepts interface and returns consumed gas.
+type GasFunction func(v interface{}) (consumed uint64)
+
+var (
+ dummyGasFunc = func(v interface{}) uint64 { return 0 }
+)
+
+func applyGas(ctx *common.Context, fn GasFunction, v interface{}) (err error) {
+ consumed := fn(v)
+ if consumed > ctx.GasLimit {
+ err = se.ErrorCodeOutOfGas
+ return
+ }
+ ctx.GasLimit -= consumed
+ return
+}
+
+// Shared gas variables
+const (
+ GasArithAdd = 2
+ GasArithMul = 3
+ GasArithCmp = 3
+ GasBoolCmp = 3
+ GasMemAlloc = 10
+ GasMemFree = 5 // GC work
+ GasMemSwap = 5
+ GasMemSearch = 20
+ GasStorageRead = 30
+ GasStorageWrite = 50
+ GasBitCmp = 5
+)
+
+func constGasFunc(c uint64) GasFunction {
+ return func(v interface{}) uint64 { return c * v.(uint64) }
+}
diff --git a/core/vm/sqlvm/runtime/instructions.go b/core/vm/sqlvm/runtime/instructions.go
index 6fa72d61a..93e43709f 100644
--- a/core/vm/sqlvm/runtime/instructions.go
+++ b/core/vm/sqlvm/runtime/instructions.go
@@ -32,7 +32,7 @@ var (
// OpFunction type
// data could be fields Fields, pattern []byte, order Orders
-type OpFunction func(ctx *common.Context, ops, registers []*Operand, output uint) error
+type OpFunction func(ctx *common.Context, in Instruction) error
// Instruction represents single instruction with essential information
// collection.
@@ -41,6 +41,10 @@ type Instruction struct {
Input []*Operand
Output uint
Position uint32 // ast tree position
+
+ // Runtime assigned
+ Registers []*Operand
+ GasFunc GasFunction
}
// Raw with embedded big.Int value or byte slice which represents the real value
@@ -119,19 +123,19 @@ func (op *Operand) toUint8() ([]uint8, error) {
return result, nil
}
-func opLoad(ctx *common.Context, input []*Operand, registers []*Operand, output uint) error {
- tableIdx := input[0].Data[0][0].Value.IntPart()
+func opLoad(ctx *common.Context, in Instruction) error {
+ tableIdx := in.Input[0].Data[0][0].Value.IntPart()
if tableIdx >= int64(len(ctx.Storage.Schema)) {
return se.ErrorCodeIndexOutOfRange
}
table := ctx.Storage.Schema[tableIdx]
tableRef := schema.TableRef(tableIdx)
- ids, err := input[1].toUint64()
+ ids, err := in.Input[1].toUint64()
if err != nil {
return err
}
- fields, err := input[2].toUint8()
+ fields, err := in.Input[2].toUint8()
if err != nil {
return err
}
@@ -165,7 +169,7 @@ func opLoad(ctx *common.Context, input []*Operand, registers []*Operand, output
}
}
}
- registers[output] = &op
+ in.Registers[in.Output] = &op
return nil
}
@@ -414,19 +418,19 @@ func flowCheck(ctx *common.Context, v decimal.Decimal, dType ast.DataType) (err
return
}
-func opAdd(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opAdd(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op1, op2 := ops[0], ops[1]
+ op1, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op1, op2) || !metaAllArith(op1) {
err = se.ErrorCodeInvalidDataType
return
}
- l, err := findMaxDataLength(ops)
+ l, err := findMaxDataLength(in.Input)
if err != nil {
return
}
@@ -448,7 +452,7 @@ func opAdd(ctx *common.Context, ops, registers []*Operand, output uint) (err err
data[i] = raw
}
- registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data}
+ in.Registers[in.Output] = &Operand{Meta: op1.cloneMeta(), Data: data}
return
}
@@ -470,19 +474,19 @@ func (r *Raw) add(r2 *Raw) (r3 *Raw) {
return
}
-func opMul(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opMul(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op1, op2 := ops[0], ops[1]
+ op1, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op1, op2) || !metaAllArith(op1) {
err = se.ErrorCodeInvalidDataType
return
}
- l, err := findMaxDataLength(ops)
+ l, err := findMaxDataLength(in.Input)
if err != nil {
return
}
@@ -504,7 +508,7 @@ func opMul(ctx *common.Context, ops, registers []*Operand, output uint) (err err
data[i] = raw
}
- registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data}
+ in.Registers[in.Output] = &Operand{Meta: op1.cloneMeta(), Data: data}
return
}
@@ -527,19 +531,19 @@ func (r *Raw) mul(r2 *Raw) (r3 *Raw) {
return
}
-func opSub(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opSub(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op1, op2 := ops[0], ops[1]
+ op1, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op1, op2) || !metaAllArith(op1) {
err = se.ErrorCodeInvalidDataType
return
}
- l, err := findMaxDataLength(ops)
+ l, err := findMaxDataLength(in.Input)
if err != nil {
return
}
@@ -561,7 +565,7 @@ func opSub(ctx *common.Context, ops, registers []*Operand, output uint) (err err
data[i] = raw
}
- registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data}
+ in.Registers[in.Output] = &Operand{Meta: op1.cloneMeta(), Data: data}
return
}
@@ -584,19 +588,19 @@ func (r *Raw) sub(r2 *Raw) (r3 *Raw) {
return
}
-func opDiv(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opDiv(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op1, op2 := ops[0], ops[1]
+ op1, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op1, op2) || !metaAllArith(op1) {
err = se.ErrorCodeInvalidDataType
return
}
- l, err := findMaxDataLength(ops)
+ l, err := findMaxDataLength(in.Input)
if err != nil {
return
}
@@ -617,7 +621,7 @@ func opDiv(ctx *common.Context, ops, registers []*Operand, output uint) (err err
data[i] = raw
}
- registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data}
+ in.Registers[in.Output] = &Operand{Meta: op1.cloneMeta(), Data: data}
return
}
@@ -653,19 +657,19 @@ func (r *Raw) div(r2 *Raw) (r3 *Raw, err error) {
return
}
-func opMod(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opMod(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op1, op2 := ops[0], ops[1]
+ op1, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op1, op2) || !metaAllArith(op1) {
err = se.ErrorCodeInvalidDataType
return
}
- l, err := findMaxDataLength(ops)
+ l, err := findMaxDataLength(in.Input)
if err != nil {
return
}
@@ -686,7 +690,7 @@ func opMod(ctx *common.Context, ops, registers []*Operand, output uint) (err err
data[i] = raw
}
- registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data}
+ in.Registers[in.Output] = &Operand{Meta: op1.cloneMeta(), Data: data}
return
}
@@ -716,19 +720,19 @@ func (r *Raw) mod(r2 *Raw) (r3 *Raw, err error) {
return
}
-func opLt(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opLt(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op1, op2 := ops[0], ops[1]
+ op1, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op1, op2) {
err = se.ErrorCodeInvalidDataType
return
}
- l, err := findMaxDataLength(ops)
+ l, err := findMaxDataLength(in.Input)
if err != nil {
return
}
@@ -750,7 +754,7 @@ func opLt(ctx *common.Context, ops, registers []*Operand, output uint) (err erro
meta[i] = boolType
}
- registers[output] = &Operand{Meta: meta, Data: data}
+ in.Registers[in.Output] = &Operand{Meta: meta, Data: data}
return
}
@@ -771,19 +775,19 @@ func (r *Raw) lt(r2 *Raw) (lt bool) {
return
}
-func opGt(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opGt(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op1, op2 := ops[0], ops[1]
+ op1, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op1, op2) {
err = se.ErrorCodeInvalidDataType
return
}
- l, err := findMaxDataLength(ops)
+ l, err := findMaxDataLength(in.Input)
if err != nil {
return
}
@@ -805,7 +809,7 @@ func opGt(ctx *common.Context, ops, registers []*Operand, output uint) (err erro
meta[i] = boolType
}
- registers[output] = &Operand{Meta: meta, Data: data}
+ in.Registers[in.Output] = &Operand{Meta: meta, Data: data}
return
}
@@ -826,19 +830,19 @@ func (r *Raw) gt(r2 *Raw) (gt bool) {
return
}
-func opEq(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opEq(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op1, op2 := ops[0], ops[1]
+ op1, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op1, op2) {
err = se.ErrorCodeInvalidDataType
return
}
- l, err := findMaxDataLength(ops)
+ l, err := findMaxDataLength(in.Input)
if err != nil {
return
}
@@ -860,7 +864,7 @@ func opEq(ctx *common.Context, ops, registers []*Operand, output uint) (err erro
meta[i] = boolType
}
- registers[output] = &Operand{Meta: meta, Data: data}
+ in.Registers[in.Output] = &Operand{Meta: meta, Data: data}
return
}
@@ -872,12 +876,12 @@ func (t Tuple) eq(t2 Tuple, meta []ast.DataType) (t3 Tuple) {
return
}
-func opAnd(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opAnd(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op1, op2 := ops[0], ops[1]
+ op1, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op1, op2) {
err = se.ErrorCodeInvalidDataType
@@ -889,7 +893,7 @@ func opAnd(ctx *common.Context, ops, registers []*Operand, output uint) (err err
return
}
- l, err := findMaxDataLength(ops)
+ l, err := findMaxDataLength(in.Input)
if err != nil {
return
}
@@ -911,7 +915,7 @@ func opAnd(ctx *common.Context, ops, registers []*Operand, output uint) (err err
meta[i] = boolType
}
- registers[output] = &Operand{Meta: meta, Data: data}
+ in.Registers[in.Output] = &Operand{Meta: meta, Data: data}
return
}
@@ -928,12 +932,12 @@ func (r *Raw) and(r2 *Raw) (r3 *Raw) {
return
}
-func opOr(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opOr(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op1, op2 := ops[0], ops[1]
+ op1, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op1, op2) {
err = se.ErrorCodeInvalidDataType
@@ -945,7 +949,7 @@ func opOr(ctx *common.Context, ops, registers []*Operand, output uint) (err erro
return
}
- l, err := findMaxDataLength(ops)
+ l, err := findMaxDataLength(in.Input)
if err != nil {
return
}
@@ -967,7 +971,7 @@ func opOr(ctx *common.Context, ops, registers []*Operand, output uint) (err erro
meta[i] = boolType
}
- registers[output] = &Operand{Meta: meta, Data: data}
+ in.Registers[in.Output] = &Operand{Meta: meta, Data: data}
return
}
@@ -984,12 +988,12 @@ func (r *Raw) or(r2 *Raw) (r3 *Raw) {
return
}
-func opNot(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 1 {
+func opNot(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 1 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op := ops[0]
+ op := in.Input[0]
if !metaAllBool(op) {
err = se.ErrorCodeInvalidDataType
@@ -1001,7 +1005,7 @@ func opNot(ctx *common.Context, ops, registers []*Operand, output uint) (err err
data[i] = op.Data[i].not()
}
- registers[output] = &Operand{Meta: op.cloneMeta(), Data: data}
+ in.Registers[in.Output] = &Operand{Meta: op.cloneMeta(), Data: data}
return
}
@@ -1018,12 +1022,12 @@ func (r *Raw) not() (r2 *Raw) {
return
}
-func opUnion(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opUnion(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op1, op2 := ops[0], ops[1]
+ op1, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op1, op2) {
err = se.ErrorCodeInvalidDataType
@@ -1057,16 +1061,16 @@ func opUnion(ctx *common.Context, ops, registers []*Operand, output uint) (err e
func(i, j int) bool { return op3.Data[i].less(op3.Data[j], orders) },
)
- registers[output] = op3
+ in.Registers[in.Output] = op3
return
}
-func opIntxn(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opIntxn(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op1, op2 := ops[0], ops[1]
+ op1, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op1, op2) {
err = se.ErrorCodeInvalidDataType
@@ -1100,20 +1104,20 @@ func opIntxn(ctx *common.Context, ops, registers []*Operand, output uint) (err e
func(i, j int) bool { return op3.Data[i].less(op3.Data[j], orders) },
)
- registers[output] = op3
+ in.Registers[in.Output] = op3
return
}
-func opLike(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 && len(ops) != 3 {
+func opLike(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 && len(in.Input) != 3 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op, pattern := ops[0], ops[1]
+ op, pattern := in.Input[0], in.Input[1]
var escape *Operand
- if len(ops) > 2 {
- escape = ops[2]
+ if len(in.Input) > 2 {
+ escape = in.Input[2]
}
var cReg *regexp.Regexp
@@ -1197,7 +1201,7 @@ func opLike(ctx *common.Context, ops, registers []*Operand, output uint) (err er
meta[i] = boolType
}
- registers[output] = &Operand{Meta: meta, Data: data}
+ in.Registers[in.Output] = &Operand{Meta: meta, Data: data}
return
}
@@ -1283,49 +1287,49 @@ func (r *Raw) like(reg *regexp.Regexp) (r2 *Raw) {
return
}
-func opZip(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) == 0 {
+func opZip(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) == 0 {
err = se.ErrorCodeInvalidOperandNum
return
}
- l, err := findMaxDataLength(ops)
+ l, err := findMaxDataLength(in.Input)
if err != nil {
return
}
op3 := &Operand{Meta: make([]ast.DataType, 0), Data: make([]Tuple, l)}
- for i := 0; i < len(ops); i++ {
- op3.Meta = append(op3.Meta, ops[i].Meta...)
+ for i := 0; i < len(in.Input); i++ {
+ op3.Meta = append(op3.Meta, in.Input[i].Meta...)
}
for i := 0; i < l; i++ {
- if ops[0].IsImmediate {
- op3.Data[i] = append(Tuple{}, ops[0].Data[0]...)
+ if in.Input[0].IsImmediate {
+ op3.Data[i] = append(Tuple{}, in.Input[0].Data[0]...)
} else {
- op3.Data[i] = append(Tuple{}, ops[0].Data[i]...)
+ op3.Data[i] = append(Tuple{}, in.Input[0].Data[i]...)
}
- for j := 1; j < len(ops); j++ {
- if ops[j].IsImmediate {
- op3.Data[i] = append(op3.Data[i], ops[j].Data[0]...)
+ for j := 1; j < len(in.Input); j++ {
+ if in.Input[j].IsImmediate {
+ op3.Data[i] = append(op3.Data[i], in.Input[j].Data[0]...)
} else {
- op3.Data[i] = append(op3.Data[i], ops[j].Data[i]...)
+ op3.Data[i] = append(op3.Data[i], in.Input[j].Data[i]...)
}
}
}
- registers[output] = op3
+ in.Registers[in.Output] = op3
return
}
-func opField(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opField(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op, fields := ops[0], ops[1].Data[0]
+ op, fields := in.Input[0], in.Input[1].Data[0]
fLen := len(fields)
var fieldIdx uint16
@@ -1348,17 +1352,17 @@ func opField(ctx *common.Context, ops, registers []*Operand, output uint) (err e
data[i] = tuple
}
- registers[output] = &Operand{Meta: meta, Data: data}
+ in.Registers[in.Output] = &Operand{Meta: meta, Data: data}
return
}
// in-place Op
-func opPrune(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opPrune(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op, fields := ops[0], ops[1].Data[0]
+ op, fields := in.Input[0], in.Input[1].Data[0]
fLen := len(fields)
var fieldIdx uint16
@@ -1405,12 +1409,12 @@ func pruneTuple(t Tuple, prune []int) Tuple {
}
// in-place Op
-func opCut(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opCut(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op, slice := ops[0], ops[1].Data[0]
+ op, slice := in.Input[0], in.Input[1].Data[0]
maxL := uint16(len(op.Meta))
start, end := value2ColIdx(slice[0].Value), maxL
@@ -1429,17 +1433,17 @@ func opCut(ctx *common.Context, ops, registers []*Operand, output uint) (err err
op.Data[i] = append(op.Data[i][:start], op.Data[i][end:]...)
}
- registers[output] = op
+ in.Registers[in.Output] = op
return
}
// in-place Op
-func opRange(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opRange(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op, slice := ops[0], ops[1].Data[0]
+ op, slice := in.Input[0], in.Input[1].Data[0]
offset, err := ast.DecimalToUint64(slice[0].Value)
if err != nil {
@@ -1465,21 +1469,21 @@ func opRange(ctx *common.Context, ops, registers []*Operand, output uint) (err e
}
}
- registers[output] = op
+ in.Registers[in.Output] = op
return
}
// in-place Op
-func opSort(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opSort(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op := ops[0]
+ op := in.Input[0]
// orders (ascending bool, field)
- orders := make([]sortOption, len(ops[1].Data))
- for i, o := range ops[1].Data {
+ orders := make([]sortOption, len(in.Input[1].Data))
+ for i, o := range in.Input[1].Data {
orders[i] = sortOption{
Asc: o[0].isTrue(),
Field: uint(value2ColIdx(o[1].Value)),
@@ -1529,12 +1533,12 @@ func (r *Raw) cmp(r2 *Raw) (v int) {
return
}
-func opFilter(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opFilter(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op, filters := ops[0], ops[1]
+ op, filters := in.Input[0], in.Input[1]
op2 := &Operand{Meta: op.cloneMeta(), Data: make([]Tuple, 0)}
@@ -1544,25 +1548,25 @@ func opFilter(ctx *common.Context, ops, registers []*Operand, output uint) (err
}
}
- registers[output] = op2
+ in.Registers[in.Output] = op2
return
}
// Type check will ensure all cast is valid
-func opCast(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opCast(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
- op := ops[0]
- dTypes := ops[1].Meta
+ op := in.Input[0]
+ dTypes := in.Input[1].Meta
if len(dTypes) != len(op.Meta) {
err = se.ErrorCodeInvalidDataType
return
}
- op2 := &Operand{Meta: ops[1].cloneMeta(), Data: make([]Tuple, len(op.Data))}
+ op2 := &Operand{Meta: in.Input[1].cloneMeta(), Data: make([]Tuple, len(op.Data))}
for i := 0; i < len(op.Data); i++ {
op2.Data[i] = append(Tuple{}, op.Data[i]...)
@@ -1578,7 +1582,7 @@ func opCast(ctx *common.Context, ops, registers []*Operand, output uint) (err er
}
}
- registers[output] = op2
+ in.Registers[in.Output] = op2
return
}
@@ -1773,12 +1777,12 @@ func (r *Raw) shiftBytes(src []byte, l int, signed, rPadding bool) (tgr []byte)
return
}
-func opConcat(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 2 {
+func opConcat(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 2 {
err = se.ErrorCodeDataLengthNotMatch
return
}
- op, op2 := ops[0], ops[1]
+ op, op2 := in.Input[0], in.Input[1]
if !metaAllEq(op, op2) {
err = se.ErrorCodeInvalidDataType
@@ -1797,7 +1801,7 @@ func opConcat(ctx *common.Context, ops, registers []*Operand, output uint) (err
op3.Data[i] = op.Data[i].concat(op2.Data[i])
}
- registers[output] = op3
+ in.Registers[in.Output] = op3
return
}
@@ -1811,12 +1815,12 @@ func (t Tuple) concat(t2 Tuple) (t3 Tuple) {
return
}
-func opNeg(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) != 1 {
+func opNeg(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) != 1 {
err = se.ErrorCodeDataLengthNotMatch
return
}
- op := ops[0]
+ op := in.Input[0]
if !metaAllSignedNumeric(op) {
err = se.ErrorCodeInvalidDataType
@@ -1833,7 +1837,7 @@ func opNeg(ctx *common.Context, ops, registers []*Operand, output uint) (err err
}
}
- registers[output] = op2
+ in.Registers[in.Output] = op2
return
}
@@ -1850,14 +1854,14 @@ func (t Tuple) neg(ctx *common.Context, meta []ast.DataType) (t2 Tuple, err erro
return
}
-func opFunc(ctx *common.Context, ops, registers []*Operand, output uint) (err error) {
- if len(ops) < 2 {
+func opFunc(ctx *common.Context, in Instruction) (err error) {
+ if len(in.Input) < 2 {
err = se.ErrorCodeInvalidOperandNum
return
}
var (
- opLength, opFuncID = ops[0], ops[1]
+ opLength, opFuncID = in.Input[0], in.Input[1]
result *Operand
length uint64
)
@@ -1882,12 +1886,12 @@ func opFunc(ctx *common.Context, ops, registers []*Operand, output uint) (err er
return
}
- result, err = fnTable[id](ctx, ops[2:], length)
+ result, err = fnTable[id](ctx, in.Input[2:], length)
if err != nil {
return
}
- registers[output] = result
+ in.Registers[in.Output] = result
return
}
@@ -1908,12 +1912,12 @@ func uint64ToOperands(numbers []uint64) (*Operand, error) {
return result, nil
}
-func opRepeatPK(ctx *common.Context, input []*Operand, registers []*Operand, output int) (err error) {
- tableRef, err := input[0].toTableRef()
+func opRepeatPK(ctx *common.Context, in Instruction) (err error) {
+ tableRef, err := in.Input[0].toTableRef()
if err != nil {
return err
}
IDs := ctx.Storage.RepeatPK(ctx.Contract.Address(), tableRef)
- registers[output], err = uint64ToOperands(IDs)
+ in.Registers[in.Output], err = uint64ToOperands(IDs)
return
}
diff --git a/core/vm/sqlvm/runtime/instructions_test.go b/core/vm/sqlvm/runtime/instructions_test.go
index a00ac12dd..7dcc29449 100644
--- a/core/vm/sqlvm/runtime/instructions_test.go
+++ b/core/vm/sqlvm/runtime/instructions_test.go
@@ -407,7 +407,12 @@ func (s *opLoadSuite) TestOpLoad() {
reg := s.newRegisters(t.tableRef, t.ids, t.fields)
loadRegister(input, reg)
- err := opLoad(s.ctx, input, reg, t.outputIdx)
+ in := Instruction{
+ Input: input,
+ Registers: reg,
+ Output: t.outputIdx,
+ }
+ err := opLoad(s.ctx, in)
s.Require().Equalf(t.expectedErr, err, "testcase: [%v]", t.title)
s.Require().Truef(reflect.DeepEqual(t.expectedOutput, reg[t.outputIdx]),
@@ -474,7 +479,12 @@ func (s opRepeatPKSuite) TestRepeatPK() {
reg := s.newRegisters(t.tableRef)
input := newInput([]int{1})
loadRegister(input, reg)
- err := opRepeatPK(ctx, input, reg, 0)
+ in := Instruction{
+ Input: input,
+ Registers: reg,
+ Output: 0,
+ }
+ err := opRepeatPK(ctx, in)
s.Require().Equalf(t.expectedErr, err, "testcase: [%v]", t.title)
result, _ := reg[0].toUint64()
s.Require().Equalf(t.expectedIDs, result, "testcase: [%v]", t.title)
@@ -513,17 +523,19 @@ type instructionSuite struct {
func (s *instructionSuite) run(testcases []opTestcase, opfunc OpFunction) {
for idx, c := range testcases {
- registers := make([]*Operand, len(c.In.Input))
+ c.In.Registers = make([]*Operand, len(c.In.Input))
for i, j := 0, 0; i < len(c.In.Input); i++ {
if !c.In.Input[i].IsImmediate {
- registers[j] = c.In.Input[i]
+ c.In.Registers[j] = c.In.Input[i]
j++
}
}
+
err := opfunc(
&common.Context{Opt: common.Option{SafeMath: true}},
- c.In.Input, registers, c.In.Output)
+ c.In,
+ )
s.Require().Equal(
c.Err, err,
"idx: %v, op: %v, case: %v\nerror not equal: %v != %v",
@@ -533,7 +545,7 @@ func (s *instructionSuite) run(testcases []opTestcase, opfunc OpFunction) {
continue
}
- result := registers[0]
+ result := c.In.Registers[0]
s.Require().True(
c.Output.Equal(result),
"idx: %v, op: %v, case: %v\noutput not equal.\nExpect: %v\nResult: %v\n",
diff --git a/core/vm/sqlvm/runtime/jumptable.go b/core/vm/sqlvm/runtime/jumptable.go
index 2930520a1..62357c20a 100644
--- a/core/vm/sqlvm/runtime/jumptable.go
+++ b/core/vm/sqlvm/runtime/jumptable.go
@@ -1,39 +1,128 @@
package runtime
-var jumpTable = [256]OpFunction{
+type jumpUnit struct {
+ Func OpFunction
+ GasFunc GasFunction
+}
+
+var jumpTable = [256]jumpUnit{
// 0x10
- ADD: opAdd,
- MUL: opMul,
- SUB: opSub,
- DIV: opDiv,
- MOD: opMod,
- CONCAT: opConcat,
- NEG: opNeg,
+ ADD: {
+ Func: opAdd,
+ GasFunc: constGasFunc(GasArithAdd),
+ },
+ MUL: {
+ Func: opMul,
+ GasFunc: constGasFunc(GasArithMul),
+ },
+ SUB: {
+ Func: opSub,
+ GasFunc: constGasFunc(GasArithAdd),
+ },
+ DIV: {
+ Func: opDiv,
+ GasFunc: constGasFunc(GasArithMul),
+ },
+ MOD: {
+ Func: opMod,
+ GasFunc: constGasFunc(GasArithMul),
+ },
+ CONCAT: {
+ Func: opConcat,
+ GasFunc: constGasFunc(GasMemAlloc),
+ },
+ NEG: {
+ Func: opNeg,
+ GasFunc: constGasFunc(GasArithAdd),
+ },
// 0x20
- LT: opLt,
- GT: opGt,
- EQ: opEq,
- AND: opAnd,
- OR: opOr,
- NOT: opNot,
- UNION: opUnion,
- INTXN: opIntxn,
- LIKE: opLike,
+ LT: {
+ Func: opLt,
+ GasFunc: constGasFunc(GasArithCmp),
+ },
+ GT: {
+ Func: opGt,
+ GasFunc: constGasFunc(GasArithCmp),
+ },
+ EQ: {
+ Func: opEq,
+ GasFunc: constGasFunc(GasArithCmp),
+ },
+ AND: {
+ Func: opAnd,
+ GasFunc: constGasFunc(GasBoolCmp),
+ },
+ OR: {
+ Func: opOr,
+ GasFunc: constGasFunc(GasBoolCmp),
+ },
+ NOT: {
+ Func: opNot,
+ GasFunc: constGasFunc(GasBoolCmp),
+ },
+ UNION: {
+ Func: opUnion,
+ GasFunc: constGasFunc(GasMemAlloc + GasMemSwap),
+ },
+ INTXN: {
+ Func: opIntxn,
+ GasFunc: constGasFunc(GasMemAlloc + GasMemSwap),
+ },
+ LIKE: {
+ Func: opLike,
+ GasFunc: constGasFunc(GasMemSearch),
+ },
+
+ // 0x30
+ REPEATPK: {
+ Func: opRepeatPK,
+ GasFunc: constGasFunc(GasStorageRead),
+ },
// 0x40
- ZIP: opZip,
- FIELD: opField,
- PRUNE: opPrune,
- SORT: opSort,
- FILTER: opFilter,
- CAST: opCast,
- CUT: opCut,
- RANGE: opRange,
+ ZIP: {
+ Func: opZip,
+ GasFunc: constGasFunc(GasMemAlloc),
+ },
+ FIELD: {
+ Func: opField,
+ GasFunc: constGasFunc(GasMemAlloc),
+ },
+ PRUNE: {
+ Func: opPrune,
+ GasFunc: constGasFunc(GasMemFree),
+ },
+ SORT: {
+ Func: opSort,
+ GasFunc: constGasFunc(GasMemSwap),
+ },
+ FILTER: {
+ Func: opFilter,
+ GasFunc: constGasFunc(GasMemAlloc),
+ },
+ CAST: {
+ Func: opCast,
+ GasFunc: dummyGasFunc,
+ },
+ CUT: {
+ Func: opCut,
+ GasFunc: constGasFunc(GasMemFree),
+ },
+ RANGE: {
+ Func: opRange,
+ GasFunc: constGasFunc(GasMemFree),
+ },
// 0x50
- FUNC: opFunc,
+ FUNC: {
+ Func: opFunc,
+ GasFunc: dummyGasFunc,
+ },
// 0x60
- LOAD: opLoad,
+ LOAD: {
+ Func: opLoad,
+ GasFunc: constGasFunc(GasStorageRead),
+ },
}
diff --git a/core/vm/sqlvm/runtime/runtime.go b/core/vm/sqlvm/runtime/runtime.go
index 3b6d49a72..f138aad26 100644
--- a/core/vm/sqlvm/runtime/runtime.go
+++ b/core/vm/sqlvm/runtime/runtime.go
@@ -9,12 +9,18 @@ import (
// Run is runtime entrypoint.
func Run(stateDB vm.StateDB, ins []Instruction, registers []*Operand) (ret []byte, err error) {
for _, in := range ins {
+ // load register
for i := 0; i < len(in.Input); i++ {
if !in.Input[i].IsImmediate {
in.Input[i] = registers[in.Input[i].RegisterIndex]
}
}
- errCode := jumpTable[in.Op](&common.Context{}, in.Input, registers, in.Output)
+ in.Registers = registers
+
+ // jump table and run
+ jUnit := jumpTable[in.Op]
+ in.GasFunc = jUnit.GasFunc
+ errCode := jUnit.Func(&common.Context{}, in)
if errCode != nil {
err = se.Error{
Position: in.Position,