OSDN Git Service

the other statements support to operate contract value besides lock/unlock statement...
[bytom/equity.git] / 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
62                 if !used {
63                         return fmt.Errorf("parameter \"%s\" is unused", p.Name)
64                 }
65         }
66         return nil
67 }
68
69 func requireAllParamsUsedInClause(params []*Param, clause *Clause) error {
70         for _, p := range params {
71                 used := false
72                 for _, stmt := range clause.statements {
73                         if used = checkParamUsedInStatement(p, stmt); used {
74                                 break
75                         }
76                 }
77
78                 if !used {
79                         return fmt.Errorf("parameter \"%s\" is unused in clause \"%s\"", p.Name, clause.Name)
80                 }
81         }
82         return nil
83 }
84
85 func checkParamUsedInStatement(param *Param, stmt statement) (used bool) {
86         switch s := stmt.(type) {
87         case *ifStatement:
88                 if used = references(s.condition, param.Name); used {
89                         return used
90                 }
91
92                 for _, st := range s.body.trueBody {
93                         if used = checkParamUsedInStatement(param, st); used {
94                                 break
95                         }
96                 }
97
98                 if !used {
99                         for _, st := range s.body.falseBody {
100                                 if used = checkParamUsedInStatement(param, st); used {
101                                         break
102                                 }
103                         }
104                 }
105
106         case *defineStatement:
107                 used = references(s.expr, param.Name)
108         case *assignStatement:
109                 used = references(s.expr, param.Name)
110         case *verifyStatement:
111                 used = references(s.expr, param.Name)
112         case *lockStatement:
113                 used = references(s.lockedAmount, param.Name) || references(s.lockedAsset, param.Name) || references(s.program, param.Name)
114         case *unlockStatement:
115                 used = references(s.unlockedAmount, param.Name) || references(s.unlockedAsset, param.Name)
116         }
117
118         return used
119 }
120
121 func references(expr expression, name string) bool {
122         switch e := expr.(type) {
123         case *binaryExpr:
124                 return references(e.left, name) || references(e.right, name)
125         case *unaryExpr:
126                 return references(e.expr, name)
127         case *callExpr:
128                 if references(e.fn, name) {
129                         return true
130                 }
131                 for _, a := range e.args {
132                         if references(a, name) {
133                                 return true
134                         }
135                 }
136                 return false
137         case varRef:
138                 return string(e) == name
139         case listExpr:
140                 for _, elt := range []expression(e) {
141                         if references(elt, name) {
142                                 return true
143                         }
144                 }
145                 return false
146         }
147         return false
148 }
149
150 func referencedBuiltin(expr expression) *builtin {
151         if v, ok := expr.(varRef); ok {
152                 for _, b := range builtins {
153                         if string(v) == b.name {
154                                 return &b
155                         }
156                 }
157         }
158         return nil
159 }
160
161 func countsVarRef(stat statement, counts map[string]int) map[string]int {
162         if stmt, ok := stat.(*defineStatement); ok && stmt.expr == nil {
163                 return counts
164         }
165
166         if _, ok := stat.(*unlockStatement); ok {
167                 return counts
168         }
169
170         stat.countVarRefs(counts)
171         if stmt, ok := stat.(*ifStatement); ok {
172                 for _, trueStmt := range stmt.body.trueBody {
173                         counts = countsVarRef(trueStmt, counts)
174                 }
175
176                 for _, falseStmt := range stmt.body.falseBody {
177                         counts = countsVarRef(falseStmt, counts)
178                 }
179         }
180
181         return counts
182 }
183
184 func assignIndexes(clause *Clause) error {
185         var nextIndex int64
186         for i, stmt := range clause.statements {
187                 if nextIndex = assignStatIndexes(stmt, nextIndex, i != len(clause.statements)-1); nextIndex < 0 {
188                         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)
189                 }
190         }
191
192         return nil
193 }
194
195 func assignStatIndexes(stat statement, nextIndex int64, nonFinalFlag bool) int64 {
196         switch stmt := stat.(type) {
197         case *ifStatement:
198                 trueIndex := nextIndex
199                 falseIndex := nextIndex
200                 for _, trueStmt := range stmt.body.trueBody {
201                         trueIndex = assignStatIndexes(trueStmt, trueIndex, nonFinalFlag)
202                 }
203
204                 for _, falseStmt := range stmt.body.falseBody {
205                         falseIndex = assignStatIndexes(falseStmt, falseIndex, nonFinalFlag)
206                 }
207
208                 if trueIndex != falseIndex && nonFinalFlag {
209                         return -1
210                 } else if trueIndex == falseIndex {
211                         nextIndex = trueIndex
212                 }
213
214         case *lockStatement:
215                 stmt.index = nextIndex
216                 nextIndex++
217
218         case *unlockStatement:
219                 nextIndex++
220         }
221
222         return nextIndex
223 }
224
225 func typeCheckClause(contract *Contract, clause *Clause, env *environ) error {
226         for _, s := range clause.statements {
227                 if err := typeCheckStatement(s, contract.Value, clause.Name, env); err != nil {
228                         return err
229                 }
230         }
231         return nil
232 }
233
234 func typeCheckStatement(stat statement, contractValue ValueInfo, clauseName string, env *environ) error {
235         switch stmt := stat.(type) {
236         case *ifStatement:
237                 for _, trueStmt := range stmt.body.trueBody {
238                         if err := typeCheckStatement(trueStmt, contractValue, clauseName, env); err != nil {
239                                 return err
240                         }
241                 }
242
243                 for _, falseStmt := range stmt.body.falseBody {
244                         if err := typeCheckStatement(falseStmt, contractValue, clauseName, env); err != nil {
245                                 return err
246                         }
247                 }
248
249         case *defineStatement:
250                 if stmt.expr != nil && stmt.expr.typ(env) != stmt.variable.Type && !isHashSubtype(stmt.expr.typ(env)) {
251                         return fmt.Errorf("expression in define statement in clause \"%s\" has type \"%s\", must be \"%s\"",
252                                 clauseName, stmt.expr.typ(env), stmt.variable.Type)
253                 }
254
255         case *assignStatement:
256                 if stmt.expr.typ(env) != stmt.variable.Type && !isHashSubtype(stmt.expr.typ(env)) {
257                         return fmt.Errorf("expression in assign statement in clause \"%s\" has type \"%s\", must be \"%s\"",
258                                 clauseName, stmt.expr.typ(env), stmt.variable.Type)
259                 }
260
261         case *verifyStatement:
262                 if t := stmt.expr.typ(env); t != boolType {
263                         return fmt.Errorf("expression in verify statement in clause \"%s\" has type \"%s\", must be Boolean", clauseName, t)
264                 }
265
266         case *lockStatement:
267                 if t := stmt.lockedAmount.typ(env); !(t == intType || t == amountType) {
268                         return fmt.Errorf("lockedAmount expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Integer", stmt.lockedAmount, clauseName, t)
269                 }
270                 if t := stmt.lockedAsset.typ(env); t != assetType {
271                         return fmt.Errorf("lockedAsset expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Asset", stmt.lockedAsset, clauseName, t)
272                 }
273                 if t := stmt.program.typ(env); t != progType {
274                         return fmt.Errorf("program in lock statement in clause \"%s\" has type \"%s\", must be Program", clauseName, t)
275                 }
276
277         case *unlockStatement:
278                 if t := stmt.unlockedAmount.typ(env); !(t == intType || t == amountType) {
279                         return fmt.Errorf("unlockedAmount expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Integer", stmt.unlockedAmount, clauseName, t)
280                 }
281                 if t := stmt.unlockedAsset.typ(env); t != assetType {
282                         return fmt.Errorf("unlockedAsset expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Asset", stmt.unlockedAsset, clauseName, t)
283                 }
284                 if stmt.unlockedAmount.String() != contractValue.Amount || stmt.unlockedAsset.String() != contractValue.Asset {
285                         return fmt.Errorf("amount \"%s\" of asset \"%s\" expression in unlock statement of clause \"%s\" must be the contract valueAmount \"%s\" of valueAsset \"%s\"",
286                                 stmt.unlockedAmount.String(), stmt.unlockedAsset.String(), clauseName, contractValue.Amount, contractValue.Asset)
287                 }
288         }
289
290         return nil
291 }