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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -3
- data/Gemfile.lock +4 -6
- data/README +11 -0
- data/README.md +18 -10
- data/bin/rlang +17 -5
- data/docs/RlangCompiler.md +5 -1
- data/docs/RlangManual.md +98 -20
- data/examples/fib/fib.rb +5 -1
- data/lib/builder/rlang/compiler.rb +2 -21
- data/lib/rlang/lib/array/array32.rb +59 -0
- data/lib/rlang/lib/array/array64.rb +56 -0
- data/lib/rlang/lib/array.rb +6 -0
- data/lib/rlang/lib/base64.rb +223 -0
- data/lib/rlang/lib/io.rb +75 -0
- data/lib/rlang/lib/kernel.rb +23 -0
- data/lib/rlang/lib/malloc.rb +12 -8
- data/lib/rlang/lib/memory.rb +102 -2
- data/lib/rlang/lib/object.rb +40 -4
- data/lib/rlang/lib/rlang.rb +29 -0
- data/lib/rlang/lib/rlang_core.rb +15 -0
- data/lib/rlang/lib/string.rb +106 -8
- data/lib/rlang/lib/type/i32.rb +43 -0
- data/lib/rlang/lib/type/i64.rb +2 -0
- data/lib/rlang/lib/unistd.rb +1 -2
- data/lib/rlang/lib/wasi.rb +184 -0
- data/lib/rlang/parser/attr.rb +9 -13
- data/lib/rlang/parser/const.rb +105 -1
- data/lib/rlang/parser/cvar.rb +11 -7
- data/lib/rlang/parser/data.rb +17 -6
- data/lib/rlang/parser/export.rb +4 -3
- data/lib/rlang/parser/ext/integer.rb +3 -1
- data/lib/rlang/parser/ext/type.rb +30 -1
- data/lib/rlang/parser/global.rb +10 -2
- data/lib/rlang/parser/ivar.rb +3 -7
- data/lib/rlang/parser/klass.rb +8 -35
- data/lib/rlang/parser/method.rb +36 -12
- data/lib/rlang/parser/module.rb +143 -0
- data/lib/rlang/parser/wgenerator.rb +462 -168
- data/lib/rlang/parser/wnode.rb +387 -142
- data/lib/rlang/parser/wtype.rb +30 -7
- data/lib/rlang/parser.rb +506 -231
- data/lib/rlang/version.rb +1 -1
- data/lib/rlang.rb +3 -0
- data/lib/ruby/mirror/rstring.rb +16 -0
- data/lib/utils/exceptions.rb +12 -0
- data/rlang.gemspec +4 -4
- metadata +25 -13
- 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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
:>= => :
|
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
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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, :
|
97
|
+
result :Object, :allocate, :%{default_wtype}
|
71
98
|
def self.new(%{margs})
|
72
|
-
result
|
73
|
-
object_ptr = Object.
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
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
|
-
|
125
|
-
#
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
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
|
-
|
154
|
-
logger.debug("Generating accessors for attribute #{
|
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,
|
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.
|
179
|
-
wns = WNode.new(:insn, wnc
|
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
|
-
|
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,
|
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
|
-
|
203
|
-
offset: attr.offset)
|
366
|
+
wasm_type: attr.wasm_type, offset: attr.offset)
|
204
367
|
wn_get
|
205
368
|
end
|
206
369
|
|
207
|
-
def
|
208
|
-
logger.debug("
|
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
|
-
|
215
|
-
#
|
216
|
-
wn.create_marg(:_self_)
|
217
|
-
logger.debug("
|
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
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
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,
|
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
|
456
|
+
# Get constant
|
277
457
|
def const(wnode, const)
|
278
458
|
(wn = WNode.new(:insn, wnode)).wtype = const.wtype
|
279
|
-
wn.c(:load,
|
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.
|
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.
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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
|
-
|
392
|
-
phony
|
585
|
+
def phony(wnode, type=:none)
|
586
|
+
WNode.new(type, wnode)
|
393
587
|
end
|
394
588
|
|
395
|
-
# Static string allocation
|
396
|
-
def
|
397
|
-
#
|
398
|
-
|
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.
|
597
|
+
klass = wnode.find_current_class_or_module()
|
406
598
|
data_label = "#{klass.name}_string_#{@static_count += 1}"
|
407
|
-
#
|
408
|
-
data_stg
|
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.
|
616
|
+
klass = wnode.find_current_class_or_module()
|
422
617
|
data_label = "#{klass.name}_string_#{@static_count += 1}"
|
423
|
-
data_stg
|
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
|
-
|
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
|
-
|
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 ,
|
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
|
-
|
457
|
-
|
458
|
-
|
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,
|
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 #{
|
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)).
|
532
|
-
wn_op.
|
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
|
-
#
|
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
|
-
|
794
|
+
leading_wtype = self.class.leading_wtype(wnode_recv, *wnode_args)
|
560
795
|
|
561
|
-
wnode_op.wtype =
|
562
|
-
logger.debug "leading type cast: #{
|
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
|
-
|
567
|
-
|
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
|
-
|
574
|
-
|
575
|
-
|
576
|
-
#
|
577
|
-
if [:add, :sub].include?
|
578
|
-
(wn_mulop = WNode.new(:insn, wnode_op)).
|
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,
|
596
|
-
klass = wnode.
|
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 = "#{
|
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(
|
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
|
-
|
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
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
logger.debug "Creating code for #{
|
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:
|
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
|
-
|
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(
|
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
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
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,
|
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
|