tealrb 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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: []