rlang 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,61 +9,64 @@ require_relative './ivar'
9
9
  require_relative './wtype'
10
10
 
11
11
  module Rlang::Parser
12
- class WAttr
12
+ class Attr
13
13
  include Log
14
- attr_reader :name, :wtype, :getter, :setter, :ivar
14
+ attr_reader :name, :getter, :setter, :ivar
15
15
 
16
16
  # The name argument can either be the attribute name
17
17
  # (e.g. :size) or an ivar name (e.g. :@size)
18
18
  def initialize(class_wnode, name, wtype=WType::DEFAULT)
19
19
  @class_wnode = class_wnode
20
- if name.to_s[0] == '@'
21
- @ivar = IVar.new(@class_wnode, name, wtype)
22
- @name = @ivar.wattr_name
23
- else
24
- @name = name
25
- @ivar = IVar.new(@class_wnode, self.ivar_name, wtype)
26
- end
27
- @wtype = wtype
28
- # Also create the corresponding getter and setter
29
- # method objects (with default WType - wattr_type
30
- # directives might later change this wtype)
31
- # Don't generate WAT code yet
20
+ @name = name
21
+ @ivar = class_wnode.create_ivar(:"@#{name}", wtype)
22
+ @getter = nil
23
+ @setter = nil
24
+ @export = false
25
+ logger.debug "Class attribute #{name} created"
26
+ end
27
+
28
+ def attr_reader
32
29
  @getter = @class_wnode.find_or_create_method(self.getter_name, nil, wtype, :instance)
33
- @getter.instance!
30
+ @getter.export! if @export
34
31
  logger.debug "Getter created: #{@getter.inspect}"
32
+ @getter
33
+ end
35
34
 
35
+ def attr_writer
36
36
  @setter = @class_wnode.find_or_create_method(self.setter_name, nil, wtype, :instance)
37
- @setter.instance!
37
+ @setter.export! if @export
38
38
  logger.debug "Setter created: #{@setter.inspect}"
39
- logger.debug "Class attribute #{name} created"
39
+ @setter
40
40
  end
41
41
 
42
- def class_name
43
- @class_wnode.class_name
42
+ def attr_accessor
43
+ [self.attr_reader, self.attr_writer]
44
44
  end
45
45
 
46
- def wtype=(wtype)
47
- @wtype = wtype
48
- # Adjust corresponding method objects wtype accordingly
49
- @getter.wtype = wtype
50
- @setter.wtype = wtype
51
- @ivar.wtype = wtype
52
- logger.debug "WAttr/Getter/Setter/ivar wtype updated : #{@getter.inspect}"
46
+ def export!
47
+ @export = true
53
48
  end
54
49
 
55
- def size
56
- @wtype.size
50
+ def class_name
51
+ @class_wnode.class_name
57
52
  end
58
53
 
59
- def ivar_name
60
- @ivar ? @ivar.name : :"@#{name}"
54
+ def wtype
55
+ @ivar.wtype
61
56
  end
62
57
 
63
- def wasm_name
64
- "$#{@name}"
58
+ def offset
59
+ @ivar.offset
65
60
  end
66
61
 
62
+ def wtype=(wtype)
63
+ # Adjust getter/setter and ivar wtype accordingly
64
+ @getter.wtype = wtype if @getter
65
+ @setter.wtype = wtype if @setter
66
+ @ivar.wtype = wtype
67
+ logger.debug "Attr/Getter/Setter/ivar wtype updated : #{@getter.inspect}"
68
+ end
69
+
67
70
  def getter_name
68
71
  @name
69
72
  end
@@ -71,8 +74,13 @@ module Rlang::Parser
71
74
  def setter_name
72
75
  "#{@name}=".to_sym
73
76
  end
77
+
78
+ def wasm_name
79
+ "$#{@name}"
80
+ end
81
+
74
82
  def wasm_type
75
- @wtype.wasm_type
83
+ self.wtype.wasm_type
76
84
  end
77
85
  end
78
86
  end
@@ -17,6 +17,9 @@ module Rlang::Parser
17
17
 
18
18
  attr_reader :label, :wtype, :address, :value
19
19
 
20
+ # NOTE: new and append only takes individual DAta values
21
+ # not an array of values
22
+ # TODO: fix DAta.new and DAta.append not accepting an array of values
20
23
  def initialize(label, value, wtype=WType::DEFAULT)
21
24
  raise "Data label '#{label}' already initialized" \
22
25
  if self.class.exist? label
@@ -2,7 +2,7 @@
2
2
  # Copyright (c) 2019, Laurent Julliard and contributors
3
3
  # All rights reserved.
4
4
  #
5
- # Instance variables
5
+ # Instance variables class
6
6
  #
7
7
  require_relative '../../utils/log'
8
8
  require_relative './wtype'
@@ -11,22 +11,31 @@ require_relative './data'
11
11
  module Rlang::Parser
12
12
  class IVar
13
13
  include Log
14
- attr_reader :name, :class_wnode
15
- attr_accessor :wtype
14
+ attr_reader :name
15
+ attr_accessor :wtype, :offset
16
16
 
17
17
  def initialize(class_wnode, name, wtype=WType::DEFAULT)
18
- @name = name
19
18
  @class_wnode = class_wnode
19
+ @name = name
20
20
  @wtype = wtype
21
- logger.debug "creating instance variable #{name} in class #{self.class_name} / wtype #{wtype}"
21
+ # this is the offset of the instance variable
22
+ # in memory in the order they are declared
23
+ # It is computed at the end of a class
24
+ # definition
25
+ @offset = nil
26
+ logger.debug "Instance variable #{name} created"
22
27
  end
23
28
 
24
29
  def class_name
25
30
  @class_wnode.class_name
26
31
  end
27
32
 
28
- def wattr_name
29
- @name.to_s.tr('@','').to_sym
33
+ def size
34
+ @wtype.size
35
+ end
36
+
37
+ def wasm_name
38
+ "$#{@name}"
30
39
  end
31
40
 
32
41
  def wasm_type
@@ -13,7 +13,8 @@ module Rlang::Parser
13
13
  include Log
14
14
 
15
15
  attr_reader :wtype
16
- attr_accessor :name, :wnode, :wattrs, :cvars, :consts, :methods
16
+ attr_accessor :name, :wnode, :attrs, :ivars, :cvars,
17
+ :consts, :methods, :offset
17
18
 
18
19
  def initialize(name)
19
20
  @name = name
@@ -23,14 +24,16 @@ module Rlang::Parser
23
24
  # the wnode implementing the code of the class
24
25
  @wnode = nil
25
26
  logger.debug "Klass created #{self.inspect}"
26
- @wattrs = [] # class attributes
27
+ @attrs = [] # class attributes
28
+ @ivars = [] # instance variables
27
29
  @cvars = [] # class variables
28
30
  @consts = [] # class constants
29
31
  @methods = [] # methods
32
+ @offset = 0 # offset of the next class attribute in memory
30
33
  end
31
34
 
32
35
  def size
33
- self.wattrs.sum(&:size)
36
+ @offset
34
37
  end
35
38
 
36
39
  def wtype=(wtype)
@@ -16,32 +16,40 @@ module Rlang::Parser
16
16
  attr_reader :name, :wtype
17
17
  attr_accessor :class_name, :margs, :lvars, :wnode
18
18
 
19
- def initialize(name, class_name, wtype=WType::DEFAULT)
19
+ METHOD_TYPES = [:class, :instance]
20
+
21
+ def initialize(name, class_name, wtype, method_type)
20
22
  raise "Wrong method wtype argument: #{wtype.inspect}" unless wtype.is_a? WType
21
23
  @name = name
22
24
  @class_name = class_name
23
- @wtype = wtype
24
- @instance = false
25
+ @wtype = wtype || WType::DEFAULT
26
+ @method_type = method_type
27
+ raise "Unknown method type: #{method_type}" unless METHOD_TYPES.include? @method_type
25
28
  @wnode = nil # wnode where method is implemented
26
29
  logger.debug "Method created #{self.inspect}"
27
30
  @margs = [] # method args
28
31
  @lvars = [] # local variables
29
32
  end
30
33
 
34
+ def implemented?
35
+ !@wnode.nil?
36
+ end
37
+
31
38
  def instance!
32
- @instance = true
39
+ @method_type = :instance
33
40
  end
34
41
 
35
42
  def instance?
36
- @instance
43
+ @method_type == :instance
44
+
37
45
  end
38
46
 
39
47
  def class!
40
- @instance = false
48
+ @method_type = :class
41
49
  end
42
50
 
43
51
  def class?
44
- !@instance
52
+ @method_type == :class
45
53
  end
46
54
 
47
55
  def wtype=(wtype)
@@ -50,7 +58,7 @@ module Rlang::Parser
50
58
  end
51
59
 
52
60
  def wasm_name
53
- if @instance
61
+ if self.instance?
54
62
  "$#{@class_name}##{@name}"
55
63
  else
56
64
  "$#{@class_name}::#{@name}"
@@ -62,7 +70,7 @@ module Rlang::Parser
62
70
  end
63
71
 
64
72
  def export_name
65
- if @instance
73
+ if self.instance?
66
74
  "#{@class_name.downcase}_i_#{@name}"
67
75
  else
68
76
  "#{@class_name.downcase}_c_#{@name}"
@@ -50,6 +50,8 @@ module Rlang::Parser
50
50
  :'!' => :eqz
51
51
  }
52
52
 
53
+ ALL_OPS_MAP = [*ARITHMETIC_OPS_MAP, *RELATIONAL_OPS_MAP, *BOOLEAN_OPS_MAP, *UNARY_OPS_MAP].to_h
54
+
53
55
  # Matrix of how to cast a WASM type to another
54
56
  CAST_OPS = {
55
57
  I32: { I32: :cast_nope, I64: :cast_extend, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_wtype, none: :cast_error},
@@ -60,6 +62,9 @@ module Rlang::Parser
60
62
  none: { I32: :cast_error, I64: :cast_error, F32: :cast_error, F64: :cast_error, Class: :cast_error, none: :cast_error},
61
63
  }
62
64
 
65
+ # Rlang class size method name
66
+ SIZE_METHOD = :_size_
67
+
63
68
  # new template when object size > 0
64
69
  NEW_TMPL = %q{
65
70
  result :Object, :alloc, :%{default_wtype}
@@ -71,7 +76,7 @@ module Rlang::Parser
71
76
  end
72
77
  }
73
78
 
74
- # new template when object size iz 0 (no instance var)
79
+ # new template when object size is 0 (no instance var)
75
80
  # use 0 as the _self_ address in memory. It should never
76
81
  # be used anyway
77
82
  NEW_ZERO_TMPL = %q{
@@ -91,6 +96,11 @@ module Rlang::Parser
91
96
  end
92
97
  }
93
98
 
99
+ # Dynamically allocate a string object
100
+ STRING_NEW_TMPL = %q{
101
+ String.new(%{ptr}, %{length})
102
+ }
103
+
94
104
  # Generate the wasm nodes and tree structure
95
105
  # ***IMPORTANT NOTE***
96
106
  # Unless otherwise stated all methods receive
@@ -106,6 +116,7 @@ module Rlang::Parser
106
116
  @parser = parser
107
117
  @root = WTree.new().root
108
118
  @new_count = 0
119
+ @static_count = 0
109
120
  end
110
121
 
111
122
  def klass(wnode, class_name)
@@ -118,22 +129,51 @@ module Rlang::Parser
118
129
  k.wnode
119
130
  end
120
131
 
121
- def def_wattr(wnode)
132
+ # Postprocess ivars
133
+ # (called at end of class parsing)
134
+ def ivars_setup(wnode)
122
135
  wnc = wnode.class_wnode
123
- # Process each declared attribute
124
- offset = 0
125
- wnc.klass.wattrs.each do |wa|
126
- logger.debug("Generating accessors for attribute #{wnc.klass.name}\##{wa.name}")
136
+ raise "Cannot find class for attributes definition!!" unless wnc
137
+ klass = wnc.klass
138
+ logger.debug "Postprocessing ivars for class #{klass.name}..."
139
+ klass.ivars.each do |iv|
140
+ iv.offset = klass.offset
141
+ logger.debug "... ivar #{iv.name} has offset #{iv.offset}"
142
+ # Update offset for next ivar
143
+ klass.offset += iv.size
144
+ end
145
+ end
146
+
147
+ # generate code for class attributes
148
+ # (called at end of class parsing)
149
+ def def_attr(wnode)
150
+ wnc = wnode.class_wnode
151
+ raise "Cannot find class for attributes definition!!" unless wnc
152
+ # Process each declared class attribute
153
+ wnc.klass.attrs.each do |attr|
154
+ logger.debug("Generating accessors for attribute #{wnc.klass.name}\##{attr.name}")
127
155
  # Generate getter and setter methods wnode
128
- (wa.setter.wnode = wattr_setter(wnc, wa, offset)) unless wa.setter.wnode
129
- (wa.getter.wnode = wattr_getter(wnc, wa, offset)) unless wa.getter.wnode
130
- # Update offset
131
- offset += wa.wtype.size
156
+ # unless method already implemented by user
157
+ if attr.setter
158
+ unless attr.setter.implemented?
159
+ attr.setter.wnode = self.attr_setter(wnc, attr)
160
+ else
161
+ logger.debug "Attribute setter #{attr.setter.name} already defined. Skipping"
162
+ end
163
+ end
164
+ if attr.getter
165
+ unless attr.getter.implemented?
166
+ attr.getter.wnode = self.attr_getter(wnc, attr)
167
+ else
168
+ logger.debug "Attribute getter #{attr.getter.name} already defined. Skipping"
169
+ end
170
+ end
132
171
  end
133
172
 
134
173
  # Also generate the Class::_size_ method
135
- # always (needed by Object.allocate)
136
- size_method = wnc.find_or_create_method(:_size_, wnc.klass.name, WType::DEFAULT, :class )
174
+ # (needed for dynamic memory allocation
175
+ # by Object.allocate)
176
+ size_method = wnc.find_or_create_method(SIZE_METHOD, wnc.klass.name, WType::DEFAULT, :class)
137
177
  unless size_method.wnode
138
178
  logger.debug("Generating #{size_method.class_name}\##{size_method.name}")
139
179
  wns = WNode.new(:insn, wnc, true)
@@ -145,22 +185,22 @@ module Rlang::Parser
145
185
  end
146
186
 
147
187
  # Generate attribute setter method wnode
148
- def wattr_setter(wnode, wattr, offset)
188
+ def attr_setter(wnode, attr)
149
189
  wnc = wnode.class_wnode
150
190
  wn_set = WNode.new(:insn, wnc, true)
151
- wn_set.c(:wattr_writer, func_name: wattr.setter.wasm_name,
152
- wattr_name: wattr.wasm_name, wtype: wattr.wasm_type,
153
- offset: offset)
191
+ wn_set.c(:attr_setter, func_name: attr.setter.wasm_name,
192
+ attr_name: attr.wasm_name, wtype: attr.wasm_type,
193
+ offset: attr.offset)
154
194
  wn_set
155
195
  end
156
196
 
157
197
  # Generate attribute getter method wnode
158
- def wattr_getter(wnode, wattr, offset)
198
+ def attr_getter(wnode, attr)
159
199
  wnc = wnode.class_wnode
160
200
  wn_get = WNode.new(:insn, wnc, true)
161
- wn_get.c(:wattr_reader, func_name: wattr.getter.wasm_name,
162
- wattr_name: wattr.wasm_name, wtype: wattr.wasm_type,
163
- offset: offset)
201
+ 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)
164
204
  wn_get
165
205
  end
166
206
 
@@ -225,11 +265,12 @@ module Rlang::Parser
225
265
  wn
226
266
  end
227
267
 
228
- # Set class variable
229
- # Constant assignment doesn't generate any code
230
- # A Data object is instantiated and initialized
231
- # when the Const object is created in parser
268
+ # Set constant
232
269
  def casgn(wnode, const)
270
+ (wn = WNode.new(:insn, wnode)).wtype = const.wtype
271
+ wn.c(:store, wtype: const.wtype)
272
+ WNode.new(:insn, wn).c(:addr, value: const.address)
273
+ wn
233
274
  end
234
275
 
235
276
  # Get class variable
@@ -254,34 +295,38 @@ module Rlang::Parser
254
295
  wn
255
296
  end
256
297
 
257
- # Call setter (on wattr or instance variable)
298
+ # Call setter (on attr or instance variable)
258
299
  # This is the same as calling the corresponding setter
259
- def call_setter(wnode, wnode_recv, wattr)
260
- wn = self.call(wnode, wnode_recv.wtype.name, wattr.setter_name, :instance)
300
+ def call_setter(wnode, wnode_recv, attr)
301
+ wn = self.call(wnode, wnode_recv.wtype.name, attr.setter_name, :instance)
261
302
  # First argument of the setter must be the receiver
262
303
  wnode_recv.reparent_to(wn)
263
304
  wn
264
305
  end
265
306
 
266
- # Call getter (on wattr or instance variable)
307
+ # Call getter (on attr or instance variable)
267
308
  # This is the same as calling the corresponding getter
268
- def call_getter(wnode, wnode_recv, wattr)
269
- wn = self.call(wnode, wnode_recv.wtype.name, wattr.getter_name, :instance)
309
+ def call_getter(wnode, wnode_recv, attr)
310
+ wn = self.call(wnode, wnode_recv.wtype.name, attr.getter_name, :instance)
270
311
  # First argument of the getter must always be the receiver
271
312
  wnode_recv.reparent_to(wn)
272
313
  wn
273
314
  end
274
315
 
275
316
  # Set instance variable
276
- # This is the same as calling the corresponding setter
277
- def ivasgn(wnode, wnode_recv, wattr)
278
- self.call_setter(wnode, wnode_recv, wattr)
317
+ def ivasgn(wnode, ivar)
318
+ (wn = WNode.new(:insn, wnode)).wtype = ivar.wtype
319
+ wn.c(:store_offset, wtype: ivar.wasm_type, offset: lambda { ivar.offset })
320
+ self._self_(wn)
321
+ wn
279
322
  end
280
323
 
281
324
  # Get instance variable.
282
- # This is the same as calling the corresponding getter
283
- def ivar(wnode, wnode_recv, wattr)
284
- self.call_getter(wnode, wnode_recv, wattr)
325
+ def ivar(wnode, ivar)
326
+ (wn = WNode.new(:insn, wnode)).wtype = ivar.wtype
327
+ wn.c(:load_offset, wtype: ivar.wasm_type, offset: lambda { ivar.offset })
328
+ self._self_(wn)
329
+ wn
285
330
  end
286
331
 
287
332
  # Set class variable
@@ -339,6 +384,52 @@ module Rlang::Parser
339
384
  wn
340
385
  end
341
386
 
387
+ # Generate a phony node (generally used to create
388
+ # a wnode subtree under the phony node and later
389
+ # reparent it to the proper place in the wtree)
390
+ def phony(wnode)
391
+ phony = WNode.new(:none, wnode)
392
+ phony
393
+ end
394
+
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
401
+ end
402
+
403
+ # Static new string object
404
+ def string_static_new(wnode, string)
405
+ klass = wnode.find_class(nil)
406
+ data_label = "#{klass.name}_string_#{@static_count += 1}"
407
+ # Statically
408
+ data_stg = self.string_static(string, data_label)
409
+ # align on :I32 boundary
410
+ DAta.align(4)
411
+ 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)
413
+ # Generate address wnode
414
+ (wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: data_len.address)
415
+ wn_object_addr.wtype = WType.new(:String)
416
+ wn_object_addr
417
+ end
418
+
419
+ # Dynamic new string object
420
+ def string_dynamic_new(wnode, string)
421
+ klass = wnode.find_class(nil)
422
+ data_label = "#{klass.name}_string_#{@static_count += 1}"
423
+ data_stg = self.string_static(string, data_label)
424
+ string_new_source = STRING_NEW_TMPL % {
425
+ string: string,
426
+ ptr: data_stg.address,
427
+ length: string.length
428
+ }
429
+ #puts string_new_source;exit
430
+ wn_string = self.parser.parse(string_new_source, wnode)
431
+ end
432
+
342
433
  # All the cast_xxxx methods below returns
343
434
  # the new wnode doing the cast operation
344
435
  # or the same wnode if there is no additional code
@@ -402,27 +493,44 @@ module Rlang::Parser
402
493
  # TODO: simplify this complex method (possibly by using
403
494
  # a conversion table source type -> destination type)
404
495
  def cast(wnode, wtype, signed=false)
405
- logger.debug "wnode: #{wnode}, wtype: #{wtype}"
496
+ logger.debug "Casting wnode: #{wnode}, wtype: #{wtype}, wnode ID: #{wnode.object_id}"
406
497
  src_type = (wnode.wtype.native? ? wnode.wtype.name : :Class)
407
498
  dest_type = (wtype.native? ? wtype.name : :Class)
408
499
  cast_method = CAST_OPS[src_type] && CAST_OPS[src_type][dest_type] || :cast_error
409
-
500
+ logger.debug "Calling cast method: #{cast_method}"
410
501
  wn_cast_op = self.send(cast_method, wnode, wtype, signed)
411
- logger.debug "After type cast: wnode: #{wn_cast_op}, wtype: #{wtype}"
502
+ logger.debug "After type cast: wnode: #{wn_cast_op}, wtype: #{wtype}, wnode ID: #{wn_cast_op.object_id}"
412
503
  wn_cast_op
413
504
  end
414
505
 
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
+
415
525
  # just create a wnode for the WASM operator
416
526
  # Do not set wtype or a code template yet,
417
527
  # wait until operands type is known (see
418
528
  # operands below)
419
- def operator(wnode, operator, wtype=WType.new(:none))
420
- if (op = (ARITHMETIC_OPS_MAP[operator] ||
421
- RELATIONAL_OPS_MAP[operator] ||
422
- BOOLEAN_OPS_MAP[operator] ||
423
- UNARY_OPS_MAP[operator] ))
529
+ def native_operator(wnode, operator, wtype=WType.new(:none))
530
+ if (op = ALL_OPS_MAP[operator])
424
531
  (wn_op = WNode.new(:insn, wnode)).c(:operator, operator: op)
425
532
  wn_op.wtype = wtype
533
+ logger.debug "Creating operator #{operator} wnode: #{wn_op}"
426
534
  wn_op
427
535
  else
428
536
  raise "operator '#{operator}' not supported"
@@ -432,8 +540,21 @@ module Rlang::Parser
432
540
  # finish the setting of the operator node and
433
541
  # attach operands
434
542
  def operands(wnode_op, wnode_recv, wnode_args)
543
+ logger.debug "Processing operands in operator wnode: #{wnode_op}..."
544
+ # Do not post process operands if the operator
545
+ # wnode is a call (= overloaded operator)
546
+ # and not a native operand
547
+ if wnode_op.template == :call
548
+ logger.debug "Doing nothing because it's a func call..."
549
+ return wnode_op
550
+ end
551
+
552
+ # A native operator only expects 0 (unary) or 1 (binary)
553
+ # argument in addition to the receiver
435
554
  raise "only 0 or 1 operand expected (got #{wnode_args.count})" if wnode_args.count > 1
436
555
  op = wnode_op.wargs[:operator]
556
+ #wnode_recv = wnode_op.children[0]
557
+ #wnode_args = wnode_op.children[1..-1]
437
558
  # First find out the wtype that has precedence
438
559
  wtype = self.class.leading_wtype(wnode_recv, *wnode_args)
439
560
 
@@ -441,7 +562,7 @@ module Rlang::Parser
441
562
  logger.debug "leading type cast: #{wtype}"
442
563
 
443
564
  # Attach receiver and argument to the operator wnode
444
- # type casting them if necessary
565
+ # type casting them if necessary
445
566
  self.cast(wnode_recv, wtype).reparent_to(wnode_op)
446
567
  self.cast(wnode_args.first, wtype).reparent_to(wnode_op) unless wnode_args.empty?
447
568
 
@@ -455,8 +576,7 @@ module Rlang::Parser
455
576
  # if + or - operator then multiply arg by size of object
456
577
  if [:add, :sub].include? wnode_op.wargs[:operator]
457
578
  (wn_mulop = WNode.new(:insn, wnode_op)).c(:operator, operator: :mul)
458
- WNode.new(:insn, wn_mulop).c(:const,
459
- value: lambda { wnode_recv.find_class(wnode_recv.wtype.name).size })
579
+ WNode.new(:insn, wn_mulop).c(:call, func_name: "$#{wnode_recv.wtype.name}::#{SIZE_METHOD}")
460
580
  wnode_args.first.reparent_to(wn_mulop)
461
581
  else
462
582
  # It's a relational operator. In this case
@@ -466,6 +586,7 @@ module Rlang::Parser
466
586
  wnode_op.wtype = WType::DEFAULT
467
587
  end
468
588
  end
589
+ logger.debug "Operands in operator wnode after postprocessing: #{wnode_op}..."
469
590
  wnode_op
470
591
  end
471
592