OSDN Git Service

cp code from bytom repo
[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 l, ok := stmt.(*lockStatement); ok {
9                                 if c, ok := l.program.(*callExpr); ok {
10                                         if references(c.fn, contract.Name) {
11                                                 return true
12                                         }
13                                 }
14                         }
15                 }
16         }
17         return false
18 }
19
20 func prohibitSigParams(contract *Contract) error {
21         for _, p := range contract.Params {
22                 if p.Type == sigType {
23                         return fmt.Errorf("contract parameter \"%s\" has type Signature, but contract parameters cannot have type Signature", p.Name)
24                 }
25         }
26         return nil
27 }
28
29 func prohibitValueParams(contract *Contract) error {
30         for _, p := range contract.Params {
31                 if p.Type == valueType {
32                         return fmt.Errorf("Value-typed contract parameter \"%s\" must appear in a \"locks\" clause", p.Name)
33                 }
34         }
35         for _, c := range contract.Clauses {
36                 for _, p := range c.Params {
37                         if p.Type == valueType {
38                                 return fmt.Errorf("Value-typed parameter \"%s\" of clause \"%s\" must appear in a \"requires\" clause", p.Name, c.Name)
39                         }
40                 }
41         }
42         return nil
43 }
44
45 func requireAllParamsUsedInClauses(params []*Param, clauses []*Clause) error {
46         for _, p := range params {
47                 used := false
48                 for _, c := range clauses {
49                         err := requireAllParamsUsedInClause([]*Param{p}, c)
50                         if err == nil {
51                                 used = true
52                                 break
53                         }
54                 }
55                 if !used {
56                         return fmt.Errorf("parameter \"%s\" is unused", p.Name)
57                 }
58         }
59         return nil
60 }
61
62 func requireAllParamsUsedInClause(params []*Param, clause *Clause) error {
63         for _, p := range params {
64                 used := false
65                 for _, stmt := range clause.statements {
66                         switch s := stmt.(type) {
67                         case *verifyStatement:
68                                 used = references(s.expr, p.Name)
69                         case *lockStatement:
70                                 used = references(s.locked, p.Name) || references(s.program, p.Name)
71                         case *unlockStatement:
72                                 used = references(s.expr, p.Name)
73                         }
74                         if used {
75                                 break
76                         }
77                 }
78                 if !used {
79                         for _, r := range clause.Reqs {
80                                 if references(r.amountExpr, p.Name) || references(r.assetExpr, p.Name) {
81                                         used = true
82                                         break
83                                 }
84                         }
85                 }
86                 if !used {
87                         return fmt.Errorf("parameter \"%s\" is unused in clause \"%s\"", p.Name, clause.Name)
88                 }
89         }
90         return nil
91 }
92
93 func references(expr expression, name string) bool {
94         switch e := expr.(type) {
95         case *binaryExpr:
96                 return references(e.left, name) || references(e.right, name)
97         case *unaryExpr:
98                 return references(e.expr, name)
99         case *callExpr:
100                 if references(e.fn, name) {
101                         return true
102                 }
103                 for _, a := range e.args {
104                         if references(a, name) {
105                                 return true
106                         }
107                 }
108                 return false
109         case varRef:
110                 return string(e) == name
111         case listExpr:
112                 for _, elt := range []expression(e) {
113                         if references(elt, name) {
114                                 return true
115                         }
116                 }
117                 return false
118         }
119         return false
120 }
121
122 func requireAllValuesDisposedOnce(contract *Contract, clause *Clause) error {
123         err := valueDisposedOnce(contract.Value, clause)
124         if err != nil {
125                 return err
126         }
127         for _, req := range clause.Reqs {
128                 err = valueDisposedOnce(req.Name, clause)
129                 if err != nil {
130                         return err
131                 }
132         }
133         return nil
134 }
135
136 func valueDisposedOnce(name string, clause *Clause) error {
137         var count int
138         for _, s := range clause.statements {
139                 switch stmt := s.(type) {
140                 case *unlockStatement:
141                         if references(stmt.expr, name) {
142                                 count++
143                         }
144                 case *lockStatement:
145                         if references(stmt.locked, name) {
146                                 count++
147                         }
148                 }
149         }
150         switch count {
151         case 0:
152                 return fmt.Errorf("value \"%s\" not disposed in clause \"%s\"", name, clause.Name)
153         case 1:
154                 return nil
155         default:
156                 return fmt.Errorf("value \"%s\" disposed multiple times in clause \"%s\"", name, clause.Name)
157         }
158 }
159
160 func referencedBuiltin(expr expression) *builtin {
161         if v, ok := expr.(varRef); ok {
162                 for _, b := range builtins {
163                         if string(v) == b.name {
164                                 return &b
165                         }
166                 }
167         }
168         return nil
169 }
170
171 func assignIndexes(clause *Clause) {
172         var nextIndex int64
173         for _, s := range clause.statements {
174                 switch stmt := s.(type) {
175                 case *lockStatement:
176                         stmt.index = nextIndex
177                         nextIndex++
178
179                 case *unlockStatement:
180                         nextIndex++
181                 }
182         }
183 }
184
185 func typeCheckClause(contract *Contract, clause *Clause, env *environ) error {
186         for _, s := range clause.statements {
187                 switch stmt := s.(type) {
188                 case *verifyStatement:
189                         if t := stmt.expr.typ(env); t != boolType {
190                                 return fmt.Errorf("expression in verify statement in clause \"%s\" has type \"%s\", must be Boolean", clause.Name, t)
191                         }
192
193                 case *lockStatement:
194                         if t := stmt.locked.typ(env); t != valueType {
195                                 return fmt.Errorf("expression in lock statement in clause \"%s\" has type \"%s\", must be Value", clause.Name, t)
196                         }
197                         if t := stmt.program.typ(env); t != progType {
198                                 return fmt.Errorf("program in lock statement in clause \"%s\" has type \"%s\", must be Program", clause.Name, t)
199                         }
200
201                 case *unlockStatement:
202                         if t := stmt.expr.typ(env); t != valueType {
203                                 return fmt.Errorf("expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Value", stmt.expr, clause.Name, t)
204                         }
205                         if stmt.expr.String() != contract.Value {
206                                 return fmt.Errorf("expression in unlock statement of clause \"%s\" must be the contract value", clause.Name)
207                         }
208                 }
209         }
210         return nil
211 }