rlang 0.3.1 → 0.4.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.
@@ -29,6 +29,11 @@ module Rlang::Parser
29
29
  logger.debug "New Data[#{@label}] initialized with #{@value} at address #{@address}"
30
30
  end
31
31
 
32
+ def self.reset!
33
+ @@label_table = {}
34
+ @@current_address = 0
35
+ end
36
+
32
37
  def append_value(value, wtype)
33
38
  @value << value
34
39
  if value.is_a?(String)
@@ -11,6 +11,10 @@ module Rlang::Parser
11
11
  @@exports << self
12
12
  end
13
13
 
14
+ def self.reset!
15
+ @@exports = []
16
+ end
17
+
14
18
  # Export Rlang funcs, etc... grouping them
15
19
  # by object type for Wasm code readability
16
20
  def self.transpile
@@ -24,6 +24,10 @@ module Rlang::Parser
24
24
  @@globals.find {|g| g.name == name}
25
25
  end
26
26
 
27
+ def self.reset!
28
+ @@globals = []
29
+ end
30
+
27
31
  def mutable?
28
32
  @mutable
29
33
  end
@@ -0,0 +1,36 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+ #
5
+ # Instance variables
6
+ #
7
+ require_relative '../../utils/log'
8
+ require_relative './wtype'
9
+ require_relative './data'
10
+
11
+ module Rlang::Parser
12
+ class IVar
13
+ include Log
14
+ attr_reader :name, :class_wnode
15
+ attr_accessor :wtype
16
+
17
+ def initialize(class_wnode, name, wtype=WType::DEFAULT)
18
+ @name = name
19
+ @class_wnode = class_wnode
20
+ @wtype = wtype
21
+ logger.debug "creating instance variable #{name} in class #{self.class_name} / wtype #{wtype}"
22
+ end
23
+
24
+ def class_name
25
+ @class_wnode.class_name
26
+ end
27
+
28
+ def wattr_name
29
+ @name.to_s.tr('@','').to_sym
30
+ end
31
+
32
+ def wasm_type
33
+ @wtype.wasm_type
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,49 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+
5
+ # Rlang classes
6
+ require_relative '../../utils/log'
7
+ require_relative './wtype'
8
+
9
+ module Rlang::Parser
10
+ # Note: Cannot use Class as class name
11
+ # because it's already used by Ruby
12
+ class Klass
13
+ include Log
14
+
15
+ attr_reader :wtype
16
+ attr_accessor :name, :wnode, :wattrs, :cvars, :consts, :methods
17
+
18
+ def initialize(name)
19
+ @name = name
20
+ # the type of a class is its name by definition
21
+ @wtype = WType.new(name)
22
+ @size = 0
23
+ # the wnode implementing the code of the class
24
+ @wnode = nil
25
+ logger.debug "Klass created #{self.inspect}"
26
+ @wattrs = [] # class attributes
27
+ @cvars = [] # class variables
28
+ @consts = [] # class constants
29
+ @methods = [] # methods
30
+ end
31
+
32
+ def size
33
+ self.wattrs.sum(&:size)
34
+ end
35
+
36
+ def wtype=(wtype)
37
+ @wtype = wtype
38
+ logger.debug "Klass #{@name} wtype updated: #{self.inspect}"
39
+ end
40
+
41
+ def wasm_name
42
+ @name
43
+ end
44
+
45
+ def wasm_type
46
+ @wtype.wasm_type
47
+ end
48
+ end
49
+ end
@@ -19,6 +19,10 @@ module Rlang::Parser
19
19
  logger.debug "Method argument #{name} created"
20
20
  end
21
21
 
22
+ def _self_?
23
+ @name == :_self_
24
+ end
25
+
22
26
  def wasm_name
23
27
  "$#{@name}"
24
28
  end
@@ -2,7 +2,7 @@
2
2
  # Copyright (c) 2019, Laurent Julliard and contributors
3
3
  # All rights reserved.
4
4
 
5
- # Class variables
5
+ # Rlang methods
6
6
  require_relative '../../utils/log'
7
7
  require_relative './wtype'
8
8
  require_relative './export'
@@ -14,14 +14,18 @@ module Rlang::Parser
14
14
  include Log
15
15
 
16
16
  attr_reader :name, :wtype
17
- attr_accessor :class_name
17
+ attr_accessor :class_name, :margs, :lvars, :wnode
18
18
 
19
19
  def initialize(name, class_name, wtype=WType::DEFAULT)
20
+ raise "Wrong method wtype argument: #{wtype.inspect}" unless wtype.is_a? WType
20
21
  @name = name
21
22
  @class_name = class_name
22
23
  @wtype = wtype
23
24
  @instance = false
24
- logger.debug "Method instance created #{self.inspect}"
25
+ @wnode = nil # wnode where method is implemented
26
+ logger.debug "Method created #{self.inspect}"
27
+ @margs = [] # method args
28
+ @lvars = [] # local variables
25
29
  end
26
30
 
27
31
  def instance!
@@ -42,7 +46,7 @@ module Rlang::Parser
42
46
 
43
47
  def wtype=(wtype)
44
48
  @wtype = wtype
45
- logger.debug "Method wtype updated: #{self.inspect}"
49
+ logger.debug "Method wtype updated: #{self}"
46
50
  end
47
51
 
48
52
  def wasm_name
@@ -5,29 +5,37 @@
5
5
  # Method argument class
6
6
 
7
7
  require_relative '../../utils/log'
8
+ require_relative './ivar'
8
9
  require_relative './wtype'
9
10
 
10
11
  module Rlang::Parser
11
12
  class WAttr
12
13
  include Log
13
- attr_reader :name, :wtype, :getter, :setter
14
+ attr_reader :name, :wtype, :getter, :setter, :ivar
14
15
 
16
+ # The name argument can either be the attribute name
17
+ # (e.g. :size) or an ivar name (e.g. :@size)
15
18
  def initialize(class_wnode, name, wtype=WType::DEFAULT)
16
19
  @class_wnode = class_wnode
17
- @name = name
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
18
27
  @wtype = wtype
19
28
  # Also create the corresponding getter and setter
20
29
  # method objects (with default WType - wattr_type
21
30
  # directives might later change this wtype)
22
31
  # Don't generate WAT code yet
23
- @getter = @class_wnode.create_method(self.getter_name, nil, wtype, :instance)
32
+ @getter = @class_wnode.find_or_create_method(self.getter_name, nil, wtype, :instance)
24
33
  @getter.instance!
25
34
  logger.debug "Getter created: #{@getter.inspect}"
26
35
 
27
- @setter = @class_wnode.create_method(self.setter_name, nil, wtype, :instance)
36
+ @setter = @class_wnode.find_or_create_method(self.setter_name, nil, wtype, :instance)
28
37
  @setter.instance!
29
38
  logger.debug "Setter created: #{@setter.inspect}"
30
-
31
39
  logger.debug "Class attribute #{name} created"
32
40
  end
33
41
 
@@ -40,13 +48,18 @@ module Rlang::Parser
40
48
  # Adjust corresponding method objects wtype accordingly
41
49
  @getter.wtype = wtype
42
50
  @setter.wtype = wtype
43
- logger.debug "Getter and Setter wtype updated : #{@getter.inspect}"
51
+ @ivar.wtype = wtype
52
+ logger.debug "WAttr/Getter/Setter/ivar wtype updated : #{@getter.inspect}"
44
53
  end
45
54
 
46
55
  def size
47
56
  @wtype.size
48
57
  end
49
58
 
59
+ def ivar_name
60
+ @ivar ? @ivar.name : :"@#{name}"
61
+ end
62
+
50
63
  def wasm_name
51
64
  "$#{@name}"
52
65
  end
@@ -60,11 +60,43 @@ module Rlang::Parser
60
60
  none: { I32: :cast_error, I64: :cast_error, F32: :cast_error, F64: :cast_error, Class: :cast_error, none: :cast_error},
61
61
  }
62
62
 
63
+ # new template when object size > 0
64
+ NEW_TMPL = %q{
65
+ result :Object, :alloc, :%{default_wtype}
66
+ def self.new(%{margs})
67
+ result :%{class_name}
68
+ object_ptr = Object.alloc(%{class_name}._size_).cast_to(:%{class_name})
69
+ object_ptr.initialize(%{margs})
70
+ return object_ptr
71
+ end
72
+ }
73
+
74
+ # new template when object size iz 0 (no instance var)
75
+ # use 0 as the _self_ address in memory. It should never
76
+ # be used anyway
77
+ NEW_ZERO_TMPL = %q{
78
+ result :Object, :alloc, :%{default_wtype}
79
+ def self.new(%{margs})
80
+ result :%{class_name}
81
+ object_ptr = 0.cast_to(:%{class_name})
82
+ object_ptr.initialize(%{margs})
83
+ return object_ptr
84
+ end
85
+ }
86
+
87
+ # Do nothing initialize method
88
+ DUMB_INIT_TMPL = %q{
89
+ def initialize()
90
+ result :nil
91
+ end
92
+ }
93
+
63
94
  # Generate the wasm nodes and tree structure
64
95
  # ***IMPORTANT NOTE***
65
96
  # Unless otherwise stated all methods receive
66
97
  # the parent wnode as their first argument
67
98
  # and must generate child nodes of this parent
99
+ # Child node created is returned
68
100
  class WGenerator
69
101
  include Log
70
102
  attr_accessor :parser
@@ -76,38 +108,40 @@ module Rlang::Parser
76
108
  @new_count = 0
77
109
  end
78
110
 
79
- def klass(wnode, klass_name)
80
- # First see if that class already exist
81
- # If not create it.
82
- unless (cwn = wnode.find_class(klass_name))
83
- cwn = WNode.new(:class, wnode)
84
- cwn.class_name = klass_name
85
- cwn.wtype = WType.new(cwn.class_name)
86
- WNode.root.class_wnodes << cwn
87
- end
88
- cwn
111
+ def klass(wnode, class_name)
112
+ # Create class object and class wnode if it doesn't exist yet
113
+ k = wnode.find_or_create_class(class_name)
114
+ # Create the Class.new method object too (not
115
+ # the code yet in case the end user code defines
116
+ # its own implementation in the class body)
117
+ k.wnode.find_or_create_method(:new, k.name, k.wtype, :class)
118
+ k.wnode
89
119
  end
90
120
 
91
- def attributes(wnode)
121
+ def def_wattr(wnode)
92
122
  wnc = wnode.class_wnode
93
- return if wnc.wattrs.empty?
94
123
  # Process each declared attribute
95
124
  offset = 0
96
- wnc.wattrs.each do |wa|
97
- logger.debug("Generating accessors for attribute #{wa}")
125
+ wnc.klass.wattrs.each do |wa|
126
+ logger.debug("Generating accessors for attribute #{wnc.klass.name}\##{wa.name}")
98
127
  # Generate getter and setter methods wnode
99
- wattr_setter(wnc, wa, offset)
100
- wattr_getter(wnc, wa, offset)
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
101
130
  # Update offset
102
131
  offset += wa.wtype.size
103
132
  end
104
133
 
105
- # Also generate the Class::size method
106
- size_method = wnc.create_method(:size, nil, WType::DEFAULT, :class)
107
- wns = WNode.new(:insn, wnc, true)
108
- wns.wtype = WType::DEFAULT
109
- wns.c(:class_size, func_name: size_method.wasm_name,
110
- wtype: wns.wasm_type, size: wnc.class_size)
134
+ # 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 )
137
+ unless size_method.wnode
138
+ logger.debug("Generating #{size_method.class_name}\##{size_method.name}")
139
+ wns = WNode.new(:insn, wnc, true)
140
+ wns.wtype = WType::DEFAULT
141
+ wns.c(:class_size, func_name: size_method.wasm_name,
142
+ wtype: wns.wasm_type, size: wnc.class_size)
143
+ size_method.wnode = wns
144
+ end
111
145
  end
112
146
 
113
147
  # Generate attribute setter method wnode
@@ -133,32 +167,34 @@ module Rlang::Parser
133
167
  def instance_method(wnode, method)
134
168
  logger.debug("Generating wnode for instance method #{method.inspect}")
135
169
  wn = WNode.new(:method, wnode)
170
+ method.wnode = wn
136
171
  wn.method = method # must be set before calling func_name
137
172
  wn.wtype = method.wtype
138
173
  wn.c(:func, func_name: wn.method.wasm_name)
139
174
  # Also declare a "hidden" parameter representing the
140
175
  # pointer to the instance (always default wtype)
141
176
  wn.create_marg(:_self_)
142
- logger.debug("MEthod built: #{wn.method.inspect}")
177
+ logger.debug("Building instance method: wn.wtype #{wn.wtype}, wn.method #{wn.method}")
143
178
  wn
144
179
  end
145
180
 
146
181
  def class_method(wnode, method)
147
182
  logger.debug("Generating wnode for class method #{method}")
148
183
  wn = WNode.new(:method, wnode)
184
+ method.wnode = wn
149
185
  wn.method = method # must be set before calling func_name
150
186
  wn.wtype = method.wtype
151
187
  wn.c(:func, func_name: wn.method.wasm_name)
152
- logger.debug("Building method: wn.wtype #{wn.wtype}, wn.method #{wn.method}")
188
+ logger.debug("Building class method: wn.wtype #{wn.wtype}, wn.method #{wn.method}")
153
189
  wn
154
190
  end
155
191
 
156
192
  def params(wnode)
157
- wnode = wnode.method_wnode
193
+ wnm = wnode.method_wnode
158
194
  # use reverse to preserve proper param order
159
- wnode.margs.reverse.each do |marg|
195
+ wnm.method.margs.reverse.each do |marg|
160
196
  logger.debug("Prepending param #{marg}")
161
- wn = WNode.new(:insn, wnode, true)
197
+ wn = WNode.new(:insn, wnm, true)
162
198
  wn.wtype = marg.wtype
163
199
  wn.c(:param, name: marg.wasm_name)
164
200
  end
@@ -173,10 +209,10 @@ module Rlang::Parser
173
209
  end
174
210
 
175
211
  def locals(wnode)
176
- wnode = wnode.method_wnode
177
- wnode.lvars.reverse.each do |lvar|
212
+ wnm = wnode.method_wnode
213
+ wnm.method.lvars.reverse.each do |lvar|
178
214
  logger.debug("Prepending local #{lvar.inspect}")
179
- wn = WNode.new(:insn, wnode, true)
215
+ wn = WNode.new(:insn, wnm, true)
180
216
  wn.wtype = lvar.wtype
181
217
  wn.c(:local, name: lvar.wasm_name)
182
218
  end
@@ -189,13 +225,14 @@ module Rlang::Parser
189
225
  wn
190
226
  end
191
227
 
228
+ # Set class variable
192
229
  # Constant assignment doesn't generate any code
193
230
  # A Data object is instantiated and initialized
194
231
  # when the Const object is created in parser
195
232
  def casgn(wnode, const)
196
233
  end
197
234
 
198
- # Read class variable
235
+ # Get class variable
199
236
  def const(wnode, const)
200
237
  (wn = WNode.new(:insn, wnode)).wtype = const.wtype
201
238
  wn.c(:load, wtype: const.wtype, var_name: const.wasm_name)
@@ -203,20 +240,51 @@ module Rlang::Parser
203
240
  wn
204
241
  end
205
242
 
206
- # Global variable assignment
243
+ # Set Global variable
207
244
  def gvasgn(wnode, gvar)
208
245
  (wn = WNode.new(:insn, wnode)).wtype = gvar.wtype
209
246
  wn.c(:global_set, var_name: gvar.name)
210
247
  wn
211
248
  end
212
249
 
213
- # Global variable read
250
+ # Get Global variable
214
251
  def gvar(wnode, gvar)
215
252
  (wn = WNode.new(:insn, wnode)).wtype = gvar.wtype
216
253
  wn.c(:global_get, var_name: gvar.name)
217
254
  wn
218
255
  end
219
256
 
257
+ # Call setter (on wattr or instance variable)
258
+ # 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)
261
+ # First argument of the setter must be the receiver
262
+ wnode_recv.reparent_to(wn)
263
+ wn
264
+ end
265
+
266
+ # Call getter (on wattr or instance variable)
267
+ # 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)
270
+ # First argument of the getter must always be the receiver
271
+ wnode_recv.reparent_to(wn)
272
+ wn
273
+ end
274
+
275
+ # 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)
279
+ end
280
+
281
+ # 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)
285
+ end
286
+
287
+ # Set class variable
220
288
  # Create the class variable storage node and
221
289
  # an empty expression node to populate later
222
290
  def cvasgn(wnode, cvar)
@@ -226,7 +294,7 @@ module Rlang::Parser
226
294
  wn
227
295
  end
228
296
 
229
- # Read class variable
297
+ # Get class variable
230
298
  def cvar(wnode, cvar)
231
299
  (wn = WNode.new(:insn, wnode)).wtype = cvar.wtype
232
300
  wn.c(:load, wtype: cvar.wtype, var_name: cvar.wasm_name)
@@ -388,7 +456,7 @@ module Rlang::Parser
388
456
  if [:add, :sub].include? wnode_op.wargs[:operator]
389
457
  (wn_mulop = WNode.new(:insn, wnode_op)).c(:operator, operator: :mul)
390
458
  WNode.new(:insn, wn_mulop).c(:const,
391
- value: lambda { wnode_recv.find_class(wnode_recv.wtype.name).class_size })
459
+ value: lambda { wnode_recv.find_class(wnode_recv.wtype.name).size })
392
460
  wnode_args.first.reparent_to(wn_mulop)
393
461
  else
394
462
  # It's a relational operator. In this case
@@ -403,11 +471,11 @@ module Rlang::Parser
403
471
 
404
472
  # Statically allocate an object in data segment
405
473
  # with the size of the class
406
- def new(wnode, class_name)
407
- class_wnode = wnode.find_class(class_name)
408
- if class_wnode.class_size > 0
474
+ def static_new(wnode, class_name)
475
+ klass = wnode.find_class(class_name)
476
+ if klass.size > 0
409
477
  data_label = "#{class_name}_new_#{@new_count += 1}"
410
- data = DAta.new(data_label.to_sym, "\x00"*class_wnode.class_size)
478
+ data = DAta.new(data_label.to_sym, "\x00"*klass.wnode.class_size)
411
479
  address = data.address
412
480
  else
413
481
  # TODO: point to address 0. It is not safe but normally
@@ -421,9 +489,44 @@ module Rlang::Parser
421
489
  wn_object_addr
422
490
  end
423
491
 
492
+ # Create the dynamic new method. It allocates memory
493
+ # for the object created and calls initialize
494
+ def def_new(wnode_class)
495
+ # no new method for native types
496
+ return if wnode_class.klass.wtype.native?
497
+ new_method = wnode_class.find_method(:new, wnode_class.class_name, :class)
498
+ return if new_method.wnode # already implemented
499
+
500
+ init_method = wnode_class.find_method(:initialize, wnode_class.class_name, :instance)
501
+ logger.debug "Creating code for #{wnode_class.class_name}.new"
502
+ new_tmpl = wnode_class.class_size.zero? ? NEW_ZERO_TMPL : NEW_TMPL
503
+ new_source = new_tmpl % {
504
+ default_wtype: WType::DEFAULT.name,
505
+ class_name: wnode_class.class_name,
506
+ # Do not pass _self_ argument to the new method of course !!
507
+ margs: init_method.margs.reject {|ma| ma._self_?}.map(&:name).join(', '),
508
+ class_size: wnode_class.class_size
509
+ }
510
+ new_method.wnode = self.parser.parse(new_source, wnode_class)
511
+ end
512
+
513
+ # Define a dumb initialize method if not implemented
514
+ # already in user code
515
+ def def_initialize(wnode_class)
516
+ # no new/initialize method for native types
517
+ return if WType.new(wnode_class.class_name).native?
518
+ # generate code for a dumb initialize method if not defined
519
+ # in user code
520
+ unless wnode_class.find_method(:initialize, wnode_class.class_name, :instance)
521
+ logger.debug "Creating MEthod and code for #{wnode_class.class_name}#initialize"
522
+ init_source = DUMB_INIT_TMPL
523
+ self.parser.parse(init_source, wnode_class)
524
+ end
525
+ end
526
+
424
527
  def call(wnode, class_name, method_name, method_type)
425
- method = wnode.find_or_create_method(method_name, class_name, method_type)
426
- logger.debug "found method #{method.inspect}"
528
+ method = wnode.find_or_create_method(method_name, class_name, nil, method_type)
529
+ logger.debug "found method #{method}"
427
530
  (wn_call = WNode.new(:insn, wnode)).c(:call, func_name: method.wasm_name)
428
531
  wn_call.wtype = method.wtype
429
532
  wn_call
@@ -431,7 +534,7 @@ module Rlang::Parser
431
534
 
432
535
  # self in an instance context is passed as the first argument
433
536
  # of a method call
434
- def _self(wnode)
537
+ def _self_(wnode)
435
538
  (wns = WNode.new(:insn, wnode)).c(:local_get, var_name: '$_self_')
436
539
  wns.wtype = WType.new(wnode.class_name)
437
540
  wns
@@ -499,11 +602,7 @@ module Rlang::Parser
499
602
  # Determine which wasm type has precedence among
500
603
  # all wnodes
501
604
  def self.leading_wtype(*wnodes)
502
- begin
503
- WType.leading(wnodes.map(&:wtype))
504
- rescue
505
- raise "#{wnodes.map(&:to_s)}"
506
- end
605
+ WType.leading(wnodes.map(&:wtype))
507
606
  end
508
607
  end
509
608
  end