@@ -12,6 +12,7 @@ import (
12
12
"strings"
13
13
14
14
"github.com/gnolang/gno/gnovm"
15
+ storetypes "github.com/gnolang/gno/tm2/pkg/store/types"
15
16
"go.uber.org/multierr"
16
17
)
17
18
@@ -23,26 +24,40 @@ type MemPackageGetter interface {
23
24
GetMemPackage (path string ) * gnovm.MemPackage
24
25
}
25
26
27
+ const DEFAULT_MAX_GAS_UGNOT = 1_000_000 // 1Gnot aka 1e6 ugnots
28
+
26
29
// TypeCheckMemPackage performs type validation and checking on the given
27
30
// mempkg. To retrieve dependencies, it uses getter.
28
31
//
29
32
// The syntax checking is performed entirely using Go's go/types package.
30
33
//
31
34
// If format is true, the code will be automatically updated with the
32
35
// formatted source code.
36
+ //
37
+ // By default is uses a gas meter of `DEFAULT_MAX_GAS_UGNOT`.
33
38
func TypeCheckMemPackage (mempkg * gnovm.MemPackage , getter MemPackageGetter , format bool ) error {
34
- return typeCheckMemPackage (mempkg , getter , false , format )
39
+ return typeCheckMemPackage (mempkg , getter , false , format , newDefaultGasMeter ())
40
+ }
41
+
42
+ // TypeCheckMemPackageWithGasMeter is like TypeCheckMemPackage, except
43
+ // that it allows passing in the gas meter to use.
44
+ func TypeCheckMemPackageWithGasMeter (mempkg * gnovm.MemPackage , getter MemPackageGetter , format bool , gasMeter storetypes.GasMeter ) error {
45
+ return typeCheckMemPackage (mempkg , getter , false , format , gasMeter )
35
46
}
36
47
37
48
// TypeCheckMemPackageTest performs the same type checks as [TypeCheckMemPackage],
38
49
// but allows re-declarations.
39
50
//
40
51
// Note: like TypeCheckMemPackage, this function ignores tests and filetests.
41
52
func TypeCheckMemPackageTest (mempkg * gnovm.MemPackage , getter MemPackageGetter ) error {
42
- return typeCheckMemPackage (mempkg , getter , true , false )
53
+ return typeCheckMemPackage (mempkg , getter , true , false , newDefaultGasMeter () )
43
54
}
44
55
45
- func typeCheckMemPackage (mempkg * gnovm.MemPackage , getter MemPackageGetter , testing , format bool ) error {
56
+ func newDefaultGasMeter () storetypes.GasMeter {
57
+ return storetypes .NewGasMeter (DEFAULT_MAX_GAS_UGNOT )
58
+ }
59
+
60
+ func typeCheckMemPackage (mempkg * gnovm.MemPackage , getter MemPackageGetter , testing , format bool , gasMeter storetypes.GasMeter ) error {
46
61
var errs error
47
62
imp := & gnoImporter {
48
63
getter : getter ,
@@ -53,6 +68,7 @@ func typeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, test
53
68
},
54
69
},
55
70
allowRedefinitions : testing ,
71
+ gasMeter : gasMeter ,
56
72
}
57
73
imp .cfg .Importer = imp
58
74
@@ -77,6 +93,7 @@ type gnoImporter struct {
77
93
78
94
// allow symbol redefinitions? (test standard libraries)
79
95
allowRedefinitions bool
96
+ gasMeter storetypes.GasMeter
80
97
}
81
98
82
99
// Unused, but satisfies the Importer interface.
@@ -136,7 +153,7 @@ func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt bool) (*t
136
153
continue
137
154
}
138
155
139
- // m. chargeGasForTypecheck(f)
156
+ chargeGasForTypecheck (g . gasMeter , f )
140
157
141
158
if delFunc != nil {
142
159
deleteOldIdents (delFunc , f )
@@ -162,64 +179,107 @@ func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt bool) (*t
162
179
return g .cfg .Check (mpkg .Path , fset , files , nil )
163
180
}
164
181
165
- func (m * Machine ) chargeGasForTypecheck (f * ast.File ) {
166
- ast .Walk (& astTraversingGasCharger {m }, f )
182
+ func deleteOldIdents (idents map [string ]func (), f * ast.File ) {
183
+ for _ , decl := range f .Decls {
184
+ fd , ok := decl .(* ast.FuncDecl )
185
+ if ! ok || fd .Recv != nil { // ignore methods
186
+ continue
187
+ }
188
+ if del := idents [fd .Name .Name ]; del != nil {
189
+ del ()
190
+ }
191
+ decl := decl
192
+ idents [fd .Name .Name ] = func () {
193
+ // NOTE: cannot use the index as a file may contain multiple decls to be removed,
194
+ // so removing one would make all "later" indexes wrong.
195
+ f .Decls = slices .DeleteFunc (f .Decls , func (d ast.Decl ) bool { return decl == d })
196
+ }
197
+ }
198
+ }
199
+
200
+ func chargeGasForTypecheck (gasMeter storetypes.GasMeter , f * ast.File ) {
201
+ ast .Walk (& astTraversingGasCharger {gasMeter }, f )
167
202
}
168
203
169
204
// astTraversingGasCharger is an ast.Visitor helper that statically traverses an AST
170
205
// charging gas for the respective typechecking operations so as to bear a cost
171
206
// and not let typechecking be abused
172
207
type astTraversingGasCharger struct {
173
- m * Machine
208
+ m storetypes. GasMeter
174
209
}
175
210
176
211
var _ ast.Visitor = (* astTraversingGasCharger )(nil )
177
212
213
+ func (atgc * astTraversingGasCharger ) consumeGas (amount storetypes.Gas ) {
214
+ atgc .m .ConsumeGas (amount , "typeCheck" )
215
+ }
216
+
217
+ const _BASIC_TYPECHECK_GAS_CHARGE = 5 // Arbitrary value.
218
+
178
219
func (wmv * astTraversingGasCharger ) Visit (n ast.Node ) ast.Visitor {
179
- switch n := n .(type ) {
220
+ switch n .(type ) {
180
221
case * ast.ImportSpec :
222
+ // No need to charge gas for imports.
181
223
return nil
182
224
225
+ case * ast.UnaryExpr :
226
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 2 )
227
+
183
228
case * ast.BinaryExpr :
184
- // TODO: Charge more gas for the operands that aren't constant.
185
- opCode := word2UnaryOp (Word (n .Op ))
186
- wmv .m .incrCPU (opCode .cpuCycles ())
187
- wmv .m .incrCPU (OpCPUBinary1 )
188
- return wmv
229
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 3 )
189
230
190
- case * ast.UnaryExpr :
191
- opCode := word2BinaryOp (Word (n .Op ))
192
- wmv .m .incrCPU (opCode .cpuCycles ())
193
- // TODO: Charge gas for the expression itself too?
231
+ case * ast.BasicLit :
232
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 2 )
233
+
234
+ case * ast.CompositeLit :
235
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 3 )
194
236
195
237
case * ast.CallExpr :
196
- wmv .m . incrCPU ( OpCall . cpuCycles () )
238
+ wmv .consumeGas ( _BASIC_TYPECHECK_GAS_CHARGE * 4 )
197
239
198
240
case * ast.ForStmt :
199
- wmv .m . incrCPU ( OpCPUForLoop )
241
+ wmv .consumeGas ( _BASIC_TYPECHECK_GAS_CHARGE * 5 )
200
242
201
243
case * ast.RangeStmt :
202
- wmv .m . incrCPU ( OpRangeIter )
244
+ wmv .consumeGas ( _BASIC_TYPECHECK_GAS_CHARGE * 6 )
203
245
// TODO: Alternate on the different type of range statements.
204
- }
205
246
206
- return wmv
207
- }
247
+ case * ast. FuncDecl :
248
+ wmv . consumeGas ( _BASIC_TYPECHECK_GAS_CHARGE * 6 )
208
249
209
- func deleteOldIdents (idents map [string ]func (), f * ast.File ) {
210
- for _ , decl := range f .Decls {
211
- fd , ok := decl .(* ast.FuncDecl )
212
- if ! ok || fd .Recv != nil { // ignore methods
213
- continue
214
- }
215
- if del := idents [fd .Name .Name ]; del != nil {
216
- del ()
217
- }
218
- decl := decl
219
- idents [fd .Name .Name ] = func () {
220
- // NOTE: cannot use the index as a file may contain multiple decls to be removed,
221
- // so removing one would make all "later" indexes wrong.
222
- f .Decls = slices .DeleteFunc (f .Decls , func (d ast.Decl ) bool { return decl == d })
223
- }
250
+ case * ast.SwitchStmt :
251
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 4 )
252
+
253
+ case * ast.IfStmt :
254
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 5 )
255
+
256
+ case * ast.CaseClause :
257
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 3 )
258
+
259
+ case * ast.BranchStmt :
260
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 3 )
261
+
262
+ case * ast.AssignStmt :
263
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 2 )
264
+
265
+ case * ast.Ident :
266
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 1 )
267
+
268
+ case * ast.SelectorExpr :
269
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 5 )
270
+
271
+ case * ast.ParenExpr :
272
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 3 )
273
+
274
+ case * ast.ReturnStmt , * ast.DeferStmt :
275
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 2 )
276
+
277
+ case nil :
278
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE / 2 )
279
+
280
+ default :
281
+ wmv .consumeGas (_BASIC_TYPECHECK_GAS_CHARGE * 3 )
224
282
}
283
+
284
+ return wmv
225
285
}
0 commit comments