"fmt"
"io"
"io/ioutil"
- "strings"
chainjson "github.com/bytom/encoding/json"
"github.com/bytom/errors"
return err
}
}
- assignIndexes(clause)
+
+ if err = assignIndexes(clause); err != nil {
+ return err
+ }
var stk stack
for _, p := range clause.Params {
// a count of the number of times each variable is referenced
counts := make(map[string]int)
for _, s := range clause.statements {
+ if stmt, ok := s.(*defineStatement); ok && stmt.expr == nil {
+ continue
+ }
+
s.countVarRefs(counts)
if stmt, ok := s.(*ifStatement); ok {
for _, trueStmt := range stmt.body.trueBody {
st.countVarRefs(counts)
}
+ // modify value amount because of using only once
+ if counts[contract.Value.Amount] > 1 {
+ counts[contract.Value.Amount] = 1
+ }
+
+ // modify value asset because of using only once
+ if counts[contract.Value.Asset] > 1 {
+ counts[contract.Value.Asset] = 1
+ }
+
for _, st := range stmt.body.trueBody {
if stk, err = compileStatement(b, stk, contract, env, clause, counts, st, sequence); err != nil {
return stk, err
st.countVarRefs(counts)
}
+ // modify value amount because of using only once
+ if counts[contract.Value.Amount] > 1 {
+ counts[contract.Value.Amount] = 1
+ }
+
+ // modify value asset because of using only once
+ if counts[contract.Value.Asset] > 1 {
+ counts[contract.Value.Asset] = 1
+ }
+
stk = condStk
b.addJump(stk, "endif_"+strSequence)
b.addJumpTarget(stk, "else_"+strSequence)
b.addJumpTarget(stk, "endif_"+strSequence)
case *defineStatement:
+ // add environ for define variable
+ if err = env.add(stmt.variable.Name, stmt.variable.Type, roleClauseVariable); err != nil {
+ return stk, err
+ }
+
+ // check whether the variable is used or not
+ if counts[stmt.variable.Name] == 0 {
+ return stk, fmt.Errorf("the defined variable \"%s\" is unused in clause \"%s\"", stmt.variable.Name, clause.Name)
+ }
+
+ if stmt.expr != nil {
+ // variable
+ stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
+ if err != nil {
+ return stk, errors.Wrapf(err, "in define statement in clause \"%s\"", clause.Name)
+ }
+
+ // modify stack name
+ stk.str = stmt.variable.Name
+ }
+
+ case *assignStatement:
+ // find variable from environ with roleClauseVariable
+ if entry := env.lookup(string(stmt.variable.Name)); entry != nil {
+ if entry.r != roleClauseVariable {
+ return stk, fmt.Errorf("the type of variable is not roleClauseVariable in assign statement in clause \"%s\"", clause.Name)
+ }
+ stmt.variable.Type = entry.t
+ } else {
+ return stk, fmt.Errorf("the variable \"%s\" is not defined before the assign statement in clause \"%s\"", stmt.variable.Name, clause.Name)
+ }
+
+ // temporary store the counts of defined variable
+ varCount := counts[stmt.variable.Name]
+
+ // calculate the counts of variable for assign statement
+ tmpCounts := make(map[string]int)
+ stmt.countVarRefs(tmpCounts)
+
+ // modify the map counts of defined variable to 1 and minus the number of defined variable
+ // when the assign expression contains the defined variable
+ if tmpCounts[stmt.variable.Name] > 0 {
+ counts[stmt.variable.Name] = 1
+ varCount -= tmpCounts[stmt.variable.Name]
+ } else {
+ depth := stk.find(stmt.variable.Name)
+ switch depth {
+ case 0:
+ break
+ case 1:
+ stk = b.addSwap(stk)
+ default:
+ stk = b.addRoll(stk, depth)
+ }
+ stk = b.addDrop(stk)
+ }
+
// variable
stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
if err != nil {
return stk, errors.Wrapf(err, "in define statement in clause \"%s\"", clause.Name)
}
- // check variable type
- if stmt.expr.typ(env) != stmt.varName.Type {
- return stk, fmt.Errorf("expression in define statement in clause \"%s\" has type \"%s\", must be \"%s\"",
- clause.Name, stmt.expr.typ(env), stmt.varName.Type)
- }
+ // restore the defined variable counts
+ counts[stmt.variable.Name] = varCount
// modify stack name
- stk.str = stmt.varName.Name
-
- // add environ for define variable
- if err = env.add(stmt.varName.Name, stmt.varName.Type, roleClauseVariable); err != nil {
- return stk, err
- }
+ stk.str = stmt.variable.Name
case *verifyStatement:
stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
stk = b.addAmount(stk, contract.Value.Amount)
stk = b.addAsset(stk, contract.Value.Asset)
} else {
- if strings.Contains(stmt.lockedAmount.String(), contract.Value.Amount) {
- stk = b.addAmount(stk, contract.Value.Amount)
- }
-
- if strings.Contains(stmt.lockedAsset.String(), contract.Value.Asset) {
- stk = b.addAsset(stk, contract.Value.Asset)
- }
+ // calculate the counts of variable for lockStatement
+ lockCounts := make(map[string]int)
+ stmt.countVarRefs(lockCounts)
// amount
- stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAmount)
- if err != nil {
- return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
+ switch {
+ case stmt.lockedAmount.String() == contract.Value.Amount:
+ stk = b.addAmount(stk, contract.Value.Amount)
+ case stmt.lockedAmount.String() != contract.Value.Amount && lockCounts[contract.Value.Amount] > 0:
+ stk = b.addAmount(stk, contract.Value.Amount)
+ stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAmount)
+ if err != nil {
+ return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
+ }
+ default:
+ stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAmount)
+ if err != nil {
+ return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
+ }
}
// asset
- stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAsset)
- if err != nil {
- return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
+ switch {
+ case stmt.lockedAsset.String() == contract.Value.Asset:
+ stk = b.addAsset(stk, contract.Value.Asset)
+ case stmt.lockedAsset.String() != contract.Value.Asset && lockCounts[contract.Value.Asset] > 0:
+ stk = b.addAsset(stk, contract.Value.Asset)
+ stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAsset)
+ if err != nil {
+ return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
+ }
+ default:
+ stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAsset)
+ if err != nil {
+ return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
+ }
}
}