OSDN Git Service

Merge pull request #41 from Bytom/dev
[bytom/vapor.git] / equity / compiler / checks.go
1 package compiler
2
3 import "fmt"
4
5 func checkRecursive(contract *Contract) bool {
6         for _, clause := range contract.Clauses {
7                 for _, stmt := range clause.statements {
8                         if result := checkStatRecursive(stmt, contract.Name); result {
9                                 return true
10                         }
11                 }
12         }
13         return false
14 }
15
16 func checkStatRecursive(stmt statement, contractName string) bool {
17         switch s := stmt.(type) {
18         case *ifStatement:
19                 for _, trueStmt := range s.body.trueBody {
20                         if result := checkStatRecursive(trueStmt, contractName); result {
21                                 return true
22                         }
23                 }
24
25                 for _, falseStmt := range s.body.falseBody {
26                         if result := checkStatRecursive(falseStmt, contractName); result {
27                                 return true
28                         }
29                 }
30
31         case *lockStatement:
32                 if c, ok := s.program.(*callExpr); ok {
33                         if references(c.fn, contractName) {
34                                 return true
35                         }
36                 }
37         }
38
39         return false
40 }
41
42 func prohibitSigParams(contract *Contract) error {
43         for _, p := range contract.Params {
44                 if p.Type == sigType {
45                         return fmt.Errorf("contract parameter \"%s\" has type Signature, but contract parameters cannot have type Signature", p.Name)
46                 }
47         }
48         return nil
49 }
50
51 func requireAllParamsUsedInClauses(params []*Param, clauses []*Clause) error {
52         for _, p := range params {
53                 used := false
54                 for _, c := range clauses {
55                         err := requireAllParamsUsedInClause([]*Param{p}, c)
56                         if err == nil {
57                                 used = true
58                                 break
59                         }
60                 }
61                 if !used {
62                         return fmt.Errorf("parameter \"%s\" is unused", p.Name)
63                 }
64         }
65         return nil
66 }
67
68 func requireAllParamsUsedInClause(params []*Param, clause *Clause) error {
69         for _, p := range params {
70                 used := false
71                 for _, stmt := range clause.statements {
72                         if used = checkParamUsedInStatement(p, stmt); used {
73                                 break
74                         }
75                 }
76
77                 if !used {
78                         return fmt.Errorf("parameter \"%s\" is unused in clause \"%s\"", p.Name, clause.Name)
79                 }
80         }
81         return nil
82 }
83
84 func checkParamUsedInStatement(param *Param, stmt statement) (used bool) {
85         switch s := stmt.(type) {
86         case *ifStatement:
87                 if used = references(s.condition, param.Name); used {
88                         return used
89                 }
90
91                 for _, st := range s.body.trueBody {
92                         if used = checkParamUsedInStatement(param, st); used {
93                                 break
94                         }
95                 }
96
97                 if !used {
98                         for _, st := range s.body.falseBody {
99                                 if used = checkParamUsedInStatement(param, st); used {
100                                         break
101                                 }
102                         }
103                 }
104
105         case *defineStatement:
106                 used = references(s.expr, param.Name)
107         case *assignStatement:
108                 used = references(s.expr, param.Name)
109         case *verifyStatement:
110                 used = references(s.expr, param.Name)
111         case *lockStatement:
112                 used = references(s.lockedAmount, param.Name) || references(s.lockedAsset, param.Name) || references(s.program, param.Name)
113         case *unlockStatement:
114                 used = references(s.unlockedAmount, param.Name) || references(s.unlockedAsset, param.Name)
115         }
116
117         return used
118 }
119
120 func references(expr expression, name string) bool {
121         switch e := expr.(type) {
122         case *binaryExpr:
123                 return references(e.left, name) || references(e.right, name)
124         case *unaryExpr:
125                 return references(e.expr, name)
126         case *callExpr:
127                 if references(e.fn, name) {
128                         return true
129                 }
130                 for _, a := range e.args {
131                         if references(a, name) {
132                                 return true
133                         }
134                 }
135                 return false
136         case varRef:
137                 return string(e) == name
138         case listExpr:
139                 for _, elt := range []expression(e) {
140                         if references(elt, name) {
141                                 return true
142                         }
143                 }
144                 return false
145         }
146         return false
147 }
148
149 func referencedBuiltin(expr expression) *builtin {
150         if v, ok := expr.(varRef); ok {
151                 for _, b := range builtins {
152                         if string(v) == b.name {
153                                 return &b
154                         }
155                 }
156         }
157         return nil
158 }
159
160 func assignIndexes(clause *Clause) error {
161         var nextIndex int64
162         for i, stmt := range clause.statements {
163                 if nextIndex = assignStatIndexes(stmt, nextIndex, i != len(clause.statements)-1); nextIndex < 0 {
164                         return fmt.Errorf("Not support that the number of lock/unlock statement is not equal between ifbody and elsebody when the if-else is not the last statement in clause \"%s\"", clause.Name)
165                 }
166         }
167
168         return nil
169 }
170
171 func assignStatIndexes(stat statement, nextIndex int64, nonFinalFlag bool) int64 {
172         switch stmt := stat.(type) {
173         case *ifStatement:
174                 trueIndex := nextIndex
175                 falseIndex := nextIndex
176                 for _, trueStmt := range stmt.body.trueBody {
177                         trueIndex = assignStatIndexes(trueStmt, trueIndex, nonFinalFlag)
178                 }
179
180                 for _, falseStmt := range stmt.body.falseBody {
181                         falseIndex = assignStatIndexes(falseStmt, falseIndex, nonFinalFlag)
182                 }
183
184                 if trueIndex != falseIndex && nonFinalFlag {
185                         return -1
186                 } else if trueIndex == falseIndex {
187                         nextIndex = trueIndex
188                 }
189
190         case *lockStatement:
191                 stmt.index = nextIndex
192                 nextIndex++
193
194         case *unlockStatement:
195                 nextIndex++
196         }
197
198         return nextIndex
199 }
200
201 func typeCheckClause(contract *Contract, clause *Clause, env *environ) error {
202         for _, s := range clause.statements {
203                 if err := typeCheckStatement(s, contract.Value, clause.Name, env); err != nil {
204                         return err
205                 }
206         }
207         return nil
208 }
209
210 func typeCheckStatement(stat statement, contractValue ValueInfo, clauseName string, env *environ) error {
211         switch stmt := stat.(type) {
212         case *ifStatement:
213                 for _, trueStmt := range stmt.body.trueBody {
214                         if err := typeCheckStatement(trueStmt, contractValue, clauseName, env); err != nil {
215                                 return err
216                         }
217                 }
218
219                 for _, falseStmt := range stmt.body.falseBody {
220                         if err := typeCheckStatement(falseStmt, contractValue, clauseName, env); err != nil {
221                                 return err
222                         }
223                 }
224
225         case *defineStatement:
226                 if stmt.expr != nil && stmt.expr.typ(env) != stmt.variable.Type && !isHashSubtype(stmt.expr.typ(env)) {
227                         return fmt.Errorf("expression in define statement in clause \"%s\" has type \"%s\", must be \"%s\"",
228                                 clauseName, stmt.expr.typ(env), stmt.variable.Type)
229                 }
230
231         case *assignStatement:
232                 if stmt.expr.typ(env) != stmt.variable.Type && !isHashSubtype(stmt.expr.typ(env)) {
233                         return fmt.Errorf("expression in assign statement in clause \"%s\" has type \"%s\", must be \"%s\"",
234                                 clauseName, stmt.expr.typ(env), stmt.variable.Type)
235                 }
236
237         case *verifyStatement:
238                 if t := stmt.expr.typ(env); t != boolType {
239                         return fmt.Errorf("expression in verify statement in clause \"%s\" has type \"%s\", must be Boolean", clauseName, t)
240                 }
241
242         case *lockStatement:
243                 if t := stmt.lockedAmount.typ(env); !(t == intType || t == amountType) {
244                         return fmt.Errorf("lockedAmount expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Integer", stmt.lockedAmount, clauseName, t)
245                 }
246                 if t := stmt.lockedAsset.typ(env); t != assetType {
247                         return fmt.Errorf("lockedAsset expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Asset", stmt.lockedAsset, clauseName, t)
248                 }
249                 if t := stmt.program.typ(env); t != progType {
250                         return fmt.Errorf("program in lock statement in clause \"%s\" has type \"%s\", must be Program", clauseName, t)
251                 }
252
253         case *unlockStatement:
254                 if t := stmt.unlockedAmount.typ(env); !(t == intType || t == amountType) {
255                         return fmt.Errorf("unlockedAmount expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Integer", stmt.unlockedAmount, clauseName, t)
256                 }
257                 if t := stmt.unlockedAsset.typ(env); t != assetType {
258                         return fmt.Errorf("unlockedAsset expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Asset", stmt.unlockedAsset, clauseName, t)
259                 }
260                 if stmt.unlockedAmount.String() != contractValue.Amount || stmt.unlockedAsset.String() != contractValue.Asset {
261                         return fmt.Errorf("amount \"%s\" of asset \"%s\" expression in unlock statement of clause \"%s\" must be the contract valueAmount \"%s\" of valueAsset \"%s\"",
262                                 stmt.unlockedAmount.String(), stmt.unlockedAsset.String(), clauseName, contractValue.Amount, contractValue.Asset)
263                 }
264         }
265
266         return nil
267 }