tealrb 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/tealrb/abi.rb +5 -3
- data/lib/tealrb/account.rb +78 -0
- data/lib/tealrb/algod.rb +19 -0
- data/lib/tealrb/app.rb +132 -0
- data/lib/tealrb/app_args.rb +10 -0
- data/lib/tealrb/asset.rb +106 -0
- data/lib/tealrb/box.rb +17 -0
- data/lib/tealrb/byte_opcodes.rb +13 -0
- data/lib/tealrb/contract.rb +401 -127
- data/lib/tealrb/enums.rb +45 -0
- data/lib/tealrb/global.rb +94 -0
- data/lib/tealrb/group_txn.rb +42 -0
- data/lib/tealrb/if_block.rb +17 -24
- data/lib/tealrb/inner_txn.rb +95 -0
- data/lib/tealrb/local.rb +27 -0
- data/lib/tealrb/logs.rb +10 -0
- data/lib/tealrb/maybe_ops.rb +96 -0
- data/lib/tealrb/opcode_type.rb +31 -0
- data/lib/tealrb/opcodes.rb +541 -260
- data/lib/tealrb/rewriters.rb +77 -41
- data/lib/tealrb/scratch.rb +5 -4
- data/lib/tealrb/this_txn.rb +13 -0
- data/lib/tealrb/txn_fields.rb +333 -0
- data/lib/tealrb.rb +48 -18
- metadata +64 -6
- data/lib/tealrb/opcode_modules.rb +0 -883
data/lib/tealrb/contract.rb
CHANGED
@@ -3,91 +3,104 @@
|
|
3
3
|
module TEALrb
|
4
4
|
class Contract
|
5
5
|
include TEALrb
|
6
|
-
include Opcodes
|
7
|
-
include
|
6
|
+
include Opcodes::TEALOpcodes
|
7
|
+
include MaybeOps
|
8
|
+
include ByteOpcodes
|
8
9
|
include ABI
|
9
10
|
include Rewriters
|
11
|
+
include Enums
|
10
12
|
|
11
|
-
|
13
|
+
alias global_opcode global
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
:disable_abi_routing
|
15
|
+
attr_reader :eval_location
|
16
|
+
attr_accessor :teal, :if_count
|
16
17
|
|
17
|
-
|
18
|
+
class << self
|
19
|
+
attr_accessor :subroutines, :version, :abi_interface, :debug,
|
20
|
+
:disable_abi_routing, :method_hashes, :src_map
|
18
21
|
|
19
22
|
def inherited(klass)
|
20
23
|
klass.version = 6
|
21
24
|
klass.subroutines = {}
|
22
|
-
klass.
|
23
|
-
klass.
|
24
|
-
klass.
|
25
|
+
klass.abi_interface = ABI::ABIDescription.new
|
26
|
+
klass.abi_interface.name = klass.to_s
|
27
|
+
klass.method_hashes = []
|
25
28
|
klass.debug = false
|
26
29
|
klass.disable_abi_routing = false
|
30
|
+
klass.src_map = true
|
27
31
|
super
|
28
32
|
end
|
29
|
-
end
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
def parse(klass)
|
35
|
+
YARD::Tags::Library.define_tag('ABI Method', :abi)
|
36
|
+
YARD::Tags::Library.define_tag('Subroutine', :subroutine)
|
37
|
+
YARD::Tags::Library.define_tag('OnCompletes', :on_completion)
|
38
|
+
YARD::Tags::Library.define_tag('Create', :create)
|
39
|
+
|
40
|
+
YARD.parse Object.const_source_location(klass.to_s).first
|
41
|
+
|
42
|
+
parsed_methods = {}
|
43
|
+
YARD::Registry.all.each do |y|
|
44
|
+
next unless y.type == :method
|
45
|
+
next unless klass.instance_methods.include? y.name
|
46
|
+
next if y.parent.type == :class && y.parent.to_s != klass.to_s
|
47
|
+
|
48
|
+
if parsed_methods.keys.include? y.name
|
49
|
+
raise "#{y.name} defined in two locations: \n #{parsed_methods[y.name]}\n #{y.file}:#{y.line}"
|
50
|
+
end
|
51
|
+
|
52
|
+
parsed_methods[y.name] = "#{y.file}:#{y.line}"
|
53
|
+
|
54
|
+
tags = y.tags.map(&:tag_name)
|
38
55
|
|
39
|
-
|
56
|
+
next unless tags.include?('abi') || tags.include?('subroutine')
|
57
|
+
|
58
|
+
method_hash = { name: y.name.to_s, desc: y.base_docstring, args: [], returns: { type: 'void' },
|
59
|
+
on_completion: ['NoOp'], create: y.has_tag?('create') }
|
60
|
+
|
61
|
+
y.tags.each do |t|
|
62
|
+
method_hash[:returns] = { type: t.types&.first&.downcase } if t.tag_name == 'return'
|
63
|
+
|
64
|
+
method_hash[:on_completion] = t.text[1..-2].split(',').map(&:strip) if t.tag_name == 'on_completion'
|
65
|
+
next unless t.tag_name == 'param'
|
66
|
+
|
67
|
+
method_hash[:args] << { name: t.name, type: t.types&.first&.downcase, desc: t.text }
|
68
|
+
end
|
69
|
+
|
70
|
+
klass.method_hashes << method_hash
|
71
|
+
|
72
|
+
klass.abi_interface.add_method(**method_hash) if tags.include? 'abi'
|
73
|
+
end
|
74
|
+
end
|
40
75
|
end
|
41
76
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
#
|
47
|
-
# @overload subroutine(name)
|
48
|
-
# @param name [Symbol] name of the subroutine
|
49
|
-
#
|
50
|
-
# @yield [*args] the definition of the subroutine
|
51
|
-
def self.subroutine(name, &blk)
|
52
|
-
@subroutines[name] = (blk || instance_method(name))
|
53
|
-
abi_description.add_method(**({ name: name.to_s }.merge abi_method_hash)) unless abi_method_hash.empty?
|
54
|
-
@abi_method_hash = {}
|
55
|
-
nil
|
77
|
+
TEALrb::Opcodes::BINARY_OPCODE_METHOD_MAPPING.each do |meth, opcode|
|
78
|
+
define_method(meth) do |other|
|
79
|
+
@contract.send(opcode, self, other)
|
80
|
+
end
|
56
81
|
end
|
57
82
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
#
|
63
|
-
# @overload subroutine(name)
|
64
|
-
# @param name [Symbol] name of the method
|
65
|
-
#
|
66
|
-
# @yield [*args] the definition of the TEAL method
|
67
|
-
def self.teal(name, &blk)
|
68
|
-
@teal_methods[name] = (blk || instance_method(name))
|
69
|
-
nil
|
83
|
+
TEALrb::Opcodes::UNARY_OPCODE_METHOD_MAPPING.each do |meth, opcode|
|
84
|
+
define_method(meth) do
|
85
|
+
@contract.send(opcode, self)
|
86
|
+
end
|
70
87
|
end
|
71
88
|
|
72
89
|
# sets the `#pragma version`, defines teal methods, and defines subroutines
|
73
90
|
def initialize
|
74
|
-
|
75
|
-
IfBlock.id = 0
|
76
|
-
@scratch = Scratch.new
|
91
|
+
self.class.parse(self.class)
|
77
92
|
|
78
|
-
self.class.
|
79
|
-
|
80
|
-
callsub(name)
|
81
|
-
end
|
82
|
-
end
|
93
|
+
@teal = TEAL.new ["#pragma version #{self.class.version}"], self
|
94
|
+
@scratch = Scratch.new self
|
83
95
|
|
84
|
-
|
85
|
-
|
86
|
-
end
|
96
|
+
@contract = self
|
97
|
+
@if_count = 0
|
87
98
|
|
88
|
-
self.class.
|
89
|
-
|
99
|
+
self.class.method_hashes.each do |mh|
|
100
|
+
define_subroutine(mh[:name], method(mh[:name]))
|
90
101
|
end
|
102
|
+
|
103
|
+
compile
|
91
104
|
end
|
92
105
|
|
93
106
|
VOID_OPS = %w[assert err return app_global_put b bnz bz store
|
@@ -95,47 +108,209 @@ module TEALrb
|
|
95
108
|
log itxn_submit itxn_next].freeze
|
96
109
|
|
97
110
|
def teal_source
|
98
|
-
|
111
|
+
@teal.compact.join("\n")
|
112
|
+
end
|
99
113
|
|
100
|
-
|
101
|
-
|
114
|
+
def account(_account = nil)
|
115
|
+
@account ||= Account.new self
|
116
|
+
end
|
102
117
|
|
103
|
-
|
118
|
+
alias accounts account
|
104
119
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
120
|
+
def app(_app = nil)
|
121
|
+
@app ||= App.new self
|
122
|
+
end
|
123
|
+
|
124
|
+
alias apps app
|
125
|
+
|
126
|
+
def asset(_asset = nil)
|
127
|
+
@asset ||= Asset.new self
|
128
|
+
end
|
129
|
+
|
130
|
+
alias assets asset
|
131
|
+
|
132
|
+
def group_txn(_group_txn = nil)
|
133
|
+
@group_txn ||= GroupTxn.new self
|
134
|
+
end
|
135
|
+
|
136
|
+
alias group_txns group_txn
|
137
|
+
|
138
|
+
def global(field = nil, *_args)
|
139
|
+
if field
|
140
|
+
global_opcode(field)
|
141
|
+
else
|
142
|
+
@global ||= Global.new self
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def this_txn
|
147
|
+
ThisTxn.new self
|
148
|
+
end
|
149
|
+
|
150
|
+
def box
|
151
|
+
Box.new self
|
152
|
+
end
|
153
|
+
|
154
|
+
def inner_txn
|
155
|
+
InnerTxn.new self
|
156
|
+
end
|
157
|
+
|
158
|
+
def logs
|
159
|
+
Logs.new self
|
160
|
+
end
|
161
|
+
|
162
|
+
def app_args
|
163
|
+
AppArgs.new self
|
164
|
+
end
|
165
|
+
|
166
|
+
def local
|
167
|
+
Local.new self
|
168
|
+
end
|
169
|
+
|
170
|
+
def txn_type
|
171
|
+
TxnType.new self
|
172
|
+
end
|
173
|
+
|
174
|
+
def generate_source_map(src)
|
175
|
+
last_location = nil
|
176
|
+
src_map_hash = {}
|
177
|
+
|
178
|
+
src.each_line.with_index do |ln, i|
|
179
|
+
if ln[/src_map:/]
|
180
|
+
last_location = ln[/(?<=src_map:)\S+/]
|
181
|
+
next
|
112
182
|
end
|
183
|
+
|
184
|
+
src_map_hash[i + 1] ||= { location: last_location } if last_location
|
113
185
|
end
|
114
186
|
|
115
|
-
|
116
|
-
|
187
|
+
compile_response = Algod.new.compile(src)
|
188
|
+
|
189
|
+
case compile_response.status
|
190
|
+
when 400
|
191
|
+
msg = JSON.parse(compile_response.body)['message']
|
192
|
+
e_msg = StringIO.new
|
193
|
+
e_msg.puts 'Error(s) while attempting to compile TEAL'
|
194
|
+
msg.each_line do |ln|
|
195
|
+
teal_line = ln.split(':').first.to_i
|
196
|
+
ln_msg = ln.split(':')[1..].join(':').strip
|
197
|
+
next if ln_msg == '"0"'
|
198
|
+
|
199
|
+
if src_map_hash[teal_line]
|
200
|
+
e_msg.puts " #{teal_line} - #{src_map_hash[teal_line][:location]}: #{ln_msg}"
|
201
|
+
else
|
202
|
+
e_msg.puts " #{ln}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
raise e_msg.string
|
206
|
+
when 200
|
207
|
+
json_body = JSON.parse(compile_response.body)
|
208
|
+
pc_mapping = json_body['sourcemap']['mapping']
|
209
|
+
|
210
|
+
pc_array = pc_mapping.split(';').map do |v|
|
211
|
+
SourceMap::VLQ.decode_array(v)[2]
|
212
|
+
end
|
213
|
+
|
214
|
+
last_line = 1
|
215
|
+
|
216
|
+
line_to_pc = {}
|
217
|
+
pc_to_line = {}
|
218
|
+
pc_array.each_with_index do |line_delta, pc|
|
219
|
+
last_line += line_delta.to_i
|
220
|
+
|
221
|
+
next if last_line == 1
|
222
|
+
|
223
|
+
line_to_pc[last_line] ||= []
|
224
|
+
line_to_pc[last_line] << pc
|
225
|
+
pc_to_line[pc] = last_line
|
226
|
+
end
|
227
|
+
|
228
|
+
line_to_pc.each do |line, pcs|
|
229
|
+
src_map_hash[line][:pcs] = pcs if src_map_hash[line]
|
230
|
+
end
|
117
231
|
end
|
118
232
|
|
119
|
-
|
233
|
+
src_map_hash
|
120
234
|
end
|
121
235
|
|
122
|
-
|
123
|
-
|
124
|
-
|
236
|
+
def dump(directory = Dir.pwd, name: self.class.to_s.downcase, abi: true, src_map: true)
|
237
|
+
src = formatted_teal
|
238
|
+
|
239
|
+
File.write(File.join(directory, "#{name}.teal"), src)
|
240
|
+
File.write(File.join(directory, "#{name}.abi.json"), JSON.pretty_generate(abi_hash)) if abi
|
241
|
+
|
242
|
+
return unless src_map
|
243
|
+
|
244
|
+
src_map_json = JSON.pretty_generate(generate_source_map(src))
|
245
|
+
File.write(File.join(directory, "#{name}.src_map.json"), src_map_json)
|
125
246
|
end
|
126
247
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
# @return [nil]
|
131
|
-
def define_teal_method(name, definition)
|
132
|
-
new_source = generate_method_source(name, definition)
|
248
|
+
def formatted_teal
|
249
|
+
new_lines = []
|
250
|
+
comments = []
|
133
251
|
|
134
|
-
|
135
|
-
|
252
|
+
@teal.compact.each do |ln|
|
253
|
+
ln = ln.strip
|
254
|
+
|
255
|
+
next if ln.empty?
|
256
|
+
|
257
|
+
if ln[/^#pragma/]
|
258
|
+
new_lines << { text: ln, void: true, comments: comments }
|
259
|
+
comments = []
|
260
|
+
elsif ln[%r{^//}]
|
261
|
+
comments << ln
|
262
|
+
else
|
263
|
+
op = ln.split.first
|
264
|
+
label_regex = %r{\S+:($| //)}
|
265
|
+
|
266
|
+
label_type = label_number = label_end = nil
|
267
|
+
|
268
|
+
if ln[label_regex]
|
269
|
+
label_type = ln[/^if/] || ln[/^while/]
|
270
|
+
label_number = ln[/(?<=^if)\d+/] || ln[/(?<=^while)\d+/]
|
271
|
+
label_end = ln[/_end:/]
|
272
|
+
end
|
273
|
+
|
274
|
+
new_lines << { text: ln, void: VOID_OPS.include?(op), comments: comments, label: ln[label_regex],
|
275
|
+
label_type: label_type, label_number: label_number, label_end: label_end }
|
276
|
+
comments = []
|
277
|
+
end
|
136
278
|
end
|
137
279
|
|
138
|
-
|
280
|
+
output = []
|
281
|
+
indent_level = 0
|
282
|
+
current_labels = { 'while' => [], 'if' => [] }
|
283
|
+
|
284
|
+
new_lines.each do |ln|
|
285
|
+
indent_level = 1 if ln[:label] && indent_level.zero? && !ln[:label_type]
|
286
|
+
|
287
|
+
if ln[:label_type]
|
288
|
+
indent_level += 1 unless current_labels[ln[:label_type]].include?(ln[:label_number])
|
289
|
+
current_labels[ln[:label_type]] << ln[:label_number]
|
290
|
+
end
|
291
|
+
|
292
|
+
ln_indent_level = indent_level
|
293
|
+
ln_indent_level -= 1 if ln[:label]
|
294
|
+
|
295
|
+
output << '' if !output.last&.empty? && (ln[:label] || ln[:comments].any?)
|
296
|
+
|
297
|
+
ln[:comments].each { output << (("\t" * ln_indent_level) + _1) }
|
298
|
+
output << (("\t" * ln_indent_level) + ln[:text])
|
299
|
+
|
300
|
+
output << '' if ln[:void] && !output.last.empty?
|
301
|
+
|
302
|
+
next unless ln[:label_end]
|
303
|
+
|
304
|
+
indent_level -= 1
|
305
|
+
current_labels[ln[:label_type]].delete ln[:label_number]
|
306
|
+
end
|
307
|
+
|
308
|
+
output.join("\n")
|
309
|
+
end
|
310
|
+
|
311
|
+
# return the input without transpiling to TEAL
|
312
|
+
def rb(input)
|
313
|
+
input
|
139
314
|
end
|
140
315
|
|
141
316
|
# defines a TEAL subroutine
|
@@ -143,11 +318,15 @@ module TEALrb
|
|
143
318
|
# @param definition [Lambda, Proc, UnboundMethod] the method definition
|
144
319
|
# @return [nil]
|
145
320
|
def define_subroutine(name, definition)
|
321
|
+
return if method(name).source_location.first == __FILE__
|
322
|
+
|
323
|
+
@eval_location = method(name).source_location
|
324
|
+
|
146
325
|
define_singleton_method(name) do |*_args|
|
147
326
|
callsub(name)
|
148
327
|
end
|
149
328
|
|
150
|
-
|
329
|
+
@teal << 'b main' unless @teal.include? 'b main'
|
151
330
|
|
152
331
|
label(name) # add teal label
|
153
332
|
|
@@ -155,7 +334,9 @@ module TEALrb
|
|
155
334
|
comment_content = "#{name}(#{comment_params})"
|
156
335
|
comment(comment_content, inline: true)
|
157
336
|
|
158
|
-
|
337
|
+
method_hash = self.class.method_hashes.find { _1[:name] == name.to_s }
|
338
|
+
new_source = generate_subroutine_source(definition, method_hash)
|
339
|
+
|
159
340
|
new_source = "#{new_source}retsub"
|
160
341
|
|
161
342
|
eval_tealrb(new_source, debug_context: "subroutine: #{name}")
|
@@ -169,23 +350,22 @@ module TEALrb
|
|
169
350
|
def comment(content, inline: false)
|
170
351
|
content = " #{content}" unless content[0] == ' '
|
171
352
|
if inline
|
172
|
-
last_line =
|
173
|
-
|
353
|
+
last_line = @teal.pop
|
354
|
+
@teal << "#{last_line} //#{content}"
|
174
355
|
else
|
175
|
-
|
176
|
-
TEAL.instance << "//#{content}"
|
356
|
+
@teal << "//#{content}"
|
177
357
|
end
|
178
358
|
end
|
179
359
|
|
180
360
|
# inserts a string into TEAL source
|
181
361
|
# @param string [String] the string to insert
|
182
362
|
def placeholder(string)
|
183
|
-
|
363
|
+
@teal << string
|
184
364
|
end
|
185
365
|
|
186
366
|
# the hash of the abi description
|
187
367
|
def abi_hash
|
188
|
-
self.class.
|
368
|
+
self.class.abi_interface.to_h
|
189
369
|
end
|
190
370
|
|
191
371
|
# transpiles the given string to TEAL
|
@@ -199,51 +379,157 @@ module TEALrb
|
|
199
379
|
# transpiles #main and routes abi methods. To disable abi routing, set `@disable_abi_routing` to true in your
|
200
380
|
# Contract subclass
|
201
381
|
def compile
|
202
|
-
|
382
|
+
@teal << 'main:' if @teal.include? 'b main'
|
203
383
|
route_abi_methods unless self.class.disable_abi_routing
|
204
|
-
|
384
|
+
return unless respond_to? :main
|
385
|
+
|
386
|
+
@eval_location = method(:main).source_location
|
387
|
+
|
388
|
+
eval_tealrb(
|
389
|
+
rewrite(
|
390
|
+
method(:main).source,
|
391
|
+
method_rewriter: true
|
392
|
+
),
|
393
|
+
debug_context: 'main'
|
394
|
+
)
|
395
|
+
end
|
396
|
+
|
397
|
+
def main
|
398
|
+
nil
|
399
|
+
end
|
400
|
+
|
401
|
+
def compiled_program
|
402
|
+
compile_response = Algod.new.compile(formatted_teal)
|
403
|
+
|
404
|
+
generate_source_map(formatted_teal) if compile_response.status != 200
|
405
|
+
|
406
|
+
JSON.parse(compile_response.body)['result']
|
205
407
|
end
|
206
408
|
|
207
409
|
private
|
208
410
|
|
209
411
|
def route_abi_methods
|
210
|
-
self.class.
|
412
|
+
self.class.abi_interface.methods.each_with_index do |meth, i|
|
211
413
|
signature = "#{meth[:name]}(#{meth[:args].map { _1[:type] }.join(',')})#{meth[:returns][:type]}"
|
212
414
|
selector = OpenSSL::Digest.new('SHA512-256').hexdigest(signature)[..7]
|
213
415
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
416
|
+
app_args[int(0)] == byte(selector) # rubocop:disable Lint/Void
|
417
|
+
bz("abi_routing#{i}")
|
418
|
+
callsub(meth[:name])
|
419
|
+
approve
|
420
|
+
label("abi_routing#{i}")
|
218
421
|
end
|
219
422
|
end
|
220
423
|
|
221
424
|
def generate_method_source(name, definition)
|
222
425
|
new_source = rewrite(definition.source, method_rewriter: true)
|
223
426
|
|
224
|
-
pre_string =
|
427
|
+
pre_string = []
|
225
428
|
|
226
429
|
scratch_names = []
|
227
430
|
definition.parameters.reverse.each_with_index do |param, _i|
|
228
|
-
param_name = param.
|
431
|
+
param_name = param.first
|
229
432
|
scratch_name = [name, param_name].map(&:to_s).join(': ')
|
230
433
|
scratch_names << scratch_name
|
231
434
|
|
232
|
-
pre_string
|
233
|
-
pre_string
|
435
|
+
pre_string << "@scratch.store('#{scratch_name}')"
|
436
|
+
pre_string << "#{param_name} = -> { @scratch['#{scratch_name}'] }"
|
437
|
+
end
|
438
|
+
|
439
|
+
"#{pre_string.join(';')};#{new_source}"
|
440
|
+
end
|
441
|
+
|
442
|
+
def generate_subroutine_source(definition, method_hash)
|
443
|
+
new_source = rewrite(definition.source, method_rewriter: true)
|
444
|
+
|
445
|
+
pre_string = []
|
446
|
+
|
447
|
+
scratch_names = []
|
448
|
+
|
449
|
+
txn_types = %w[txn pay keyreg acfg axfer afrz appl]
|
450
|
+
|
451
|
+
args = method_hash[:args] || []
|
452
|
+
arg_types = args.map { _1[:type] } || []
|
453
|
+
txn_params = arg_types.select { txn_types.include? _1 }.count
|
454
|
+
app_param_index = -1
|
455
|
+
asset_param_index = -1
|
456
|
+
account_param_index = 0
|
457
|
+
args_index = 0
|
458
|
+
|
459
|
+
method_hash[:on_completion].each_with_index do |oc, i|
|
460
|
+
this_txn.on_completion
|
461
|
+
int(oc)
|
462
|
+
equal
|
463
|
+
boolean_or unless i.zero?
|
234
464
|
end
|
465
|
+
assert
|
466
|
+
|
467
|
+
assert(this_txn.application_id == (int(0))) if method_hash[:create]
|
468
|
+
|
469
|
+
if abi_hash['methods'].find { _1[:name] == method_hash[:name].to_s }
|
470
|
+
definition.parameters.each_with_index do |param, i|
|
471
|
+
param_name = param.last
|
472
|
+
|
473
|
+
scratch_name = "#{definition.original_name}: #{param_name} [#{arg_types[i] || 'any'}] #{
|
474
|
+
args[i][:desc] if args[i]}"
|
475
|
+
scratch_names << scratch_name
|
476
|
+
|
477
|
+
type = arg_types[i]&.downcase
|
478
|
+
|
479
|
+
if txn_types.include? type
|
480
|
+
@scratch[scratch_name] = group_txns[this_txn.group_index.subtract int(txn_params)]
|
481
|
+
txn_params -= 1
|
482
|
+
elsif type == 'application'
|
483
|
+
@scratch[scratch_name] = apps[app_param_index += 1]
|
484
|
+
elsif type == 'asset'
|
485
|
+
@scratch[scratch_name] = assets[asset_param_index += 1]
|
486
|
+
elsif type == 'account'
|
487
|
+
@scratch[scratch_name] = accounts[account_param_index += 1]
|
488
|
+
elsif type == 'uint64'
|
489
|
+
@scratch[scratch_name] = btoi(app_args[args_index += 1])
|
490
|
+
else
|
491
|
+
@scratch[scratch_name] = app_args[args_index += 1]
|
492
|
+
end
|
493
|
+
|
494
|
+
pre_string << "#{param_name} = -> {@scratch['#{scratch_name}'] }"
|
495
|
+
end
|
496
|
+
|
497
|
+
else
|
498
|
+
args.reverse!
|
499
|
+
arg_types.reverse!
|
500
|
+
|
501
|
+
definition.parameters.reverse.each_with_index do |param, i|
|
502
|
+
param_name = param.last
|
503
|
+
|
504
|
+
scratch_name = "#{definition.original_name}: #{param_name} [#{arg_types[i] || 'any'}] #{
|
505
|
+
args[i][:desc] if args[i]}"
|
506
|
+
scratch_names << scratch_name
|
507
|
+
|
508
|
+
type = arg_types[i]&.downcase
|
509
|
+
|
510
|
+
if txn_types.include? type
|
511
|
+
@scratch[scratch_name] = group_txn
|
512
|
+
elsif type == 'application'
|
513
|
+
@scratch[scratch_name] = application
|
514
|
+
elsif type == 'asset'
|
515
|
+
@scratch[scratch_name] = asset
|
516
|
+
elsif type == 'account'
|
517
|
+
@scratch[scratch_name] = account
|
518
|
+
else
|
519
|
+
@scratch.store(scratch_name)
|
520
|
+
end
|
521
|
+
|
522
|
+
pre_string << "#{param_name} = -> { @scratch['#{scratch_name}'] }"
|
523
|
+
end
|
235
524
|
|
236
|
-
post_string = StringIO.new
|
237
|
-
scratch_names.each do |n|
|
238
|
-
post_string.puts "@scratch.delete '#{n}'"
|
239
525
|
end
|
240
526
|
|
241
|
-
"#{pre_string.
|
527
|
+
"#{pre_string.join(';')};#{new_source}"
|
242
528
|
end
|
243
529
|
|
244
530
|
def rewrite_with_rewriter(string, rewriter)
|
245
531
|
process_source = RuboCop::ProcessedSource.new(string, RUBY_VERSION[/\d\.\d/].to_f)
|
246
|
-
rewriter.new.rewrite(process_source)
|
532
|
+
rewriter.new.rewrite(process_source, self)
|
247
533
|
end
|
248
534
|
|
249
535
|
def rewrite(string, method_rewriter: false)
|
@@ -270,7 +556,7 @@ module TEALrb
|
|
270
556
|
end
|
271
557
|
|
272
558
|
def eval_tealrb(s, debug_context:)
|
273
|
-
pre_teal = Array.new
|
559
|
+
pre_teal = Array.new @teal
|
274
560
|
|
275
561
|
if self.class.debug
|
276
562
|
puts "DEBUG: Evaluating the following code (#{debug_context}):"
|
@@ -282,32 +568,20 @@ module TEALrb
|
|
282
568
|
|
283
569
|
if self.class.debug
|
284
570
|
puts "DEBUG: Resulting TEAL (#{debug_context}):"
|
285
|
-
puts Array.new(
|
571
|
+
puts Array.new(@teal) - pre_teal
|
286
572
|
puts ''
|
287
573
|
end
|
288
574
|
rescue SyntaxError, StandardError => e
|
289
575
|
@eval_tealrb_rescue_count ||= 0
|
290
576
|
|
291
577
|
eval_locations = e.backtrace.select { _1[/\(eval\)/] }
|
292
|
-
|
293
|
-
|
294
|
-
warn "'#{e}' when evaluating transpiled TEALrb source" if @eval_tealrb_rescue_count.zero?
|
295
|
-
|
296
|
-
warn "Backtrace location (#{@eval_tealrb_rescue_count + 1} / #{eval_locations.size}):"
|
297
|
-
|
298
|
-
@eval_tealrb_rescue_count += 1
|
299
|
-
|
300
|
-
s.lines.each_with_index do |line, i|
|
301
|
-
line_num = i + 1
|
302
|
-
if error_line == line_num
|
303
|
-
warn "=> #{line_num}: #{line}"
|
304
|
-
else
|
305
|
-
warn " #{line_num}: #{line}"
|
306
|
-
end
|
578
|
+
if eval_locations[@eval_tealrb_rescue_count]
|
579
|
+
error_line = eval_locations[@eval_tealrb_rescue_count].split(':')[1].to_i
|
307
580
|
end
|
308
581
|
|
309
|
-
|
310
|
-
|
582
|
+
msg = "#{@eval_location.first}:#{error_line + @eval_location.last}"
|
583
|
+
|
584
|
+
raise e, "#{msg}: #{e}"
|
311
585
|
end
|
312
586
|
end
|
313
587
|
end
|