rlang 0.3.1 → 0.4.0

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