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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 901c0ceb42462821200254c78055b4953f680c46507ede5f09d751e13194e8ae
4
- data.tar.gz: 2e24af5fa78eb16bc19fe298c047ed712d3536183e533b614a4474a9af89712d
3
+ metadata.gz: c937c416e2b2f337bdef2100c573862ed55a40e864d5a19f2cef3c737e823608
4
+ data.tar.gz: 0c09c2850d71f1392e0afc44943a62a0fed55153b6ef60b19700527639e43d84
5
5
  SHA512:
6
- metadata.gz: b8886b2142000d91603f835032058982a50bc0fddaa81cd00e00957fb1b1c5c74ba87484deb7598a660183fa810193491cb0e7c14811b830c454befd6fd4c87b
7
- data.tar.gz: b2e282ef718646af4413f6dad9a72ca70ba23d54d97108e5c34047ca5804eafecbcbe6ec822f9520b75ae18dc7fcbc57b60b33e0d4db93f5843bf580a37657f0
6
+ metadata.gz: 762827b72f11790427f51fd8021da4ef5ce2af3598b792b3b59b5ccf247fb1240cc9a0658482c09979167094a4337b9c93f27d025ae15ad603a32621c603123f
7
+ data.tar.gz: 135d42f56fdcd7520ca0677f4afb93e988b7151716fc534ba33682a0b51e1eee6ff5f0100b3ca14c2299948ab20fd9b338837e77289f2a62e6b1df8cd0c64f8d
data/lib/tealrb/abi.rb CHANGED
@@ -4,8 +4,10 @@ require 'json'
4
4
 
5
5
  module TEALrb
6
6
  module ABI
7
- def abi_return(data)
8
- log(concat('151f7c75', data.teal).teal)
7
+ def abi_return(_data)
8
+ byte('151f7c75')
9
+ concat
10
+ log
9
11
  end
10
12
 
11
13
  class ABIDescription
@@ -11,8 +11,8 @@ module TEALrb
11
11
  attr_reader :teal
12
12
 
13
13
  class << self
14
- attr_accessor :subroutines, :version, :teal_methods, :abi_method_hash, :abi_description, :debug,
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.abi_description = ABI::ABIDescription.new
24
- klass.abi_method_hash = {}
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
- # abi description for the method
32
- def self.abi(desc:, args:, returns:)
33
- args = args.map do |name, h|
34
- h[:name] = name.to_s
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
- self.abi_method_hash = { desc: desc, args: args, returns: returns.to_s.split('::').last.downcase }
40
- end
37
+ YARD.parse Object.const_source_location(klass.to_s).first
41
38
 
42
- # specifies a TEAL subroutine that will be defined upon intialization
43
- # @return [nil]
44
- # @overload subroutine(name)
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
- # specifies a method to be defined upon intialization that will be transpiled to TEAL when called
59
- # @return [nil]
60
- # @overload subroutine(name)
61
- # @param name [Symbol] name of the method to use as the TEAL definition
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
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.class.subroutines.each_key do |name|
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.subroutines.each do |name, blk|
85
- define_subroutine name, blk
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, blk|
89
- define_teal_method name, blk
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
- new_source = generate_method_source(name, definition)
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.abi_description.to_h
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.abi_description.methods.each do |meth|
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.last
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
- post_string = StringIO.new
237
- scratch_names.each do |n|
238
- post_string.puts "@scratch.delete '#{n}'"
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}#{post_string.string}"
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
- error_line = eval_locations[@eval_tealrb_rescue_count].split(':')[1].to_i
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
- ExtendedOpcodes.txna @field, index
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
@@ -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 Accounts ApplicationArgs Assets Apps Logs].include? node.children.first&.children&.last
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
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'method_source'
4
4
  require 'rubocop'
5
+ require 'yard'
5
6
 
6
7
  require_relative 'tealrb/constants'
7
8
  require_relative 'tealrb/abi'
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.10.1
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-09-13 00:00:00.000000000 Z
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: yard
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.9.27
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.9.27
166
+ version: 3.0.2
153
167
  description:
154
168
  email: joepolny+dev@gmail.com
155
169
  executables: []