rlang 0.4.1 → 0.6.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -3
  3. data/Gemfile.lock +4 -6
  4. data/README +11 -0
  5. data/README.md +18 -10
  6. data/bin/rlang +17 -5
  7. data/docs/RlangCompiler.md +5 -1
  8. data/docs/RlangManual.md +98 -20
  9. data/examples/fib/fib.rb +5 -1
  10. data/lib/builder/rlang/compiler.rb +2 -21
  11. data/lib/rlang/lib/array/array32.rb +59 -0
  12. data/lib/rlang/lib/array/array64.rb +56 -0
  13. data/lib/rlang/lib/array.rb +6 -0
  14. data/lib/rlang/lib/base64.rb +223 -0
  15. data/lib/rlang/lib/io.rb +75 -0
  16. data/lib/rlang/lib/kernel.rb +23 -0
  17. data/lib/rlang/lib/malloc.rb +12 -8
  18. data/lib/rlang/lib/memory.rb +102 -2
  19. data/lib/rlang/lib/object.rb +40 -4
  20. data/lib/rlang/lib/rlang.rb +29 -0
  21. data/lib/rlang/lib/rlang_core.rb +15 -0
  22. data/lib/rlang/lib/string.rb +106 -8
  23. data/lib/rlang/lib/type/i32.rb +43 -0
  24. data/lib/rlang/lib/type/i64.rb +2 -0
  25. data/lib/rlang/lib/unistd.rb +1 -2
  26. data/lib/rlang/lib/wasi.rb +184 -0
  27. data/lib/rlang/parser/attr.rb +9 -13
  28. data/lib/rlang/parser/const.rb +105 -1
  29. data/lib/rlang/parser/cvar.rb +11 -7
  30. data/lib/rlang/parser/data.rb +17 -6
  31. data/lib/rlang/parser/export.rb +4 -3
  32. data/lib/rlang/parser/ext/integer.rb +3 -1
  33. data/lib/rlang/parser/ext/type.rb +30 -1
  34. data/lib/rlang/parser/global.rb +10 -2
  35. data/lib/rlang/parser/ivar.rb +3 -7
  36. data/lib/rlang/parser/klass.rb +8 -35
  37. data/lib/rlang/parser/method.rb +36 -12
  38. data/lib/rlang/parser/module.rb +143 -0
  39. data/lib/rlang/parser/wgenerator.rb +462 -168
  40. data/lib/rlang/parser/wnode.rb +387 -142
  41. data/lib/rlang/parser/wtype.rb +30 -7
  42. data/lib/rlang/parser.rb +506 -231
  43. data/lib/rlang/version.rb +1 -1
  44. data/lib/rlang.rb +3 -0
  45. data/lib/ruby/mirror/rstring.rb +16 -0
  46. data/lib/utils/exceptions.rb +12 -0
  47. data/rlang.gemspec +4 -4
  48. metadata +25 -13
  49. data/lib/rlang/lib.rb +0 -11
@@ -12,6 +12,9 @@
12
12
 
13
13
  require_relative '../../utils/log'
14
14
  require_relative './wnode'
15
+ require_relative './klass'
16
+ require_relative './module'
17
+ require_relative './module'
15
18
 
16
19
  module Rlang::Parser
17
20
 
@@ -19,26 +22,29 @@ module Rlang::Parser
19
22
  :+ => :add,
20
23
  :- => :sub,
21
24
  :* => :mul,
22
- :/ => :div_u,
23
- :% => :rem_u,
24
25
  :& => :and,
25
26
  :| => :or,
26
27
  :^ => :xor,
27
- :>> => :shr_u,
28
28
  :<< => :shl
29
29
  }
30
30
 
31
+ ARITHMETIC_SIGNED_OPS_MAP = {
32
+ :/ => :div_x,
33
+ :% => :rem_x,
34
+ :>> => :shr_x
35
+ }
36
+
31
37
  RELATIONAL_OPS_MAP = {
32
38
  :== => :eq,
33
39
  :!= => :ne,
34
- :'<s' => :lt_s,
35
- :< => :lt_u,
36
- :'>s' => :gt_s,
37
- :> => :gt_u,
38
- :'<=s' => :le_s,
39
- :<= => :le_u,
40
- :'>=s' => :ge_s,
41
- :>= => :ge_u
40
+
41
+ }
42
+
43
+ RELATIONAL_SIGNED_OPS_MAP = {
44
+ :< => :lt_x,
45
+ :> => :gt_x,
46
+ :<= => :le_x,
47
+ :>= => :ge_x
42
48
  }
43
49
 
44
50
  BOOLEAN_OPS_MAP = {
@@ -47,19 +53,40 @@ module Rlang::Parser
47
53
  }
48
54
 
49
55
  UNARY_OPS_MAP = {
50
- :'!' => :eqz
56
+ :'!' => :eqz,
57
+ :'-@' => :sub # special case for unary - turned into (sub 0 x)
51
58
  }
52
59
 
53
- ALL_OPS_MAP = [*ARITHMETIC_OPS_MAP, *RELATIONAL_OPS_MAP, *BOOLEAN_OPS_MAP, *UNARY_OPS_MAP].to_h
60
+ SIGNED_OPS = {
61
+ signed: {div_x: :div_s, rem_x: :rem_s, shr_x: :shr_s, lt_x: :lt_s, gt_x: :gt_s,
62
+ le_x: :le_s, ge_x: :ge_s},
63
+ unsigned: {div_x: :div_u, rem_x: :rem_u, shr_x: :shr_u, lt_x: :lt_u, gt_x: :gt_u,
64
+ le_x: :le_u, ge_x: :ge_u},
65
+ }
66
+
67
+ # Operators that can be legally used when doing
68
+ # arithmetic on class instance (= object) pointers
69
+ # Only unsigned relational operators make sense as pointers
70
+ # are by nature unsigned integers
71
+ LEGAL_CLASS_WASM_OPS = [:eq, :ne, :lt_u, :gt_u, :le_u, :ge_u, :add, :sub]
72
+
73
+ # All operators with signed / unsigned variants
74
+ ALL_SIGNED_OPS_MAP = [*ARITHMETIC_SIGNED_OPS_MAP, *RELATIONAL_SIGNED_OPS_MAP].to_h
75
+
76
+ # All operators in on hash
77
+ ALL_OPS_MAP = [*ARITHMETIC_OPS_MAP, *ARITHMETIC_SIGNED_OPS_MAP, *RELATIONAL_OPS_MAP, *RELATIONAL_SIGNED_OPS_MAP,
78
+ *BOOLEAN_OPS_MAP, *UNARY_OPS_MAP].to_h
54
79
 
55
80
  # Matrix of how to cast a WASM type to another
56
81
  CAST_OPS = {
57
- I32: { I32: :cast_nope, I64: :cast_extend, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_wtype, none: :cast_error},
58
- I64: { I32: :cast_wrap, I64: :cast_nope, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
59
- F32: { I32: :cast_notyet, I64: :cast_notyet, F32: :cast_nope, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
60
- F64: { I32: :cast_notyet, I64: :cast_notyet, F32: :cast_notyet, F64: :cast_nope, Class: :cast_error, none: :cast_error},
61
- Class: { I32: :cast_wtype, I64: :cast_extend, F32: :cast_error, F64: :cast_error, Class: :cast_wtype, none: :cast_error},
62
- none: { I32: :cast_error, I64: :cast_error, F32: :cast_error, F64: :cast_error, Class: :cast_error, none: :cast_error},
82
+ UI32: { UI32: :cast_nope, I32: :cast_wtype, UI64: :cast_extend, I64: :cast_extend, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_wtype, none: :cast_error},
83
+ I32: { UI32: :cast_wtype, I32: :cast_nope, UI64: :cast_extend, I64: :cast_extend, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_wtype, none: :cast_error},
84
+ UI64: { UI32: :cast_wrap, I32: :cast_wrap, UI64: :cast_nope, I64: :cast_wtype, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
85
+ I64: { UI32: :cast_wrap, I32: :cast_wrap, UI64: :cast_wtype, I64: :cast_nope, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
86
+ F32: { UI32: :cast_notyet, I32: :cast_notyet, UI64: :cast_notyet, I64: :cast_notyet, F32: :cast_nope, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
87
+ F64: { UI32: :cast_notyet, I32: :cast_notyet, UI64: :cast_notyet, I64: :cast_notyet, F32: :cast_notyet, F64: :cast_nope, Class: :cast_error, none: :cast_error},
88
+ Class: { UI32: :cast_wtype, I32: :cast_wtype, UI64: :cast_extend, I64: :cast_extend, F32: :cast_error, F64: :cast_error, Class: :cast_wtype, none: :cast_error},
89
+ none: { UI32: :cast_error, I32: :cast_error, UI64: :cast_error, I64: :cast_error, F32: :cast_error, F64: :cast_error, Class: :cast_error, none: :cast_error},
63
90
  }
64
91
 
65
92
  # Rlang class size method name
@@ -67,10 +94,10 @@ module Rlang::Parser
67
94
 
68
95
  # new template when object size > 0
69
96
  NEW_TMPL = %q{
70
- result :Object, :alloc, :%{default_wtype}
97
+ result :Object, :allocate, :%{default_wtype}
71
98
  def self.new(%{margs})
72
- result :%{class_name}
73
- object_ptr = Object.alloc(%{class_name}._size_).cast_to(:%{class_name})
99
+ result :"%{class_name}"
100
+ object_ptr = Object.allocate(%{class_name}._size_).cast_to(:"%{class_name}")
74
101
  object_ptr.initialize(%{margs})
75
102
  return object_ptr
76
103
  end
@@ -80,14 +107,13 @@ module Rlang::Parser
80
107
  # use 0 as the _self_ address in memory. It should never
81
108
  # be used anyway
82
109
  NEW_ZERO_TMPL = %q{
83
- result :Object, :alloc, :%{default_wtype}
84
- def self.new(%{margs})
85
- result :%{class_name}
86
- object_ptr = 0.cast_to(:%{class_name})
87
- object_ptr.initialize(%{margs})
88
- return object_ptr
89
- end
90
- }
110
+ def self.new(%{margs})
111
+ result :"%{class_name}"
112
+ object_ptr = 0.cast_to(:"%{class_name}")
113
+ object_ptr.initialize(%{margs})
114
+ return object_ptr
115
+ end
116
+ }
91
117
 
92
118
  # Do nothing initialize method
93
119
  DUMB_INIT_TMPL = %q{
@@ -101,6 +127,11 @@ module Rlang::Parser
101
127
  String.new(%{ptr}, %{length})
102
128
  }
103
129
 
130
+ # Dynamically allocate a string object
131
+ ARRAY_NEW_TMPL = %q{
132
+ Array%{elt_size_in_bits}.new(%{ptr}, %{length})
133
+ }
134
+
104
135
  # Generate the wasm nodes and tree structure
105
136
  # ***IMPORTANT NOTE***
106
137
  # Unless otherwise stated all methods receive
@@ -110,25 +141,157 @@ module Rlang::Parser
110
141
  class WGenerator
111
142
  include Log
112
143
  attr_accessor :parser
113
- attr_reader :root
144
+ attr_reader :root, :wn_imports, :wn_exports, :wn_globals, :wn_data, :wn_code
114
145
 
115
146
  def initialize(parser)
116
147
  @parser = parser
117
148
  @root = WTree.new().root
118
149
  @new_count = 0
119
150
  @static_count = 0
120
- end
121
151
 
122
- def klass(wnode, class_name)
152
+ # Create section wnodes
153
+ @wn_imports = WNode.new(:imports, @root)
154
+ @wn_memory = WNode.new(:memory, @root)
155
+ @wn_exports = WNode.new(:exports, @root)
156
+ @wn_globals = WNode.new(:globals, @root)
157
+ @wn_data = WNode.new(:data, @root)
158
+
159
+ # Module code generation
160
+ @root.c(:module, module: parser.config[:module])
161
+
162
+ # Memory code generation
163
+ WNode.new(:insn, @wn_memory). \
164
+ c(:memory, min: parser.config[:memory_min], max: parser.config[:memory_max])
165
+
166
+ # define Object class and Kernel modules
167
+ # and include Kernel in Object
168
+ wn_object_class = self.klass(@root, [:Object], [])
169
+ @object_class = wn_object_class.klass
170
+ @root.klass = @object_class
171
+ wn_kernel_module = self.module(@root, [:Kernel])
172
+ self.include(wn_object_class, [:Kernel])
173
+
174
+ # Create Class and Module classes
175
+ # And Class inherits from module
176
+ self.klass(@root, [:Module], [:Object])
177
+ self.klass(@root, [:Class], [:Module])
178
+ end
179
+
180
+ # Create class and its basic methods (new, initialize and _size_)
181
+ def klass(wnode, class_path, super_class_path)
182
+ logger.debug "Defining klass #{class_path} < #{super_class_path}"
183
+ # See if class already created
184
+ if (k = wnode.find_class_or_module(class_path))
185
+ return k.wnode
186
+ end
187
+
188
+ # Make sure super class is known like Ruby does
189
+ if super_class_path.empty?
190
+ # special case to bootstrap Object class
191
+ if (class_path == [:Object] && wnode.in_root_scope?)
192
+ sk = nil
193
+ else
194
+ sk = @object_class
195
+ super_class_path << sk.path_name
196
+ end
197
+ else
198
+ sk = wnode.find_class_or_module(super_class_path)
199
+ raise "Unknown super class #{super_class_path}" unless sk
200
+ end
123
201
  # Create class object and class wnode if it doesn't exist yet
124
- k = wnode.find_or_create_class(class_name)
125
- # Create the Class.new method object too (not
126
- # the code yet in case the end user code defines
127
- # its own implementation in the class body)
128
- k.wnode.find_or_create_method(:new, k.name, k.wtype, :class)
202
+ # only one level deep class for now so scope class is always
203
+ # Object class
204
+ if (class_path == [:Object] && wnode.in_root_scope?)
205
+ k = wnode.create_class(class_path, super_class_path)
206
+ else
207
+ k = wnode.find_or_create_class(class_path, super_class_path)
208
+ end
209
+ # make sure the super class is correct in case class
210
+ # was previously declared in a result directive where
211
+ # no super class can be specified
212
+ k.super_class = sk if sk
213
+ # Create methods Class::new, Class#initialize and Class::_size_
214
+ # (do not generate the code yet as the end user code may
215
+ # define its own implementation in the class body)
216
+ k.wnode.find_or_create_method(k, :new, :class, k.wtype, true)
217
+ k.wnode.find_or_create_method(k, :_size_, :class, WType::DEFAULT, true)
218
+ k.wnode.find_or_create_method(k, :initialize, :instance, WType.new(:none), true)
129
219
  k.wnode
130
220
  end
131
221
 
222
+ def comments(wnode, comments)
223
+ # The gsub below is to handle =begin...=end block comments
224
+ comments.each do |c|
225
+ WNode.new(:comment, wnode).c(:comment, text: c.text.sub(/^\s*#/,'').gsub("\n", "\n;;"))
226
+ end
227
+ end
228
+
229
+ # Create module object and module wnode
230
+ # if it doesn't exist yet
231
+ def module(wnode, module_path)
232
+ m = wnode.find_or_create_module(module_path)
233
+ m.wnode
234
+ end
235
+
236
+ def include(wnode, module_path)
237
+ m = wnode.find_module(module_path)
238
+ raise "Unknown module #{module_path}. Please require module first." \
239
+ unless m
240
+ wnc = wnode.class_or_module_wnode
241
+ raise "Cannot find scope class/module for included module!!" unless wnc
242
+ wnc.klass.include(m)
243
+ m.included!
244
+ wnc
245
+ end
246
+
247
+ def prepend(wnode, module_path)
248
+ m = wnode.find_module(module_path)
249
+ raise "Unknown module #{module_path}. Please require module first." \
250
+ unless m
251
+ wnc = wnode.class_or_module_wnode
252
+ raise "Cannot find scope class/module for prepended module!!" unless wnc
253
+ wnc.klass.prepend(m)
254
+ m.prepended!
255
+ wnc
256
+ end
257
+
258
+ def extend(wnode, module_path)
259
+ m = wnode.find_module(module_path)
260
+ raise "Cannot find module #{module_path}. Please require module first." \
261
+ unless m
262
+ wnc = wnode.class_or_module_wnode
263
+ raise "Cannot find scope class/module for included module!!" unless wnc
264
+ wnc.klass.extend(m)
265
+ m.extended!
266
+ wnc
267
+ end
268
+
269
+ # Ahead of time method declaration and return type
270
+ # Create corresponding classes and method objects as we known we'll
271
+ # be calling them later on
272
+ def declare_method(wnode, wtype, method_name, result_type)
273
+ class_path = wtype.class_path
274
+ logger.debug "Declaring method #{method_name} in class #{class_path}"
275
+ klass = WNode.root.find_class_or_module(class_path)
276
+ raise "Can't find class or module #{class_path} in method declaration" unless klass
277
+ method_types = []
278
+ if method_name[0] == '#'
279
+ method_types << :instance
280
+ method_types << :class if klass.const.module?
281
+ mth_name = method_name[1..-1].to_sym
282
+ else
283
+ method_types << :class
284
+ method_types << :instance if klass.const.module?
285
+ mth_name = method_name.to_sym
286
+ end
287
+ mth = method_types.each do |mt|
288
+ (m = wnode.find_or_create_method(klass, mth_name, mt, nil)).wtype = WType.new(result_type)
289
+ logger.debug "Declared #{mt} method #{m.name} in class #{m.klass.name} with wtype #{m.wtype.name}"
290
+ m
291
+ end
292
+ mth
293
+ end
294
+
132
295
  # Postprocess ivars
133
296
  # (called at end of class parsing)
134
297
  def ivars_setup(wnode)
@@ -147,11 +310,12 @@ module Rlang::Parser
147
310
  # generate code for class attributes
148
311
  # (called at end of class parsing)
149
312
  def def_attr(wnode)
150
- wnc = wnode.class_wnode
313
+ klass = wnode.find_current_class_or_module()
314
+ wnc = klass.wnode
151
315
  raise "Cannot find class for attributes definition!!" unless wnc
152
316
  # Process each declared class attribute
153
- wnc.klass.attrs.each do |attr|
154
- logger.debug("Generating accessors for attribute #{wnc.klass.name}\##{attr.name}")
317
+ klass.attrs.each do |attr|
318
+ logger.debug("Generating accessors for attribute #{klass.name}\##{attr.name}")
155
319
  # Generate getter and setter methods wnode
156
320
  # unless method already implemented by user
157
321
  if attr.setter
@@ -173,13 +337,13 @@ module Rlang::Parser
173
337
  # Also generate the Class::_size_ method
174
338
  # (needed for dynamic memory allocation
175
339
  # by Object.allocate)
176
- size_method = wnc.find_or_create_method(SIZE_METHOD, wnc.klass.name, WType::DEFAULT, :class)
340
+ size_method = wnc.find_or_create_method(klass, SIZE_METHOD, :class, WType::DEFAULT)
177
341
  unless size_method.wnode
178
- logger.debug("Generating #{size_method.class_name}\##{size_method.name}")
179
- wns = WNode.new(:insn, wnc, true)
180
- wns.wtype = WType::DEFAULT
342
+ logger.debug("Generating #{size_method.klass.name}\##{size_method.name}")
343
+ wns = WNode.new(:insn, wnc)
344
+ wns.wtype = WType::DEFAULT
181
345
  wns.c(:class_size, func_name: size_method.wasm_name,
182
- wtype: wns.wasm_type, size: wnc.class_size)
346
+ wasm_type: wns.wasm_type, size: wnc.class_size)
183
347
  size_method.wnode = wns
184
348
  end
185
349
  end
@@ -189,7 +353,7 @@ module Rlang::Parser
189
353
  wnc = wnode.class_wnode
190
354
  wn_set = WNode.new(:insn, wnc, true)
191
355
  wn_set.c(:attr_setter, func_name: attr.setter.wasm_name,
192
- attr_name: attr.wasm_name, wtype: attr.wasm_type,
356
+ attr_name: attr.wasm_name, wasm_type: attr.wasm_type,
193
357
  offset: attr.offset)
194
358
  wn_set
195
359
  end
@@ -199,34 +363,50 @@ module Rlang::Parser
199
363
  wnc = wnode.class_wnode
200
364
  wn_get = WNode.new(:insn, wnc, true)
201
365
  wn_get.c(:attr_getter, func_name: attr.getter.wasm_name,
202
- attr_name: attr.wasm_name, wtype: attr.wasm_type,
203
- offset: attr.offset)
366
+ wasm_type: attr.wasm_type, offset: attr.offset)
204
367
  wn_get
205
368
  end
206
369
 
207
- def instance_method(wnode, method)
208
- logger.debug("Generating wnode for instance method #{method.inspect}")
370
+ def def_method(wnode, method_name, method_type)
371
+ logger.debug("Defining #{method_type} method #{method_name}...")
372
+ if (method = wnode.find_method(nil, method_name, method_type, true))
373
+ logger.warn "Redefining #{method.klass.name},#{method_name}" if method.wnode
374
+ else
375
+ method = wnode.create_method(nil, method_name, method_type, nil, true)
376
+ end
377
+
378
+ # If it's the main method, give it the proper name in export if
379
+ # specified on command line
380
+ if method.klass.path_name == :Object && method.name == :main
381
+ method.export_name = @parser.config[:start]
382
+ end
383
+
384
+ # Generate method definition wnode
385
+ logger.debug("Generating wnode for #{method_type} method #{method_name}")
209
386
  wn = WNode.new(:method, wnode)
210
387
  method.wnode = wn
211
- wn.method = method # must be set before calling func_name
212
388
  wn.wtype = method.wtype
213
389
  wn.c(:func, func_name: wn.method.wasm_name)
214
- # Also declare a "hidden" parameter representing the
215
- # pointer to the instance (always default wtype)
216
- wn.create_marg(:_self_)
217
- logger.debug("Building instance method: wn.wtype #{wn.wtype}, wn.method #{wn.method}")
390
+
391
+ # Instance methods 1st argument is always self
392
+ wn.create_marg(:_self_) if method.instance?
393
+ logger.debug("Built #{method_type} method definition: wn.wtype #{wn.wtype}, wn.method #{wn.method}")
218
394
  wn
219
395
  end
220
396
 
221
- def class_method(wnode, method)
222
- logger.debug("Generating wnode for class method #{method}")
223
- wn = WNode.new(:method, wnode)
224
- method.wnode = wn
225
- wn.method = method # must be set before calling func_name
226
- wn.wtype = method.wtype
227
- wn.c(:func, func_name: wn.method.wasm_name)
228
- logger.debug("Building class method: wn.wtype #{wn.wtype}, wn.method #{wn.method}")
229
- wn
397
+ def import_method(wnode, module_name, function_name)
398
+ # Create the import node
399
+ (wn_import = WNode.new(:insn, self.wn_imports)).c(:import, module_name: module_name, function_name: function_name)
400
+ wn_import.link = wnode
401
+ wnode.method.imported!
402
+ # now silence the method wnode so that
403
+ # it doesn't generate WASM code in the code section
404
+ wnode.silence!
405
+ wn_import
406
+ end
407
+
408
+ def export_method(wnode, export_name)
409
+ wnode.method.export!(export_name)
230
410
  end
231
411
 
232
412
  def params(wnode)
@@ -236,7 +416,7 @@ module Rlang::Parser
236
416
  logger.debug("Prepending param #{marg}")
237
417
  wn = WNode.new(:insn, wnm, true)
238
418
  wn.wtype = marg.wtype
239
- wn.c(:param, name: marg.wasm_name)
419
+ wn.c(:param, name: marg.wasm_name, wasm_type: wn.wasm_type)
240
420
  end
241
421
  end
242
422
 
@@ -244,7 +424,7 @@ module Rlang::Parser
244
424
  unless wnode.wtype.blank?
245
425
  wn = WNode.new(:insn, wnode, true)
246
426
  wn.wtype = wnode.wtype
247
- wn.c(:result)
427
+ wn.c(:result, wasm_type: wn.wasm_type)
248
428
  end
249
429
  end
250
430
 
@@ -254,7 +434,7 @@ module Rlang::Parser
254
434
  logger.debug("Prepending local #{lvar.inspect}")
255
435
  wn = WNode.new(:insn, wnm, true)
256
436
  wn.wtype = lvar.wtype
257
- wn.c(:local, name: lvar.wasm_name)
437
+ wn.c(:local, name: lvar.wasm_name, wasm_type: wn.wasm_type)
258
438
  end
259
439
  end
260
440
 
@@ -268,19 +448,26 @@ module Rlang::Parser
268
448
  # Set constant
269
449
  def casgn(wnode, const)
270
450
  (wn = WNode.new(:insn, wnode)).wtype = const.wtype
271
- wn.c(:store, wtype: const.wtype)
451
+ wn.c(:store, wasm_type: const.wtype)
272
452
  WNode.new(:insn, wn).c(:addr, value: const.address)
273
453
  wn
274
454
  end
275
455
 
276
- # Get class variable
456
+ # Get constant
277
457
  def const(wnode, const)
278
458
  (wn = WNode.new(:insn, wnode)).wtype = const.wtype
279
- wn.c(:load, wtype: const.wtype, var_name: const.wasm_name)
459
+ wn.c(:load, wasm_type: const.wasm_type)
280
460
  WNode.new(:insn, wn).c(:addr, value: const.address)
281
461
  wn
282
462
  end
283
463
 
464
+ # Get constant addres
465
+ def const_addr(wnode, const)
466
+ (wn = WNode.new(:insn, wnode)).wtype = const.wtype
467
+ wn.c(:addr, value: const.address)
468
+ wn
469
+ end
470
+
284
471
  # Set Global variable
285
472
  def gvasgn(wnode, gvar)
286
473
  (wn = WNode.new(:insn, wnode)).wtype = gvar.wtype
@@ -298,7 +485,7 @@ module Rlang::Parser
298
485
  # Call setter (on attr or instance variable)
299
486
  # This is the same as calling the corresponding setter
300
487
  def call_setter(wnode, wnode_recv, attr)
301
- wn = self.call(wnode, wnode_recv.wtype.name, attr.setter_name, :instance)
488
+ wn = self.send_method(wnode, wnode_recv.wtype, attr.setter_name, :instance)
302
489
  # First argument of the setter must be the receiver
303
490
  wnode_recv.reparent_to(wn)
304
491
  wn
@@ -307,7 +494,7 @@ module Rlang::Parser
307
494
  # Call getter (on attr or instance variable)
308
495
  # This is the same as calling the corresponding getter
309
496
  def call_getter(wnode, wnode_recv, attr)
310
- wn = self.call(wnode, wnode_recv.wtype.name, attr.getter_name, :instance)
497
+ wn = self.send_method(wnode, wnode_recv.wtype, attr.getter_name, :instance)
311
498
  # First argument of the getter must always be the receiver
312
499
  wnode_recv.reparent_to(wn)
313
500
  wn
@@ -316,7 +503,7 @@ module Rlang::Parser
316
503
  # Set instance variable
317
504
  def ivasgn(wnode, ivar)
318
505
  (wn = WNode.new(:insn, wnode)).wtype = ivar.wtype
319
- wn.c(:store_offset, wtype: ivar.wasm_type, offset: lambda { ivar.offset })
506
+ wn.c(:store_offset, wasm_type: ivar.wasm_type, offset: lambda { ivar.offset })
320
507
  self._self_(wn)
321
508
  wn
322
509
  end
@@ -324,7 +511,7 @@ module Rlang::Parser
324
511
  # Get instance variable.
325
512
  def ivar(wnode, ivar)
326
513
  (wn = WNode.new(:insn, wnode)).wtype = ivar.wtype
327
- wn.c(:load_offset, wtype: ivar.wasm_type, offset: lambda { ivar.offset })
514
+ wn.c(:load_offset, wasm_type: ivar.wasm_type, offset: lambda { ivar.offset })
328
515
  self._self_(wn)
329
516
  wn
330
517
  end
@@ -334,7 +521,7 @@ module Rlang::Parser
334
521
  # an empty expression node to populate later
335
522
  def cvasgn(wnode, cvar)
336
523
  (wn = WNode.new(:insn, wnode)).wtype = cvar.wtype
337
- wn.c(:store, wtype: cvar.wtype)
524
+ wn.c(:store, wasm_type: cvar.wasm_type)
338
525
  WNode.new(:insn, wn).c(:addr, value: cvar.address)
339
526
  wn
340
527
  end
@@ -342,22 +529,30 @@ module Rlang::Parser
342
529
  # Get class variable
343
530
  def cvar(wnode, cvar)
344
531
  (wn = WNode.new(:insn, wnode)).wtype = cvar.wtype
345
- wn.c(:load, wtype: cvar.wtype, var_name: cvar.wasm_name)
532
+ wn.c(:load, wasm_type: cvar.wasm_type)
346
533
  WNode.new(:insn, wn).c(:addr, value: cvar.address)
347
534
  wn
348
535
  end
349
536
 
537
+
538
+ # Get class variable address
539
+ def cvar_addr(wnode, cvar)
540
+ (wn = WNode.new(:insn, wnode)).wtype = cvar.wtype
541
+ wn.c(:addr, value: cvar.address)
542
+ wn
543
+ end
544
+
350
545
  # Create the local variable storage node
351
546
  def lvasgn(wnode, lvar)
352
547
  (wn = WNode.new(:insn, wnode)).wtype = lvar.wtype
353
- wn.c(:local_set, wtype: lvar.wtype, var_name: lvar.wasm_name)
548
+ wn.c(:local_set, var_name: lvar.wasm_name)
354
549
  wn
355
550
  end
356
551
 
357
552
  # Read local variable
358
553
  def lvar(wnode, lvar)
359
554
  (wn = WNode.new(:insn, wnode)).wtype = lvar.wtype
360
- wn.c(:local_get, wtype: lvar.wtype, var_name: lvar.wasm_name)
555
+ wn.c(:local_get, var_name: lvar.wasm_name)
361
556
  wn
362
557
  end
363
558
 
@@ -374,42 +569,42 @@ module Rlang::Parser
374
569
 
375
570
  def int(wnode, wtype, value)
376
571
  (wn = WNode.new(:insn, wnode)).wtype = wtype
377
- wn.c(:const, wtype: wtype, value: value)
572
+ wn.c(:const, wasm_type: wn.wasm_type, value: value)
378
573
  wn
379
574
  end
380
575
 
381
576
  def float(wnode, wtype, value)
382
577
  (wn = WNode.new(:insn, wnode)).wtype = wtype
383
- wn.c(:const, wtype: wtype, value: value)
578
+ wn.c(:const, wasm_type: wn.wasm_type, value: value)
384
579
  wn
385
580
  end
386
581
 
387
582
  # Generate a phony node (generally used to create
388
583
  # a wnode subtree under the phony node and later
389
584
  # reparent it to the proper place in the wtree)
390
- def phony(wnode)
391
- phony = WNode.new(:none, wnode)
392
- phony
585
+ def phony(wnode, type=:none)
586
+ WNode.new(type, wnode)
393
587
  end
394
588
 
395
- # Static string allocation
396
- def string_static(string, data_label)
397
- # Allocate string itself and the attributes
398
- # of String object pointing to that string
399
- data_stg = DAta.append(data_label.to_sym, string)
400
- data_stg
589
+ # Static string data allocation
590
+ def allocate_string_static_data(string, data_label)
591
+ # if string is empty do not allocate any memory space
592
+ DAta.append(data_label.to_sym, string) unless string.empty?
401
593
  end
402
594
 
403
595
  # Static new string object
404
596
  def string_static_new(wnode, string)
405
- klass = wnode.find_class(nil)
597
+ klass = wnode.find_current_class_or_module()
406
598
  data_label = "#{klass.name}_string_#{@static_count += 1}"
407
- # Statically
408
- data_stg = self.string_static(string, data_label)
599
+ # Allocate string data statically
600
+ # Note : data_stg is nil if string is empty
601
+ data_stg = self.allocate_string_static_data(string, data_label)
409
602
  # align on :I32 boundary
603
+ # then allocate the String object attributes
604
+ # and set them up
410
605
  DAta.align(4)
411
606
  data_len = DAta.append("#{data_label}_len".to_sym, string.length, WType::DEFAULT)
412
- data_ptr = DAta.append("#{data_label}_ptr".to_sym, data_stg.address, WType::DEFAULT)
607
+ data_ptr = DAta.append("#{data_label}_ptr".to_sym, data_stg ? data_stg.address : 0, WType::DEFAULT)
413
608
  # Generate address wnode
414
609
  (wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: data_len.address)
415
610
  wn_object_addr.wtype = WType.new(:String)
@@ -418,27 +613,75 @@ module Rlang::Parser
418
613
 
419
614
  # Dynamic new string object
420
615
  def string_dynamic_new(wnode, string)
421
- klass = wnode.find_class(nil)
616
+ klass = wnode.find_current_class_or_module()
422
617
  data_label = "#{klass.name}_string_#{@static_count += 1}"
423
- data_stg = self.string_static(string, data_label)
618
+ # Note : data_stg is nil if string is empty
619
+ data_stg = self.allocate_string_static_data(string, data_label)
424
620
  string_new_source = STRING_NEW_TMPL % {
425
- string: string,
426
- ptr: data_stg.address,
621
+ ptr: data_stg ? data_stg.address : 0,
427
622
  length: string.length
428
623
  }
429
624
  #puts string_new_source;exit
430
- wn_string = self.parser.parse(string_new_source, wnode)
625
+ self.parser.parse(string_new_source, wnode)
626
+ end
627
+
628
+ # Static array data allocation
629
+ def allocate_array_static_data(array, data_label)
630
+ # Append each array element to the same data section
631
+ label = data_label.to_sym
632
+ data_arr = nil
633
+ # Do not allocate memory space if array is empty
634
+ array.each { |elt| data_arr = DAta.append(label, elt) }
635
+ data_arr
636
+ end
637
+
638
+ # Static new array object
639
+ def array_static_new(wnode, array)
640
+ klass = wnode.find_current_class_or_module()
641
+ data_label = "#{klass.name}_array_#{@static_count += 1}"
642
+ # Allocate array data statically
643
+ # Note : data_arr is nil if string is empty
644
+ data_arr = self.allocate_array_static_data(array, data_label)
645
+ # align on :I32 boundary
646
+ # then allocate the Array object attributes
647
+ # and set them up
648
+ DAta.align(4)
649
+ data_count = DAta.append("#{data_label}_count".to_sym, array.length, WType::DEFAULT)
650
+ data_ptr = DAta.append("#{data_label}_ptr".to_sym, data_arr ? data_arr.address : 0, WType::DEFAULT)
651
+ # Generate address wnode
652
+ (wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: data_count.address)
653
+ wn_object_addr.wtype = WType.new(:Array32)
654
+ wn_object_addr
655
+ end
656
+
657
+ # Dynamic new array object
658
+ def array_dynamic_new(wnode, array)
659
+ klass = wnode.find_current_class_or_module()
660
+ data_label = "#{klass.name}_array_#{@static_count += 1}"
661
+ # Note : data_arr is nil if string is empty
662
+ data_arr = self.allocate_array_static_data(array, data_label)
663
+ array_new_source = ARRAY_NEW_TMPL % {
664
+ elt_size_in_bits: WTYPE::DEFAULT.size * 8,
665
+ ptr: data_arr ? data_arr.address : 0,
666
+ count: array.length
667
+ }
668
+ #puts array_new_source;exit
669
+ self.parser.parse(array_new_source, wnode)
431
670
  end
432
671
 
672
+ # TYPE CASTING methods
433
673
  # All the cast_xxxx methods below returns
434
674
  # the new wnode doing the cast operation
435
675
  # or the same wnode if there is no additional code
436
676
  # for the cast operation
677
+
678
+ # No casting. Return node as is.
437
679
  def cast_nope(wnode, wtype, signed)
438
- # Do nothing
439
680
  wnode
440
681
  end
441
682
 
683
+ # Cast by extending to a wtype of larger bit size
684
+ # (e.g. I32 to I64)
442
685
  def cast_extend(wnode, wtype, signed)
443
686
  if (wnode.template == :const)
444
687
  # it's a WASM const, simply change the wtype
@@ -447,15 +690,19 @@ module Rlang::Parser
447
690
  else
448
691
  wn_cast_op = wnode.insert(:insn)
449
692
  wn_cast_op.wtype = wtype
450
- wn_cast_op.c(signed ? :extend_i32_s : :extend_i32_u , wtype: wtype)
693
+ wn_cast_op.c(signed ? :extend_i32_s : :extend_i32_u , wasm_type: wn_cast_op.wasm_type)
451
694
  end
452
695
  wn_cast_op
453
696
  end
454
697
 
698
+ # Cast by simply changing the node wtype
699
+ # No change in native WASM type
700
+ # (e.g. casting an object pointer to I32)
455
701
  def cast_wtype(wnode, wtype, signed)
456
- if (wnode.wtype.default? && wtype.class?) ||
457
- (wnode.wtype.class? && wtype.default?) ||
458
- (wnode.wtype.class? && wtype.class?)
702
+ # Don't cast blindly. Check that source and target
703
+ # have the same bit size (e.g. Object pointers, I32, UI32
704
+ # are the same size, )
705
+ if wnode.wtype.size == wtype.size
459
706
  wnode.wtype = wtype
460
707
  else
461
708
  cast_error(wnode, wtype, signed)
@@ -463,6 +710,8 @@ module Rlang::Parser
463
710
  wnode
464
711
  end
465
712
 
713
+ # Cast by wraping a wtype to a smaller size
714
+ # (e.g. I64 to I32)
466
715
  def cast_wrap(wnode, wtype, signed)
467
716
  if (wnode.template == :const)
468
717
  # it's a WASM const, simply change the wtype
@@ -471,17 +720,19 @@ module Rlang::Parser
471
720
  else
472
721
  wn_cast_op = wnode.insert(:insn)
473
722
  wn_cast_op.wtype = wtype
474
- wn_cast_op.c(:wrap_i64, wtype: wtype)
723
+ wn_cast_op.c(:wrap_i64, wasm_type: wn_cast_op.wasm_type)
475
724
  end
476
725
  wn_cast_op
477
726
  end
478
727
 
728
+ # Cast operation not yet supported
479
729
  def cast_notyet(wnode, wtype, signed)
480
730
  raise "Type cast from #{wnode.wtype} to #{wtype} not supported yet"
481
731
  end
482
732
 
733
+ # Cast operation is invalid
483
734
  def cast_error(wnode, wtype, signed)
484
- raise "Cannot cast type #{src} to #{dest}. Time to fix your code :-)"
735
+ raise "Cannot cast type #{wnode.wtype} to #{wtype}. Time to fix your code :-)"
485
736
  end
486
737
 
487
738
  # cast an expression to a different type
@@ -503,42 +754,26 @@ module Rlang::Parser
503
754
  wn_cast_op
504
755
  end
505
756
 
506
- # before generating native operator Wasm code check
507
- # if the operator was overloaded
508
- def send_method(wnode, method_name, wtype=WType.new(:none))
509
- class_name = wtype.name
510
- if wnode.find_method(method_name, class_name, :instance)
511
- # it's an instance method call
512
- logger.debug "Overloaded operator #{method_name} found in class #{class_name}"
513
- wn_op = self.call(wnode, class_name, method_name, :instance)
514
- else
515
- # it's a native Wasm operator
516
- if ALL_OPS_MAP.has_key? method_name
517
- wn_op = self.native_operator(wnode, method_name, wtype)
518
- else
519
- raise "Unknown method '#{method_name}' in class #{class_name}"
520
- end
521
- end
522
- wn_op
523
- end
524
-
525
757
  # just create a wnode for the WASM operator
526
758
  # Do not set wtype or a code template yet,
527
759
  # wait until operands type is known (see
528
760
  # operands below)
529
761
  def native_operator(wnode, operator, wtype=WType.new(:none))
530
762
  if (op = ALL_OPS_MAP[operator])
531
- (wn_op = WNode.new(:insn, wnode)).c(:operator, operator: op)
532
- wn_op.wtype = wtype
763
+ (wn_op = WNode.new(:insn, wnode)).wtype = wtype
764
+ wn_op.c(:operator, wasm_type: wn_op.wasm_type, operator: op)
533
765
  logger.debug "Creating operator #{operator} wnode: #{wn_op}"
766
+ # special case for - unary operator transformed into (0 - x)
767
+ WNode.new(:insn, wn_op).c(:const, wasm_type: wn_op.wasm_type, value: 0) if operator == :-@
534
768
  wn_op
535
769
  else
536
770
  raise "operator '#{operator}' not supported"
537
771
  end
538
772
  end
539
773
 
540
- # finish the setting of the operator node and
541
- # attach operands
774
+ # Finish the setting of the operator node,
775
+ # attach operands and see if they need implicit
776
+ # type casting
542
777
  def operands(wnode_op, wnode_recv, wnode_args)
543
778
  logger.debug "Processing operands in operator wnode: #{wnode_op}..."
544
779
  # Do not post process operands if the operator
@@ -556,26 +791,48 @@ module Rlang::Parser
556
791
  #wnode_recv = wnode_op.children[0]
557
792
  #wnode_args = wnode_op.children[1..-1]
558
793
  # First find out the wtype that has precedence
559
- wtype = self.class.leading_wtype(wnode_recv, *wnode_args)
794
+ leading_wtype = self.class.leading_wtype(wnode_recv, *wnode_args)
560
795
 
561
- wnode_op.wtype = wtype
562
- logger.debug "leading type cast: #{wtype}"
796
+ wnode_op.wtype = leading_wtype
797
+ logger.debug "leading type cast: #{leading_wtype}"
563
798
 
564
799
  # Attach receiver and argument to the operator wnode
565
- # type casting them if necessary
566
- self.cast(wnode_recv, wtype).reparent_to(wnode_op)
567
- self.cast(wnode_args.first, wtype).reparent_to(wnode_op) unless wnode_args.empty?
800
+ # type casting them if necessary
801
+ # Note : normally for an operator there is only one argument
802
+ # but process args as if they were many one day.
803
+ logger.debug "Perform implicit type casting of receviver and operator arg(s)"
804
+ self.cast(wnode_recv, leading_wtype).reparent_to(wnode_op)
805
+ wnode_args.each do |wna|
806
+ self.cast(wna, leading_wtype).reparent_to(wnode_op)
807
+ end
808
+
809
+ # Once operands casting is done, see if we need the signed or unsigned
810
+ # version of the native operator
811
+ # NOTE : At this stage, after the operands casting both of them
812
+ # should either be signed or unsigned, hence the XNOR sanity check
813
+ # below
814
+ if ALL_SIGNED_OPS_MAP.values.include? op
815
+ if !((signed = wnode_recv.wtype.signed?) ^ (wnode_args.empty? ? true : wnode_args.first.wtype.signed?))
816
+ wnode_op.wargs[:operator] = SIGNED_OPS[signed ? :signed : :unsigned][op]
817
+ logger.debug "Receiver has wtype #{wnode_recv.wtype} / Argument has wtype #{wnode_args.first.wtype}"
818
+ logger.debug "Replacing #{op} operator with #{wnode_op.wargs[:operator]}"
819
+ op = wnode_op.wargs[:operator]
820
+ else
821
+ raise "Type mismatch between operands. Receiver is #{wnode_recv.wtype} and argument is #{wnode_args.first.wtype}"
822
+ end
823
+ end
568
824
 
569
825
  # if the receiver is a class object and not
570
826
  # a native integer then pointer arithmetic
571
827
  # applies (like in C)
572
828
  if wnode_recv.wtype.class?
573
- legal_ops = RELATIONAL_OPS_MAP.values + [:add, :sub]
574
- raise "Only #{legal_ops.join(', ')} operators are supported on objects (got #{op} in #{wnode_op})" \
575
- unless legal_ops.include?(op)
576
- # if + or - operator then multiply arg by size of object
577
- if [:add, :sub].include? wnode_op.wargs[:operator]
578
- (wn_mulop = WNode.new(:insn, wnode_op)).c(:operator, operator: :mul)
829
+ raise "Only #{LEGAL_CLASS_WASM_OPS.join(', ')} operators are supported on objects (got #{op} in #{wnode_op})" \
830
+ unless LEGAL_CLASS_WASM_OPS.include?(op)
831
+ # if :add or :sub operator then multiply arg by size of object
832
+ # like in C
833
+ if [:add, :sub].include? op
834
+ (wn_mulop = WNode.new(:insn, wnode_op)).wtype = WType::DEFAULT
835
+ wn_mulop.c(:operator, wasm_type: wn_mulop.wasm_type, operator: :mul)
579
836
  WNode.new(:insn, wn_mulop).c(:call, func_name: "$#{wnode_recv.wtype.name}::#{SIZE_METHOD}")
580
837
  wnode_args.first.reparent_to(wn_mulop)
581
838
  else
@@ -592,10 +849,10 @@ module Rlang::Parser
592
849
 
593
850
  # Statically allocate an object in data segment
594
851
  # with the size of the class
595
- def static_new(wnode, class_name)
596
- klass = wnode.find_class(class_name)
852
+ def static_new(wnode, class_path)
853
+ klass = wnode.find_class_or_module(class_path)
597
854
  if klass.size > 0
598
- data_label = "#{class_name}_new_#{@new_count += 1}"
855
+ data_label = "#{klass.path_name}_new_#{@new_count += 1}"
599
856
  data = DAta.new(data_label.to_sym, "\x00"*klass.wnode.class_size)
600
857
  address = data.address
601
858
  else
@@ -606,50 +863,87 @@ module Rlang::Parser
606
863
  end
607
864
  (wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: address)
608
865
  # VERY IMPORTANT the wtype of this node is the Class name !!!
609
- wn_object_addr.wtype = WType.new(class_name.to_sym)
866
+ wn_object_addr.wtype = WType.new(klass.path_name)
610
867
  wn_object_addr
611
868
  end
612
869
 
613
870
  # Create the dynamic new method. It allocates memory
614
871
  # for the object created and calls initialize
615
872
  def def_new(wnode_class)
616
- # no new method for native types
873
+ k = wnode_class.find_current_class_or_module()
874
+ logger.debug "Defining new method for #{k.name}"
875
+ # no need to define new method for native types
617
876
  return if wnode_class.klass.wtype.native?
618
- new_method = wnode_class.find_method(:new, wnode_class.class_name, :class)
619
- return if new_method.wnode # already implemented
620
-
621
- init_method = wnode_class.find_method(:initialize, wnode_class.class_name, :instance)
622
- logger.debug "Creating code for #{wnode_class.class_name}.new"
877
+ if (new_mth = wnode_class.find_method(k, :new, :class, true))
878
+ return if new_mth.implemented? # already implemented
879
+ end
880
+
881
+ logger.debug "Creating code for #{k.name}.new"
882
+ # Find initialize method and use the same method args for new
883
+ init_method = wnode_class.find_method(k, :initialize, :instance, true)
623
884
  new_tmpl = wnode_class.class_size.zero? ? NEW_ZERO_TMPL : NEW_TMPL
624
885
  new_source = new_tmpl % {
625
886
  default_wtype: WType::DEFAULT.name,
626
- class_name: wnode_class.class_name,
887
+ class_name: k.path_name,
627
888
  # Do not pass _self_ argument to the new method of course !!
628
889
  margs: init_method.margs.reject {|ma| ma._self_?}.map(&:name).join(', '),
629
890
  class_size: wnode_class.class_size
630
891
  }
631
- new_method.wnode = self.parser.parse(new_source, wnode_class)
892
+ new_mth.wnode = self.parser.parse(new_source, wnode_class)
632
893
  end
633
894
 
634
895
  # Define a dumb initialize method if not implemented
635
896
  # already in user code
636
897
  def def_initialize(wnode_class)
898
+ k = wnode_class.find_current_class_or_module()
637
899
  # no new/initialize method for native types
638
- return if WType.new(wnode_class.class_name).native?
900
+ return if WType.new(k.path_name).native?
639
901
  # generate code for a dumb initialize method if not defined
640
902
  # in user code
641
- unless wnode_class.find_method(:initialize, wnode_class.class_name, :instance)
642
- logger.debug "Creating MEthod and code for #{wnode_class.class_name}#initialize"
643
- init_source = DUMB_INIT_TMPL
644
- self.parser.parse(init_source, wnode_class)
903
+ if (init_mth = wnode_class.find_method(k, :initialize, :instance, true))
904
+ return if init_mth.wnode # already implemented
905
+ end
906
+ logger.debug "Creating MEthod and code for #{k.name}#initialize"
907
+ init_source = DUMB_INIT_TMPL
908
+ init_mth.wnode = self.parser.parse(init_source, wnode_class)
909
+ end
910
+
911
+ # generate code for method call
912
+ def send_method(wnode, class_path, method_name, method_type)
913
+ logger.debug "In call generator for #{class_path}::#{method_name}"
914
+ if k = wnode.find_class_or_module(class_path)
915
+ method = wnode.find_method(k, method_name, method_type)
916
+ if k.wtype.native? && ALL_OPS_MAP.has_key?(method_name)
917
+ # An Rlang method exist for this class but methods corresponding to
918
+ # Webassembly native operators applied to native Webassembly types
919
+ # (I32, I64, F32, F64) **cannot** be overriden by instance methods
920
+ # in Rlang code
921
+ if method
922
+ logger.warn "Rlang #{class_path}::#{method_name} method ignored. Native operator has precedence"
923
+ end
924
+ logger.debug "Apply native operator #{method_name} to native wtype #{class_path}"
925
+ wn_call = self.native_operator(wnode, method_name, WType.new(class_path))
926
+ elsif !k.wtype.native? && ALL_OPS_MAP.has_key?(method_name) && method.nil?
927
+ # Similarly if the Class is not a native type (a regular class)
928
+ # and the Class doesn't provide its own method implementation of the native
929
+ # operator then apply the native operands
930
+ logger.debug "Apply native operator #{method_name} to class found : #{class_path}"
931
+ wn_call = self.native_operator(wnode, method_name, WType.new(class_path))
932
+ elsif method
933
+ logger.debug "Found method #{method.name} in class #{method.klass.name}"
934
+ (wn_call = WNode.new(:insn, wnode)).c(:call, func_name: method.wasm_name)
935
+ wn_call.wtype = method.wtype
936
+ wn_call
937
+ else
938
+ raise "Unknown method '#{method_name}' in class #{class_path}"
939
+ end
940
+ elsif ALL_OPS_MAP.has_key? method_name
941
+ # It is a native Wasm operator
942
+ logger.debug "Native operator found : #{class_path}::#{method_name}"
943
+ wn_call = self.native_operator(wnode, method_name, WType.new(class_path))
944
+ else
945
+ raise "Unknown method '#{method_name}' in class #{class_path}"
645
946
  end
646
- end
647
-
648
- def call(wnode, class_name, method_name, method_type)
649
- method = wnode.find_or_create_method(method_name, class_name, nil, method_type)
650
- logger.debug "found method #{method}"
651
- (wn_call = WNode.new(:insn, wnode)).c(:call, func_name: method.wasm_name)
652
- wn_call.wtype = method.wtype
653
947
  wn_call
654
948
  end
655
949
 
@@ -682,8 +976,8 @@ module Rlang::Parser
682
976
  end
683
977
 
684
978
  def while(wnode)
685
- (wnb = WNode.new(:insn, wnode)).c(:block)
686
- (wnl = WNode.new(:insn, wnb)).c(:loop)
979
+ (wnb = WNode.new(:insn, wnode)).c(:block, label: wnb.set_label)
980
+ (wnl = WNode.new(:insn, wnb)).c(:loop, label: wnl.set_label)
687
981
  (wnbi = WNode.new(:insn, wnl)).c(:br_if, label: wnb.label)
688
982
  return wnb,wnbi,wnl
689
983
  end
@@ -693,7 +987,7 @@ module Rlang::Parser
693
987
  # negate the original while condition
694
988
  def while_cond(wnode, wnode_cond_exp)
695
989
  wn_eqz = WNode.new(:insn, wnode)
696
- wn_eqz.c(:eqz, wtype: wnode_cond_exp.wtype)
990
+ wn_eqz.c(:eqz, wasm_type: wnode_cond_exp.wasm_type)
697
991
  wnode_cond_exp.reparent_to(wn_eqz)
698
992
  wn_eqz
699
993
  end