tealrb 0.10.1 → 0.11.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 +4 -2
- data/lib/tealrb/contract.rb +123 -60
- data/lib/tealrb/opcode_modules.rb +22 -1
- data/lib/tealrb/rewriters.rb +32 -1
- data/lib/tealrb.rb +1 -0
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c937c416e2b2f337bdef2100c573862ed55a40e864d5a19f2cef3c737e823608
|
4
|
+
data.tar.gz: 0c09c2850d71f1392e0afc44943a62a0fed55153b6ef60b19700527639e43d84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 762827b72f11790427f51fd8021da4ef5ce2af3598b792b3b59b5ccf247fb1240cc9a0658482c09979167094a4337b9c93f27d025ae15ad603a32621c603123f
|
7
|
+
data.tar.gz: 135d42f56fdcd7520ca0677f4afb93e988b7151716fc534ba33682a0b51e1eee6ff5f0100b3ca14c2299948ab20fd9b338837e77289f2a62e6b1df8cd0c64f8d
|
data/lib/tealrb/abi.rb
CHANGED
data/lib/tealrb/contract.rb
CHANGED
@@ -11,8 +11,8 @@ module TEALrb
|
|
11
11
|
attr_reader :teal
|
12
12
|
|
13
13
|
class << self
|
14
|
-
attr_accessor :subroutines, :version, :teal_methods, :
|
15
|
-
:disable_abi_routing
|
14
|
+
attr_accessor :subroutines, :version, :teal_methods, :abi_interface, :debug,
|
15
|
+
:disable_abi_routing, :method_hashes
|
16
16
|
|
17
17
|
private
|
18
18
|
|
@@ -20,53 +20,47 @@ module TEALrb
|
|
20
20
|
klass.version = 6
|
21
21
|
klass.subroutines = {}
|
22
22
|
klass.teal_methods = {}
|
23
|
-
klass.
|
24
|
-
klass.
|
23
|
+
klass.abi_interface = ABI::ABIDescription.new
|
24
|
+
klass.abi_interface.name = klass.to_s
|
25
|
+
klass.method_hashes = []
|
25
26
|
klass.debug = false
|
26
27
|
klass.disable_abi_routing = false
|
28
|
+
parse(klass)
|
27
29
|
super
|
28
30
|
end
|
29
|
-
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
h[:type] = h[:type].to_s.split('::').last.downcase
|
36
|
-
h
|
37
|
-
end
|
32
|
+
def parse(klass)
|
33
|
+
YARD::Tags::Library.define_tag('ABI Method', :abi)
|
34
|
+
YARD::Tags::Library.define_tag('Subroutine', :subroutine)
|
35
|
+
YARD::Tags::Library.define_tag('TEAL Method', :teal)
|
38
36
|
|
39
|
-
|
40
|
-
end
|
37
|
+
YARD.parse Object.const_source_location(klass.to_s).first
|
41
38
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
# @param name [Symbol] name of the subroutine and the method to use as the subroutine definition
|
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
|
56
|
-
end
|
39
|
+
YARD::Registry.all.each do |y|
|
40
|
+
next unless y.type == :method
|
41
|
+
next unless y.parent.to_s == klass.to_s
|
57
42
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
43
|
+
tags = y.tags.map(&:tag_name)
|
44
|
+
|
45
|
+
if tags.include?('abi') || tags.include?('subroutine')
|
46
|
+
method_hash = { name: y.name.to_s, desc: y.base_docstring, args: [], returns: { type: 'void' } }
|
47
|
+
|
48
|
+
y.tags.each do |t|
|
49
|
+
method_hash[:returns] = { type: t.types&.first } if t.tag_name == 'return'
|
50
|
+
|
51
|
+
next unless t.tag_name == 'param'
|
52
|
+
|
53
|
+
method_hash[:args] << { name: t.name, type: t.types&.first, desc: t.text }
|
54
|
+
end
|
55
|
+
|
56
|
+
klass.method_hashes << method_hash
|
57
|
+
|
58
|
+
klass.abi_interface.add_method(**method_hash) if tags.include? 'abi'
|
59
|
+
elsif tags.include? 'teal'
|
60
|
+
klass.teal_methods[y.name.to_s] = y
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
70
64
|
end
|
71
65
|
|
72
66
|
# sets the `#pragma version`, defines teal methods, and defines subroutines
|
@@ -75,18 +69,14 @@ module TEALrb
|
|
75
69
|
IfBlock.id = 0
|
76
70
|
@scratch = Scratch.new
|
77
71
|
|
78
|
-
self
|
79
|
-
define_singleton_method(name) do |*_args|
|
80
|
-
callsub(name)
|
81
|
-
end
|
82
|
-
end
|
72
|
+
@@active_contract = self # rubocop:disable Style/ClassVars
|
83
73
|
|
84
|
-
self.class.
|
85
|
-
define_subroutine
|
74
|
+
self.class.method_hashes.each do |mh|
|
75
|
+
define_subroutine(mh[:name], method(mh[:name]))
|
86
76
|
end
|
87
77
|
|
88
|
-
self.class.teal_methods.each do |name,
|
89
|
-
define_teal_method
|
78
|
+
self.class.teal_methods.each do |name, definition|
|
79
|
+
define_teal_method(name, definition)
|
90
80
|
end
|
91
81
|
end
|
92
82
|
|
@@ -155,7 +145,9 @@ module TEALrb
|
|
155
145
|
comment_content = "#{name}(#{comment_params})"
|
156
146
|
comment(comment_content, inline: true)
|
157
147
|
|
158
|
-
|
148
|
+
method_hash = self.class.method_hashes.find { _1[:name] == name.to_s }
|
149
|
+
new_source = generate_subroutine_source(definition, method_hash)
|
150
|
+
|
159
151
|
new_source = "#{new_source}retsub"
|
160
152
|
|
161
153
|
eval_tealrb(new_source, debug_context: "subroutine: #{name}")
|
@@ -185,7 +177,7 @@ module TEALrb
|
|
185
177
|
|
186
178
|
# the hash of the abi description
|
187
179
|
def abi_hash
|
188
|
-
self.class.
|
180
|
+
self.class.abi_interface.to_h
|
189
181
|
end
|
190
182
|
|
191
183
|
# transpiles the given string to TEAL
|
@@ -207,7 +199,7 @@ module TEALrb
|
|
207
199
|
private
|
208
200
|
|
209
201
|
def route_abi_methods
|
210
|
-
self.class.
|
202
|
+
self.class.abi_interface.methods.each do |meth|
|
211
203
|
signature = "#{meth[:name]}(#{meth[:args].map { _1[:type] }.join(',')})#{meth[:returns][:type]}"
|
212
204
|
selector = OpenSSL::Digest.new('SHA512-256').hexdigest(signature)[..7]
|
213
205
|
|
@@ -225,7 +217,7 @@ module TEALrb
|
|
225
217
|
|
226
218
|
scratch_names = []
|
227
219
|
definition.parameters.reverse.each_with_index do |param, _i|
|
228
|
-
param_name = param.
|
220
|
+
param_name = param.first
|
229
221
|
scratch_name = [name, param_name].map(&:to_s).join(': ')
|
230
222
|
scratch_names << scratch_name
|
231
223
|
|
@@ -233,12 +225,81 @@ module TEALrb
|
|
233
225
|
pre_string.puts "#{param_name} = -> { @scratch['#{scratch_name}'] }"
|
234
226
|
end
|
235
227
|
|
236
|
-
|
237
|
-
|
238
|
-
|
228
|
+
"#{pre_string.string}#{new_source}"
|
229
|
+
end
|
230
|
+
|
231
|
+
def generate_subroutine_source(definition, method_hash)
|
232
|
+
new_source = rewrite(definition.source, method_rewriter: true)
|
233
|
+
|
234
|
+
pre_string = StringIO.new
|
235
|
+
|
236
|
+
scratch_names = []
|
237
|
+
|
238
|
+
txn_types = %w[txn pay keyreg acfg axfer afrz appl]
|
239
|
+
|
240
|
+
args = method_hash[:args] || []
|
241
|
+
arg_types = args.map { _1[:type] } || []
|
242
|
+
txn_params = arg_types.select { txn_types.include? _1 }.count
|
243
|
+
app_param_index = -1
|
244
|
+
asset_param_index = -1
|
245
|
+
account_param_index = 0
|
246
|
+
args_index = 0
|
247
|
+
|
248
|
+
if abi_hash['methods'].find { _1[:name] == method_hash[:name].to_s }
|
249
|
+
definition.parameters.each_with_index do |param, i|
|
250
|
+
param_name = param.last
|
251
|
+
|
252
|
+
scratch_name = "#{definition.original_name}: #{param_name} [#{arg_types[i] || 'any'}] #{if args[i]
|
253
|
+
args[i][:desc]
|
254
|
+
end}"
|
255
|
+
scratch_names << scratch_name
|
256
|
+
|
257
|
+
if txn_types.include? arg_types[i]
|
258
|
+
pre_string.puts "@scratch['#{scratch_name}'] = Gtxns[Txn.group_index - int(#{txn_params})]"
|
259
|
+
txn_params -= 1
|
260
|
+
elsif arg_types[i] == 'application'
|
261
|
+
pre_string.puts "@scratch['#{scratch_name}'] = Apps[#{app_param_index += 1}]"
|
262
|
+
elsif arg_types[i] == 'asset'
|
263
|
+
pre_string.puts "@scratch['#{scratch_name}'] = Assets[#{asset_param_index += 1}]"
|
264
|
+
elsif arg_types[i] == 'account'
|
265
|
+
pre_string.puts "@scratch['#{scratch_name}'] = Accounts[#{account_param_index += 1}]"
|
266
|
+
else
|
267
|
+
pre_string.puts "@scratch['#{scratch_name}'] = AppArgs[#{args_index += 1}]"
|
268
|
+
end
|
269
|
+
|
270
|
+
pre_string.puts "#{param_name} = -> { @scratch['#{scratch_name}'] }"
|
271
|
+
end
|
272
|
+
|
273
|
+
else
|
274
|
+
args.reverse!
|
275
|
+
arg_types.reverse!
|
276
|
+
|
277
|
+
definition.parameters.reverse.each_with_index do |param, i|
|
278
|
+
param_name = param.last
|
279
|
+
|
280
|
+
scratch_name = "#{definition.original_name}: #{param_name} [#{arg_types[i] || 'any'}] #{if args[i]
|
281
|
+
args[i][:desc]
|
282
|
+
end}"
|
283
|
+
scratch_names << scratch_name
|
284
|
+
|
285
|
+
if txn_types.include? arg_types[i]
|
286
|
+
pre_string.puts "@scratch['#{scratch_name}'] = Gtxns"
|
287
|
+
elsif arg_types[i] == 'application'
|
288
|
+
pre_string.puts "@scratch['#{scratch_name}'] = Applications.new"
|
289
|
+
elsif arg_types[i] == 'asset'
|
290
|
+
pre_string.puts "@scratch['#{scratch_name}'] = Assets.new"
|
291
|
+
elsif arg_types[i] == 'account'
|
292
|
+
pre_string.puts "@scratch['#{scratch_name}'] = Accounts.new"
|
293
|
+
else
|
294
|
+
pre_string.puts "@scratch.store('#{scratch_name}')"
|
295
|
+
end
|
296
|
+
|
297
|
+
pre_string.puts "#{param_name} = -> { @scratch['#{scratch_name}'] }"
|
298
|
+
end
|
299
|
+
|
239
300
|
end
|
240
301
|
|
241
|
-
"#{pre_string.string}#{new_source}
|
302
|
+
"#{pre_string.string}#{new_source}"
|
242
303
|
end
|
243
304
|
|
244
305
|
def rewrite_with_rewriter(string, rewriter)
|
@@ -254,7 +315,7 @@ module TEALrb
|
|
254
315
|
end
|
255
316
|
|
256
317
|
[CommentRewriter, ComparisonRewriter, WhileRewriter, InlineIfRewriter, IfRewriter, OpRewriter,
|
257
|
-
AssignRewriter].each do |rw|
|
318
|
+
AssignRewriter, InternalMethodRewriter].each do |rw|
|
258
319
|
string = rewrite_with_rewriter(string, rw)
|
259
320
|
end
|
260
321
|
|
@@ -289,7 +350,9 @@ module TEALrb
|
|
289
350
|
@eval_tealrb_rescue_count ||= 0
|
290
351
|
|
291
352
|
eval_locations = e.backtrace.select { _1[/\(eval\)/] }
|
292
|
-
|
353
|
+
if eval_locations[@eval_tealrb_rescue_count]
|
354
|
+
error_line = eval_locations[@eval_tealrb_rescue_count].split(':')[1].to_i
|
355
|
+
end
|
293
356
|
|
294
357
|
warn "'#{e}' when evaluating transpiled TEALrb source" if @eval_tealrb_rescue_count.zero?
|
295
358
|
|
@@ -474,6 +474,22 @@ module TEALrb
|
|
474
474
|
end
|
475
475
|
end
|
476
476
|
|
477
|
+
module ItxnField
|
478
|
+
extend TxnFields
|
479
|
+
|
480
|
+
def self.opcode(field, _value)
|
481
|
+
ExtendedOpcodes.itxn_field field
|
482
|
+
end
|
483
|
+
|
484
|
+
class << self
|
485
|
+
TxnFields.instance_methods.each do |m|
|
486
|
+
define_method("#{m}=") do |value|
|
487
|
+
send(m, value)
|
488
|
+
end
|
489
|
+
end
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
477
493
|
module Gtxn
|
478
494
|
extend TxnFields
|
479
495
|
|
@@ -528,7 +544,12 @@ module TEALrb
|
|
528
544
|
end
|
529
545
|
|
530
546
|
def [](index)
|
531
|
-
|
547
|
+
if index.is_a? Integer
|
548
|
+
ExtendedOpcodes.txna @field, index
|
549
|
+
else
|
550
|
+
ExtendedOpcodes.txnas @field
|
551
|
+
end
|
552
|
+
|
532
553
|
self
|
533
554
|
end
|
534
555
|
end
|
data/lib/tealrb/rewriters.rb
CHANGED
@@ -27,6 +27,29 @@ module TEALrb
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
class InternalMethodRewriter < Rewriter
|
31
|
+
def on_send(node)
|
32
|
+
teal_methods = TEALrb::Contract.class_variable_get(:@@active_contract).class.teal_methods
|
33
|
+
|
34
|
+
method_name = node.loc.selector.source.to_sym
|
35
|
+
|
36
|
+
if teal_methods.keys.include? method_name
|
37
|
+
param_names = teal_methods[method_name].parameters.map(&:last)
|
38
|
+
|
39
|
+
pre_string = StringIO.new
|
40
|
+
param_names.each_with_index do |param, i|
|
41
|
+
scratch_name = [method_name, param].map(&:to_s).join(': ')
|
42
|
+
|
43
|
+
pre_string.puts "@scratch.store('#{scratch_name}', #{node.children[i + 2].loc.expression.source})"
|
44
|
+
end
|
45
|
+
|
46
|
+
replace node.source_range, "#{pre_string.string}\n#{method_name}"
|
47
|
+
end
|
48
|
+
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
30
53
|
class MethodRewriter < Rewriter
|
31
54
|
def on_def(node)
|
32
55
|
replace node.source_range, node.body.source
|
@@ -125,6 +148,12 @@ module TEALrb
|
|
125
148
|
super
|
126
149
|
end
|
127
150
|
|
151
|
+
def on_return(node)
|
152
|
+
replace node.loc.keyword, 'abi_return'
|
153
|
+
|
154
|
+
super
|
155
|
+
end
|
156
|
+
|
128
157
|
OPCODE_METHODS = TEALrb::Opcodes::AllOpcodes.instance_methods.freeze
|
129
158
|
|
130
159
|
def on_send(node)
|
@@ -149,8 +178,10 @@ module TEALrb
|
|
149
178
|
@skips << node.children[2]
|
150
179
|
elsif node.children.first&.children&.last == :@scratch && meth_name[/=$/]
|
151
180
|
nil
|
152
|
-
elsif %i[@scratch Gtxn
|
181
|
+
elsif %i[@scratch Gtxn].include? node.children.first&.children&.last
|
153
182
|
@skips << node.children.last
|
183
|
+
elsif %i[Accounts ApplicationArgs Assets Apps Logs].include? node.children.first&.children&.last
|
184
|
+
@skips << node.children.last if node.children.last.int_type?
|
154
185
|
end
|
155
186
|
|
156
187
|
super
|
data/lib/tealrb.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tealrb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Polny
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: method_source
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.36'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: yard
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.9.27
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.9.27
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: minitest
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,19 +151,19 @@ dependencies:
|
|
137
151
|
- !ruby/object:Gem::Version
|
138
152
|
version: 0.21.2
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
154
|
+
name: terminal-table
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
142
156
|
requirements:
|
143
157
|
- - "~>"
|
144
158
|
- !ruby/object:Gem::Version
|
145
|
-
version: 0.
|
159
|
+
version: 3.0.2
|
146
160
|
type: :development
|
147
161
|
prerelease: false
|
148
162
|
version_requirements: !ruby/object:Gem::Requirement
|
149
163
|
requirements:
|
150
164
|
- - "~>"
|
151
165
|
- !ruby/object:Gem::Version
|
152
|
-
version: 0.
|
166
|
+
version: 3.0.2
|
153
167
|
description:
|
154
168
|
email: joepolny+dev@gmail.com
|
155
169
|
executables: []
|