Class: Rlang::Parser::WGenerator
- Includes:
- Log
- Defined in:
- lib/rlang/parser/wgenerator.rb
Overview
Generate the wasm nodes and tree structure ***IMPORTANT NOTE*** Unless otherwise stated all methods receive the parent wnode as their first argument and must generate child nodes of this parent Child node created is returned
Instance Attribute Summary collapse
-
#parser ⇒ Object
Returns the value of attribute parser.
-
#root ⇒ Object
readonly
Returns the value of attribute root.
-
#wn_code ⇒ Object
readonly
Returns the value of attribute wn_code.
-
#wn_data ⇒ Object
readonly
Returns the value of attribute wn_data.
-
#wn_exports ⇒ Object
readonly
Returns the value of attribute wn_exports.
-
#wn_globals ⇒ Object
readonly
Returns the value of attribute wn_globals.
-
#wn_imports ⇒ Object
readonly
Returns the value of attribute wn_imports.
Instance Method Summary collapse
-
#_self_(wnode) ⇒ Object
self in an instance context is passed as the first argument of a method call.
-
#allocate_array_static_data(array, data_label) ⇒ Object
Static array data allocation.
-
#allocate_string_static_data(string, data_label) ⇒ Object
Static string data allocation.
-
#array_dynamic_new(wnode, array) ⇒ Object
Dynamic new array object.
-
#array_static_new(wnode, array) ⇒ Object
Static new array object.
-
#attr_getter(wnode, attr) ⇒ Object
Generate attribute getter method wnode.
-
#attr_setter(wnode, attr) ⇒ Object
Generate attribute setter method wnode.
- #break(wnode) ⇒ Object
-
#call_getter(wnode, wnode_recv, attr) ⇒ Object
Call getter (on attr or instance variable) This is the same as calling the corresponding getter.
-
#call_setter(wnode, wnode_recv, attr) ⇒ Object
Call setter (on attr or instance variable) This is the same as calling the corresponding setter.
-
#casgn(wnode, const) ⇒ Object
Set constant.
-
#cast(wnode, wtype, signed = false) ⇒ Object
cast an expression to a different type if same type do nothing - wnode: the wnode to type cast - wtype: the wtype to cast wnode to - signed: whether the cast wnode must be interpreted as a signed value.
-
#cast_error(wnode, wtype, signed) ⇒ Object
Cast operation is invalid.
-
#cast_extend(wnode, wtype, signed) ⇒ Object
Cast by extending to a wtype of larger bit size (e.g. I32 to I64).
-
#cast_nope(wnode, wtype, signed) ⇒ Object
No casting.
-
#cast_notyet(wnode, wtype, signed) ⇒ Object
Cast operation not yet supported.
-
#cast_wrap(wnode, wtype, signed) ⇒ Object
Cast by wraping a wtype to a smaller size (e.g. I64 to I32).
-
#cast_wtype(wnode, wtype, signed) ⇒ Object
Cast by simply changing the node wtype No change in native WASM type (e.g. casting an object pointer to I32).
- #comments(wnode, comments) ⇒ Object
-
#const(wnode, const) ⇒ Object
Get constant.
-
#const_addr(wnode, const) ⇒ Object
Get constant addres.
-
#cvar(wnode, cvar) ⇒ Object
Get class variable.
-
#cvar_addr(wnode, cvar) ⇒ Object
Get class variable address.
-
#cvasgn(wnode, cvar) ⇒ Object
Set class variable Create the class variable storage node and an empty expression node to populate later.
-
#declare_method(wnode, wtype, method_name, result_type) ⇒ Object
Ahead of time method declaration and return type Create corresponding classes and method objects as we known we’ll be calling them later on.
-
#def_attr(wnode) ⇒ Object
generate code for class attributes (called at end of class parsing).
-
#def_initialize(wnode_class) ⇒ Object
Define a dumb initialize method if not implemented already in user code.
- #def_method(wnode, method_name, method_type) ⇒ Object
-
#def_new(wnode_class) ⇒ Object
Create the dynamic new method.
- #drop(wnode) ⇒ Object
- #else(wnode) ⇒ Object
- #export_method(wnode, export_name) ⇒ Object
- #extend(wnode, module_path) ⇒ Object
- #float(wnode, wtype, value) ⇒ Object
-
#gvar(wnode, gvar) ⇒ Object
Get Global variable.
-
#gvasgn(wnode, gvar) ⇒ Object
Set Global variable.
- #if(wnode) ⇒ Object
- #import_method(wnode, module_name, function_name) ⇒ Object
- #include(wnode, module_path) ⇒ Object
-
#initialize(parser) ⇒ WGenerator
constructor
A new instance of WGenerator.
- #inline(wnode, code, wtype = Type::I32) ⇒ Object
- #int(wnode, wtype, value) ⇒ Object
-
#ivar(wnode, ivar) ⇒ Object
Get instance variable.
-
#ivars_setup(wnode) ⇒ Object
Postprocess ivars (called at end of class parsing).
-
#ivasgn(wnode, ivar) ⇒ Object
Set instance variable.
-
#klass(wnode, class_path, super_class_path) ⇒ Object
Create class and its basic methods (new, initialize and size).
- #locals(wnode) ⇒ Object
-
#lvar(wnode, lvar) ⇒ Object
Read local variable.
-
#lvasgn(wnode, lvar) ⇒ Object
Create the local variable storage node.
-
#module(wnode, module_path) ⇒ Object
Create module object and module wnode if it doesn’t exist yet.
-
#native_operator(wnode, operator, wtype = WType.new(:none)) ⇒ Object
just create a wnode for the WASM operator Do not set wtype or a code template yet, wait until operands type is known (see operands below).
- #next(wnode) ⇒ Object
- #nop(wnode) ⇒ Object
-
#operands(wnode_op, wnode_recv, wnode_args) ⇒ Object
Finish the setting of the operator node, attach operands and see if they need implicit type casting.
- #params(wnode) ⇒ Object
-
#phony(wnode, type = :none) ⇒ Object
Generate a phony node (generally used to create a wnode subtree under the phony node and later reparent it to the proper place in the wtree).
- #prepend(wnode, module_path) ⇒ Object
- #result(wnode) ⇒ Object
- #return(wnode) ⇒ Object
-
#send_method(wnode, class_path, method_name, method_type) ⇒ Object
generate code for method call.
-
#static_new(wnode, class_path) ⇒ Object
Statically allocate an object in data segment with the size of the class.
-
#string_dynamic_new(wnode, string) ⇒ Object
Dynamic new string object.
-
#string_static_new(wnode, string) ⇒ Object
Static new string object.
- #then(wnode) ⇒ Object
- #while(wnode) ⇒ Object
-
#while_cond(wnode, wnode_cond_exp) ⇒ Object
This is a post processing of the while exp wnode because br_if requires to negate the original while condition.
-
#while_end(wnode) ⇒ Object
add the unconditional looping branch at the end of the while.
Methods included from Log
included, logger, #logger, logger=
Constructor Details
#initialize(parser) ⇒ WGenerator
Returns a new instance of WGenerator.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/rlang/parser/wgenerator.rb', line 146 def initialize(parser) @parser = parser @root = WTree.new().root @new_count = 0 @static_count = 0 # Create section wnodes @wn_imports = WNode.new(:imports, @root) @wn_memory = WNode.new(:memory, @root) @wn_exports = WNode.new(:exports, @root) @wn_globals = WNode.new(:globals, @root) @wn_data = WNode.new(:data, @root) # Module code generation @root.c(:module, module: parser.config[:module]) # Memory code generation WNode.new(:insn, @wn_memory). \ c(:memory, min: parser.config[:memory_min], max: parser.config[:memory_max]) # define Object class and Kernel modules # and include Kernel in Object wn_object_class = self.klass(@root, [:Object], []) @object_class = wn_object_class.klass @root.klass = @object_class wn_kernel_module = self.module(@root, [:Kernel]) self.include(wn_object_class, [:Kernel]) # Create Class and Module classes # And Class inherits from module self.klass(@root, [:Module], [:Object]) self.klass(@root, [:Class], [:Module]) end |
Instance Attribute Details
#parser ⇒ Object
Returns the value of attribute parser.
143 144 145 |
# File 'lib/rlang/parser/wgenerator.rb', line 143 def parser @parser end |
#root ⇒ Object (readonly)
Returns the value of attribute root.
144 145 146 |
# File 'lib/rlang/parser/wgenerator.rb', line 144 def root @root end |
#wn_code ⇒ Object (readonly)
Returns the value of attribute wn_code.
144 145 146 |
# File 'lib/rlang/parser/wgenerator.rb', line 144 def wn_code @wn_code end |
#wn_data ⇒ Object (readonly)
Returns the value of attribute wn_data.
144 145 146 |
# File 'lib/rlang/parser/wgenerator.rb', line 144 def wn_data @wn_data end |
#wn_exports ⇒ Object (readonly)
Returns the value of attribute wn_exports.
144 145 146 |
# File 'lib/rlang/parser/wgenerator.rb', line 144 def wn_exports @wn_exports end |
#wn_globals ⇒ Object (readonly)
Returns the value of attribute wn_globals.
144 145 146 |
# File 'lib/rlang/parser/wgenerator.rb', line 144 def wn_globals @wn_globals end |
#wn_imports ⇒ Object (readonly)
Returns the value of attribute wn_imports.
144 145 146 |
# File 'lib/rlang/parser/wgenerator.rb', line 144 def wn_imports @wn_imports end |
Instance Method Details
#_self_(wnode) ⇒ Object
self in an instance context is passed as the first argument of a method call
952 953 954 955 956 |
# File 'lib/rlang/parser/wgenerator.rb', line 952 def _self_(wnode) (wns = WNode.new(:insn, wnode)).c(:local_get, var_name: '$_self_') wns.wtype = WType.new(wnode.class_name) wns end |
#allocate_array_static_data(array, data_label) ⇒ Object
Static array data allocation
629 630 631 632 633 634 635 636 |
# File 'lib/rlang/parser/wgenerator.rb', line 629 def allocate_array_static_data(array, data_label) # Append each array element to the same data section label = data_label.to_sym data_arr = nil # Do not allocate memory space if array is empty array.each { |elt| data_arr = DAta.append(label, elt) } data_arr end |
#allocate_string_static_data(string, data_label) ⇒ Object
Static string data allocation
590 591 592 593 |
# File 'lib/rlang/parser/wgenerator.rb', line 590 def allocate_string_static_data(string, data_label) # if string is empty do not allocate any memory space DAta.append(data_label.to_sym, string) unless string.empty? end |
#array_dynamic_new(wnode, array) ⇒ Object
Dynamic new array object
658 659 660 661 662 663 664 665 666 667 668 669 670 |
# File 'lib/rlang/parser/wgenerator.rb', line 658 def array_dynamic_new(wnode, array) klass = wnode.find_current_class_or_module() data_label = "#{klass.name}_array_#{@static_count += 1}" # Note : data_arr is nil if string is empty data_arr = self.allocate_array_static_data(array, data_label) array_new_source = ARRAY_NEW_TMPL % { elt_size_in_bits: WTYPE::DEFAULT.size * 8, ptr: data_arr ? data_arr.address : 0, count: array.length } #puts array_new_source;exit self.parser.parse(array_new_source, wnode) end |
#array_static_new(wnode, array) ⇒ Object
Static new array object
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 |
# File 'lib/rlang/parser/wgenerator.rb', line 639 def array_static_new(wnode, array) klass = wnode.find_current_class_or_module() data_label = "#{klass.name}_array_#{@static_count += 1}" # Allocate array data statically # Note : data_arr is nil if string is empty data_arr = self.allocate_array_static_data(array, data_label) # align on :I32 boundary # then allocate the Array object attributes # and set them up DAta.align(4) data_count = DAta.append("#{data_label}_count".to_sym, array.length, WType::DEFAULT) data_ptr = DAta.append("#{data_label}_ptr".to_sym, data_arr ? data_arr.address : 0, WType::DEFAULT) # Generate address wnode (wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: data_count.address) wn_object_addr.wtype = WType.new(:Array32) wn_object_addr end |
#attr_getter(wnode, attr) ⇒ Object
Generate attribute getter method wnode
362 363 364 365 366 367 368 |
# File 'lib/rlang/parser/wgenerator.rb', line 362 def attr_getter(wnode, attr) wnc = wnode.class_wnode wn_get = WNode.new(:insn, wnc, true) wn_get.c(:attr_getter, func_name: attr.getter.wasm_name, wasm_type: attr.wasm_type, offset: attr.offset) wn_get end |
#attr_setter(wnode, attr) ⇒ Object
Generate attribute setter method wnode
352 353 354 355 356 357 358 359 |
# File 'lib/rlang/parser/wgenerator.rb', line 352 def attr_setter(wnode, attr) wnc = wnode.class_wnode wn_set = WNode.new(:insn, wnc, true) wn_set.c(:attr_setter, func_name: attr.setter.wasm_name, attr_name: attr.wasm_name, wasm_type: attr.wasm_type, offset: attr.offset) wn_set end |
#break(wnode) ⇒ Object
1002 1003 1004 1005 1006 1007 |
# File 'lib/rlang/parser/wgenerator.rb', line 1002 def break(wnode) # look for block wnode upper in the tree # and branch to that label (wn = WNode.new(:insn, wnode)).c(:br, label: wnode.block_wnode.label) wn end |
#call_getter(wnode, wnode_recv, attr) ⇒ Object
Call getter (on attr or instance variable) This is the same as calling the corresponding getter
496 497 498 499 500 501 |
# File 'lib/rlang/parser/wgenerator.rb', line 496 def call_getter(wnode, wnode_recv, attr) wn = self.send_method(wnode, wnode_recv.wtype, attr.getter_name, :instance) # First argument of the getter must always be the receiver wnode_recv.reparent_to(wn) wn end |
#call_setter(wnode, wnode_recv, attr) ⇒ Object
Call setter (on attr or instance variable) This is the same as calling the corresponding setter
487 488 489 490 491 492 |
# File 'lib/rlang/parser/wgenerator.rb', line 487 def call_setter(wnode, wnode_recv, attr) wn = self.send_method(wnode, wnode_recv.wtype, attr.setter_name, :instance) # First argument of the setter must be the receiver wnode_recv.reparent_to(wn) wn end |
#casgn(wnode, const) ⇒ Object
Set constant
449 450 451 452 453 454 |
# File 'lib/rlang/parser/wgenerator.rb', line 449 def casgn(wnode, const) (wn = WNode.new(:insn, wnode)).wtype = const.wtype wn.c(:store, wasm_type: const.wtype) WNode.new(:insn, wn).c(:addr, value: const.address) wn end |
#cast(wnode, wtype, signed = false) ⇒ Object
cast an expression to a different type if same type do nothing
-
wnode: the wnode to type cast
-
wtype: the wtype to cast wnode to
-
signed: whether the cast wnode must be interpreted as a signed value
TODO: simplify this complex method (possibly by using a conversion table source type -> destination type)
746 747 748 749 750 751 752 753 754 755 |
# File 'lib/rlang/parser/wgenerator.rb', line 746 def cast(wnode, wtype, signed=false) logger.debug "Casting wnode: #{wnode}, wtype: #{wtype}, wnode ID: #{wnode.object_id}" src_type = (wnode.wtype.native? ? wnode.wtype.name : :Class) dest_type = (wtype.native? ? wtype.name : :Class) cast_method = CAST_OPS[src_type] && CAST_OPS[src_type][dest_type] || :cast_error logger.debug "Calling cast method: #{cast_method}" wn_cast_op = self.send(cast_method, wnode, wtype, signed) logger.debug "After type cast: wnode: #{wn_cast_op}, wtype: #{wtype}, wnode ID: #{wn_cast_op.object_id}" wn_cast_op end |
#cast_error(wnode, wtype, signed) ⇒ Object
Cast operation is invalid
734 735 736 |
# File 'lib/rlang/parser/wgenerator.rb', line 734 def cast_error(wnode, wtype, signed) raise "Cannot cast type #{wnode.wtype} to #{wtype}. Time to fix your code :-)" end |
#cast_extend(wnode, wtype, signed) ⇒ Object
Cast by extending to a wtype of larger bit size (e.g. I32 to I64)
685 686 687 688 689 690 691 692 693 694 695 696 |
# File 'lib/rlang/parser/wgenerator.rb', line 685 def cast_extend(wnode, wtype, signed) if (wnode.template == :const) # it's a WASM const, simply change the wtype wnode.wtype = wtype wn_cast_op = wnode else wn_cast_op = wnode.insert(:insn) wn_cast_op.wtype = wtype wn_cast_op.c(signed ? :extend_i32_s : :extend_i32_u , wasm_type: wn_cast_op.wasm_type) end wn_cast_op end |
#cast_nope(wnode, wtype, signed) ⇒ Object
No casting. Return node as is.
679 680 681 |
# File 'lib/rlang/parser/wgenerator.rb', line 679 def cast_nope(wnode, wtype, signed) wnode end |
#cast_notyet(wnode, wtype, signed) ⇒ Object
Cast operation not yet supported
729 730 731 |
# File 'lib/rlang/parser/wgenerator.rb', line 729 def cast_notyet(wnode, wtype, signed) raise "Type cast from #{wnode.wtype} to #{wtype} not supported yet" end |
#cast_wrap(wnode, wtype, signed) ⇒ Object
Cast by wraping a wtype to a smaller size (e.g. I64 to I32)
715 716 717 718 719 720 721 722 723 724 725 726 |
# File 'lib/rlang/parser/wgenerator.rb', line 715 def cast_wrap(wnode, wtype, signed) if (wnode.template == :const) # it's a WASM const, simply change the wtype wnode.wtype = wtype wn_cast_op = wnode else wn_cast_op = wnode.insert(:insn) wn_cast_op.wtype = wtype wn_cast_op.c(:wrap_i64, wasm_type: wn_cast_op.wasm_type) end wn_cast_op end |
#cast_wtype(wnode, wtype, signed) ⇒ Object
Cast by simply changing the node wtype No change in native WASM type (e.g. casting an object pointer to I32)
701 702 703 704 705 706 707 708 709 710 711 |
# File 'lib/rlang/parser/wgenerator.rb', line 701 def cast_wtype(wnode, wtype, signed) # Don't cast blindly. Check that source and target # have the same bit size (e.g. Object pointers, I32, UI32 # are the same size, ) if wnode.wtype.size == wtype.size wnode.wtype = wtype else cast_error(wnode, wtype, signed) end wnode end |
#comments(wnode, comments) ⇒ Object
222 223 224 225 226 227 |
# File 'lib/rlang/parser/wgenerator.rb', line 222 def comments(wnode, comments) # The gsub below is to handle =begin...=end block comments comments.each do |c| WNode.new(:comment, wnode).c(:comment, text: c.text.sub(/^\s*#/,'').gsub("\n", "\n;;")) end end |
#const(wnode, const) ⇒ Object
Get constant
457 458 459 460 461 462 |
# File 'lib/rlang/parser/wgenerator.rb', line 457 def const(wnode, const) (wn = WNode.new(:insn, wnode)).wtype = const.wtype wn.c(:load, wasm_type: const.wasm_type) WNode.new(:insn, wn).c(:addr, value: const.address) wn end |
#const_addr(wnode, const) ⇒ Object
Get constant addres
465 466 467 468 469 |
# File 'lib/rlang/parser/wgenerator.rb', line 465 def const_addr(wnode, const) (wn = WNode.new(:insn, wnode)).wtype = const.wtype wn.c(:addr, value: const.address) wn end |
#cvar(wnode, cvar) ⇒ Object
Get class variable
530 531 532 533 534 535 |
# File 'lib/rlang/parser/wgenerator.rb', line 530 def cvar(wnode, cvar) (wn = WNode.new(:insn, wnode)).wtype = cvar.wtype wn.c(:load, wasm_type: cvar.wasm_type) WNode.new(:insn, wn).c(:addr, value: cvar.address) wn end |
#cvar_addr(wnode, cvar) ⇒ Object
Get class variable address
539 540 541 542 543 |
# File 'lib/rlang/parser/wgenerator.rb', line 539 def cvar_addr(wnode, cvar) (wn = WNode.new(:insn, wnode)).wtype = cvar.wtype wn.c(:addr, value: cvar.address) wn end |
#cvasgn(wnode, cvar) ⇒ Object
Set class variable Create the class variable storage node and an empty expression node to populate later
522 523 524 525 526 527 |
# File 'lib/rlang/parser/wgenerator.rb', line 522 def cvasgn(wnode, cvar) (wn = WNode.new(:insn, wnode)).wtype = cvar.wtype wn.c(:store, wasm_type: cvar.wasm_type) WNode.new(:insn, wn).c(:addr, value: cvar.address) wn end |
#declare_method(wnode, wtype, method_name, result_type) ⇒ Object
Ahead of time method declaration and return type Create corresponding classes and method objects as we known we’ll be calling them later on
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/rlang/parser/wgenerator.rb', line 272 def declare_method(wnode, wtype, method_name, result_type) class_path = wtype.class_path logger.debug "Declaring method #{method_name} in class #{class_path}" klass = WNode.root.find_class_or_module(class_path) raise "Can't find class or module #{class_path} in method declaration" unless klass method_types = [] if method_name[0] == '#' method_types << :instance method_types << :class if klass.const.module? mth_name = method_name[1..-1].to_sym else method_types << :class method_types << :instance if klass.const.module? mth_name = method_name.to_sym end mth = method_types.each do |mt| (m = wnode.find_or_create_method(klass, mth_name, mt, nil)).wtype = WType.new(result_type) logger.debug "Declared #{mt} method #{m.name} in class #{m.klass.name} with wtype #{m.wtype.name}" m end mth end |
#def_attr(wnode) ⇒ Object
generate code for class attributes (called at end of class parsing)
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
# File 'lib/rlang/parser/wgenerator.rb', line 312 def def_attr(wnode) klass = wnode.find_current_class_or_module() wnc = klass.wnode raise "Cannot find class for attributes definition!!" unless wnc # Process each declared class attribute klass.attrs.each do |attr| logger.debug("Generating accessors for attribute #{klass.name}\##{attr.name}") # Generate getter and setter methods wnode # unless method already implemented by user if attr.setter unless attr.setter.implemented? attr.setter.wnode = self.attr_setter(wnc, attr) else logger.debug "Attribute setter #{attr.setter.name} already defined. Skipping" end end if attr.getter unless attr.getter.implemented? attr.getter.wnode = self.attr_getter(wnc, attr) else logger.debug "Attribute getter #{attr.getter.name} already defined. Skipping" end end end # Also generate the Class::_size_ method # (needed for dynamic memory allocation # by Object.allocate) size_method = wnc.find_or_create_method(klass, SIZE_METHOD, :class, WType::DEFAULT) unless size_method.wnode logger.debug("Generating #{size_method.klass.name}\##{size_method.name}") wns = WNode.new(:insn, wnc) wns.wtype = WType::DEFAULT wns.c(:class_size, func_name: size_method.wasm_name, wasm_type: wns.wasm_type, size: wnc.class_size) size_method.wnode = wns end end |
#def_initialize(wnode_class) ⇒ Object
Define a dumb initialize method if not implemented already in user code
897 898 899 900 901 902 903 904 905 906 907 908 909 |
# File 'lib/rlang/parser/wgenerator.rb', line 897 def def_initialize(wnode_class) k = wnode_class.find_current_class_or_module() # no new/initialize method for native types return if WType.new(k.path_name).native? # generate code for a dumb initialize method if not defined # in user code if (init_mth = wnode_class.find_method(k, :initialize, :instance, true)) return if init_mth.wnode # already implemented end logger.debug "Creating MEthod and code for #{k.name}#initialize" init_source = DUMB_INIT_TMPL init_mth.wnode = self.parser.parse(init_source, wnode_class) end |
#def_method(wnode, method_name, method_type) ⇒ Object
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
# File 'lib/rlang/parser/wgenerator.rb', line 370 def def_method(wnode, method_name, method_type) logger.debug("Defining #{method_type} method #{method_name}...") if (method = wnode.find_method(nil, method_name, method_type, true)) logger.warn "Redefining #{method.klass.name},#{method_name}" if method.wnode else method = wnode.create_method(nil, method_name, method_type, nil, true) end # If it's the main method, give it the proper name in export if # specified on command line if method.klass.path_name == :Object && method.name == :main method.export_name = @parser.config[:start] end # Generate method definition wnode logger.debug("Generating wnode for #{method_type} method #{method_name}") wn = WNode.new(:method, wnode) method.wnode = wn wn.wtype = method.wtype wn.c(:func, func_name: wn.method.wasm_name) # Instance methods 1st argument is always self wn.create_marg(:_self_) if method.instance? logger.debug("Built #{method_type} method definition: wn.wtype #{wn.wtype}, wn.method #{wn.method}") wn end |
#def_new(wnode_class) ⇒ Object
Create the dynamic new method. It allocates memory for the object created and calls initialize
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 |
# File 'lib/rlang/parser/wgenerator.rb', line 872 def def_new(wnode_class) k = wnode_class.find_current_class_or_module() logger.debug "Defining new method for #{k.name}" # no need to define new method for native types return if wnode_class.klass.wtype.native? if (new_mth = wnode_class.find_method(k, :new, :class, true)) return if new_mth.implemented? # already implemented end logger.debug "Creating code for #{k.name}.new" # Find initialize method and use the same method args for new init_method = wnode_class.find_method(k, :initialize, :instance, true) new_tmpl = wnode_class.class_size.zero? ? NEW_ZERO_TMPL : NEW_TMPL new_source = new_tmpl % { default_wtype: WType::DEFAULT.name, class_name: k.path_name, # Do not pass _self_ argument to the new method of course !! margs: init_method.margs.reject {|ma| ma._self_?}.map(&:name).join(', '), class_size: wnode_class.class_size } new_mth.wnode = self.parser.parse(new_source, wnode_class) end |
#drop(wnode) ⇒ Object
559 560 561 562 563 |
# File 'lib/rlang/parser/wgenerator.rb', line 559 def drop(wnode) logger.debug "dropping result of #{wnode}, caller: #{caller_locations}" (wn = WNode.new(:insn, wnode)).c(:drop) wn end |
#else(wnode) ⇒ Object
973 974 975 976 |
# File 'lib/rlang/parser/wgenerator.rb', line 973 def else(wnode) (wn = WNode.new(:insn, wnode)).c(:else) wn end |
#export_method(wnode, export_name) ⇒ Object
408 409 410 |
# File 'lib/rlang/parser/wgenerator.rb', line 408 def export_method(wnode, export_name) wnode.method.export!(export_name) end |
#extend(wnode, module_path) ⇒ Object
258 259 260 261 262 263 264 265 266 267 |
# File 'lib/rlang/parser/wgenerator.rb', line 258 def extend(wnode, module_path) m = wnode.find_module(module_path) raise "Cannot find module #{module_path}. Please require module first." \ unless m wnc = wnode.class_or_module_wnode raise "Cannot find scope class/module for included module!!" unless wnc wnc.klass.extend(m) m.extended! wnc end |
#float(wnode, wtype, value) ⇒ Object
576 577 578 579 580 |
# File 'lib/rlang/parser/wgenerator.rb', line 576 def float(wnode, wtype, value) (wn = WNode.new(:insn, wnode)).wtype = wtype wn.c(:const, wasm_type: wn.wasm_type, value: value) wn end |
#gvar(wnode, gvar) ⇒ Object
Get Global variable
479 480 481 482 483 |
# File 'lib/rlang/parser/wgenerator.rb', line 479 def gvar(wnode, gvar) (wn = WNode.new(:insn, wnode)).wtype = gvar.wtype wn.c(:global_get, var_name: gvar.name) wn end |
#gvasgn(wnode, gvar) ⇒ Object
Set Global variable
472 473 474 475 476 |
# File 'lib/rlang/parser/wgenerator.rb', line 472 def gvasgn(wnode, gvar) (wn = WNode.new(:insn, wnode)).wtype = gvar.wtype wn.c(:global_set, var_name: gvar.name) wn end |
#if(wnode) ⇒ Object
963 964 965 966 |
# File 'lib/rlang/parser/wgenerator.rb', line 963 def if(wnode) (wn = WNode.new(:insn, wnode)).c(:if) wn end |
#import_method(wnode, module_name, function_name) ⇒ Object
397 398 399 400 401 402 403 404 405 406 |
# File 'lib/rlang/parser/wgenerator.rb', line 397 def import_method(wnode, module_name, function_name) # Create the import node (wn_import = WNode.new(:insn, self.wn_imports)).c(:import, module_name: module_name, function_name: function_name) wn_import.link = wnode wnode.method.imported! # now silence the method wnode so that # it doesn't generate WASM code in the code section wnode.silence! wn_import end |
#include(wnode, module_path) ⇒ Object
236 237 238 239 240 241 242 243 244 245 |
# File 'lib/rlang/parser/wgenerator.rb', line 236 def include(wnode, module_path) m = wnode.find_module(module_path) raise "Unknown module #{module_path}. Please require module first." \ unless m wnc = wnode.class_or_module_wnode raise "Cannot find scope class/module for included module!!" unless wnc wnc.klass.include(m) m.included! wnc end |
#inline(wnode, code, wtype = Type::I32) ⇒ Object
441 442 443 444 445 446 |
# File 'lib/rlang/parser/wgenerator.rb', line 441 def inline(wnode, code, wtype=Type::I32) wn = WNode.new(:insn, wnode) wn.wtype = wnode.wtype wn.c(:inline, code: code) wn end |
#int(wnode, wtype, value) ⇒ Object
570 571 572 573 574 |
# File 'lib/rlang/parser/wgenerator.rb', line 570 def int(wnode, wtype, value) (wn = WNode.new(:insn, wnode)).wtype = wtype wn.c(:const, wasm_type: wn.wasm_type, value: value) wn end |
#ivar(wnode, ivar) ⇒ Object
Get instance variable.
512 513 514 515 516 517 |
# File 'lib/rlang/parser/wgenerator.rb', line 512 def ivar(wnode, ivar) (wn = WNode.new(:insn, wnode)).wtype = ivar.wtype wn.c(:load_offset, wasm_type: ivar.wasm_type, offset: lambda { ivar.offset }) self._self_(wn) wn end |
#ivars_setup(wnode) ⇒ Object
Postprocess ivars (called at end of class parsing)
297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/rlang/parser/wgenerator.rb', line 297 def ivars_setup(wnode) wnc = wnode.class_wnode raise "Cannot find class for attributes definition!!" unless wnc klass = wnc.klass logger.debug "Postprocessing ivars for class #{klass.name}..." klass.ivars.each do |iv| iv.offset = klass.offset logger.debug "... ivar #{iv.name} has offset #{iv.offset}" # Update offset for next ivar klass.offset += iv.size end end |
#ivasgn(wnode, ivar) ⇒ Object
Set instance variable
504 505 506 507 508 509 |
# File 'lib/rlang/parser/wgenerator.rb', line 504 def ivasgn(wnode, ivar) (wn = WNode.new(:insn, wnode)).wtype = ivar.wtype wn.c(:store_offset, wasm_type: ivar.wasm_type, offset: lambda { ivar.offset }) self._self_(wn) wn end |
#klass(wnode, class_path, super_class_path) ⇒ Object
Create class and its basic methods (new, initialize and size)
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/rlang/parser/wgenerator.rb', line 181 def klass(wnode, class_path, super_class_path) logger.debug "Defining klass #{class_path} < #{super_class_path}" # See if class already created if (k = wnode.find_class_or_module(class_path)) return k.wnode end # Make sure super class is known like Ruby does if super_class_path.empty? # special case to bootstrap Object class if (class_path == [:Object] && wnode.in_root_scope?) sk = nil else sk = @object_class super_class_path << sk.path_name end else sk = wnode.find_class_or_module(super_class_path) raise "Unknown super class #{super_class_path}" unless sk end # Create class object and class wnode if it doesn't exist yet # only one level deep class for now so scope class is always # Object class if (class_path == [:Object] && wnode.in_root_scope?) k = wnode.create_class(class_path, super_class_path) else k = wnode.find_or_create_class(class_path, super_class_path) end # make sure the super class is correct in case class # was previously declared in a result directive where # no super class can be specified k.super_class = sk if sk # Create methods Class::new, Class#initialize and Class::_size_ # (do not generate the code yet as the end user code may # define its own implementation in the class body) k.wnode.find_or_create_method(k, :new, :class, k.wtype, true) k.wnode.find_or_create_method(k, :_size_, :class, WType::DEFAULT, true) k.wnode.find_or_create_method(k, :initialize, :instance, WType.new(:none), true) k.wnode end |
#locals(wnode) ⇒ Object
431 432 433 434 435 436 437 438 439 |
# File 'lib/rlang/parser/wgenerator.rb', line 431 def locals(wnode) wnm = wnode.method_wnode wnm.method.lvars.reverse.each do |lvar| logger.debug("Prepending local #{lvar.inspect}") wn = WNode.new(:insn, wnm, true) wn.wtype = lvar.wtype wn.c(:local, name: lvar.wasm_name, wasm_type: wn.wasm_type) end end |
#lvar(wnode, lvar) ⇒ Object
Read local variable
553 554 555 556 557 |
# File 'lib/rlang/parser/wgenerator.rb', line 553 def lvar(wnode, lvar) (wn = WNode.new(:insn, wnode)).wtype = lvar.wtype wn.c(:local_get, var_name: lvar.wasm_name) wn end |
#lvasgn(wnode, lvar) ⇒ Object
Create the local variable storage node
546 547 548 549 550 |
# File 'lib/rlang/parser/wgenerator.rb', line 546 def lvasgn(wnode, lvar) (wn = WNode.new(:insn, wnode)).wtype = lvar.wtype wn.c(:local_set, var_name: lvar.wasm_name) wn end |
#module(wnode, module_path) ⇒ Object
Create module object and module wnode if it doesn’t exist yet
231 232 233 234 |
# File 'lib/rlang/parser/wgenerator.rb', line 231 def module(wnode, module_path) m = wnode.find_or_create_module(module_path) m.wnode end |
#native_operator(wnode, operator, wtype = WType.new(:none)) ⇒ Object
just create a wnode for the WASM operator Do not set wtype or a code template yet, wait until operands type is known (see operands below)
761 762 763 764 765 766 767 768 769 770 771 772 |
# File 'lib/rlang/parser/wgenerator.rb', line 761 def native_operator(wnode, operator, wtype=WType.new(:none)) if (op = ALL_OPS_MAP[operator]) (wn_op = WNode.new(:insn, wnode)).wtype = wtype wn_op.c(:operator, wasm_type: wn_op.wasm_type, operator: op) logger.debug "Creating operator #{operator} wnode: #{wn_op}" # special case for - unary operator transformed into (0 - x) WNode.new(:insn, wn_op).c(:const, wasm_type: wn_op.wasm_type, value: 0) if operator == :-@ wn_op else raise "operator '#{operator}' not supported" end end |
#next(wnode) ⇒ Object
1009 1010 1011 1012 1013 1014 |
# File 'lib/rlang/parser/wgenerator.rb', line 1009 def next(wnode) # look for loop wnode upper in the tree # branch to that label (wn = WNode.new(:insn, wnode)).c(:br, label: wnode.loop_wnode.label) wn end |
#nop(wnode) ⇒ Object
565 566 567 568 |
# File 'lib/rlang/parser/wgenerator.rb', line 565 def nop(wnode) (wn = WNode.new(:insn, wnode)).c(:nop) wn end |
#operands(wnode_op, wnode_recv, wnode_args) ⇒ Object
Finish the setting of the operator node, attach operands and see if they need implicit type casting
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 |
# File 'lib/rlang/parser/wgenerator.rb', line 777 def operands(wnode_op, wnode_recv, wnode_args) logger.debug "Processing operands in operator wnode: #{wnode_op}..." # Do not post process operands if the operator # wnode is a call (= overloaded operator) # and not a native operand if wnode_op.template == :call logger.debug "Doing nothing because it's a func call..." return wnode_op end # A native operator only expects 0 (unary) or 1 (binary) # argument in addition to the receiver raise "only 0 or 1 operand expected (got #{wnode_args.count})" if wnode_args.count > 1 op = wnode_op.wargs[:operator] #wnode_recv = wnode_op.children[0] #wnode_args = wnode_op.children[1..-1] # First find out the wtype that has precedence leading_wtype = self.class.leading_wtype(wnode_recv, *wnode_args) wnode_op.wtype = leading_wtype logger.debug "leading type cast: #{leading_wtype}" # Attach receiver and argument to the operator wnode # type casting them if necessary # Note : normally for an operator there is only one argument # but process args as if they were many one day. logger.debug "Perform implicit type casting of receviver and operator arg(s)" self.cast(wnode_recv, leading_wtype).reparent_to(wnode_op) wnode_args.each do |wna| self.cast(wna, leading_wtype).reparent_to(wnode_op) end # Once operands casting is done, see if we need the signed or unsigned # version of the native operator # NOTE : At this stage, after the operands casting both of them # should either be signed or unsigned, hence the XNOR sanity check # below if ALL_SIGNED_OPS_MAP.values.include? op if !((signed = wnode_recv.wtype.signed?) ^ (wnode_args.empty? ? true : wnode_args.first.wtype.signed?)) wnode_op.wargs[:operator] = SIGNED_OPS[signed ? :signed : :unsigned][op] logger.debug "Receiver has wtype #{wnode_recv.wtype} / Argument has wtype #{wnode_args.first.wtype}" logger.debug "Replacing #{op} operator with #{wnode_op.wargs[:operator]}" op = wnode_op.wargs[:operator] else raise "Type mismatch between operands. Receiver is #{wnode_recv.wtype} and argument is #{wnode_args.first.wtype}" end end # if the receiver is a class object and not # a native integer then pointer arithmetic # applies (like in C) if wnode_recv.wtype.class? raise "Only #{LEGAL_CLASS_WASM_OPS.join(', ')} operators are supported on objects (got #{op} in #{wnode_op})" \ unless LEGAL_CLASS_WASM_OPS.include?(op) # if :add or :sub operator then multiply arg by size of object # like in C if [:add, :sub].include? op (wn_mulop = WNode.new(:insn, wnode_op)).wtype = WType::DEFAULT wn_mulop.c(:operator, wasm_type: wn_mulop.wasm_type, operator: :mul) WNode.new(:insn, wn_mulop).c(:call, func_name: "$#{wnode_recv.wtype.name}::#{SIZE_METHOD}") wnode_args.first.reparent_to(wn_mulop) else # It's a relational operator. In this case # the type of the operator node is always the # default type because a comparison between # object pointers gives a boolean (0 or 1) wnode_op.wtype = WType::DEFAULT end end logger.debug "Operands in operator wnode after postprocessing: #{wnode_op}..." wnode_op end |
#params(wnode) ⇒ Object
412 413 414 415 416 417 418 419 420 421 |
# File 'lib/rlang/parser/wgenerator.rb', line 412 def params(wnode) wnm = wnode.method_wnode # use reverse to preserve proper param order wnm.method.margs.reverse.each do |marg| logger.debug("Prepending param #{marg}") wn = WNode.new(:insn, wnm, true) wn.wtype = marg.wtype wn.c(:param, name: marg.wasm_name, wasm_type: wn.wasm_type) end end |
#phony(wnode, type = :none) ⇒ Object
Generate a phony node (generally used to create a wnode subtree under the phony node and later reparent it to the proper place in the wtree)
585 586 587 |
# File 'lib/rlang/parser/wgenerator.rb', line 585 def phony(wnode, type=:none) WNode.new(type, wnode) end |
#prepend(wnode, module_path) ⇒ Object
247 248 249 250 251 252 253 254 255 256 |
# File 'lib/rlang/parser/wgenerator.rb', line 247 def prepend(wnode, module_path) m = wnode.find_module(module_path) raise "Unknown module #{module_path}. Please require module first." \ unless m wnc = wnode.class_or_module_wnode raise "Cannot find scope class/module for prepended module!!" unless wnc wnc.klass.prepend(m) m.prepended! wnc end |
#result(wnode) ⇒ Object
423 424 425 426 427 428 429 |
# File 'lib/rlang/parser/wgenerator.rb', line 423 def result(wnode) unless wnode.wtype.blank? wn = WNode.new(:insn, wnode, true) wn.wtype = wnode.wtype wn.c(:result, wasm_type: wn.wasm_type) end end |
#return(wnode) ⇒ Object
958 959 960 961 |
# File 'lib/rlang/parser/wgenerator.rb', line 958 def return(wnode) (wn = WNode.new(:insn, wnode)).c(:return) wn end |
#send_method(wnode, class_path, method_name, method_type) ⇒ Object
generate code for method call
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 |
# File 'lib/rlang/parser/wgenerator.rb', line 912 def send_method(wnode, class_path, method_name, method_type) logger.debug "In call generator for #{class_path}::#{method_name}" if k = wnode.find_class_or_module(class_path) method = wnode.find_method(k, method_name, method_type) if k.wtype.native? && ALL_OPS_MAP.has_key?(method_name) # An Rlang method exist for this class but methods corresponding to # Webassembly native operators applied to native Webassembly types # (I32, I64, F32, F64) **cannot** be overriden by instance methods # in Rlang code if method logger.warn "Rlang #{class_path}::#{method_name} method ignored. Native operator has precedence" end logger.debug "Apply native operator #{method_name} to native wtype #{class_path}" wn_call = self.native_operator(wnode, method_name, WType.new(class_path)) elsif !k.wtype.native? && ALL_OPS_MAP.has_key?(method_name) && method.nil? # Similarly if the Class is not a native type (a regular class) # and the Class doesn't provide its own method implementation of the native # operator then apply the native operands logger.debug "Apply native operator #{method_name} to class found : #{class_path}" wn_call = self.native_operator(wnode, method_name, WType.new(class_path)) elsif method logger.debug "Found method #{method.name} in class #{method.klass.name}" (wn_call = WNode.new(:insn, wnode)).c(:call, func_name: method.wasm_name) wn_call.wtype = method.wtype wn_call else raise "Unknown method '#{method_name}' in class #{class_path}" end elsif ALL_OPS_MAP.has_key? method_name # It is a native Wasm operator logger.debug "Native operator found : #{class_path}::#{method_name}" wn_call = self.native_operator(wnode, method_name, WType.new(class_path)) else raise "Unknown method '#{method_name}' in class #{class_path}" end wn_call end |
#static_new(wnode, class_path) ⇒ Object
Statically allocate an object in data segment with the size of the class
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 |
# File 'lib/rlang/parser/wgenerator.rb', line 852 def static_new(wnode, class_path) klass = wnode.find_class_or_module(class_path) if klass.size > 0 data_label = "#{klass.path_name}_new_#{@new_count += 1}" data = DAta.new(data_label.to_sym, "\x00"*klass.wnode.class_size) address = data.address else # TODO: point to address 0. It is not safe but normally # this class is without attribute so the code will never # use memory address to access attribute address = 0 end (wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: address) # VERY IMPORTANT the wtype of this node is the Class name !!! wn_object_addr.wtype = WType.new(klass.path_name) wn_object_addr end |
#string_dynamic_new(wnode, string) ⇒ Object
Dynamic new string object
615 616 617 618 619 620 621 622 623 624 625 626 |
# File 'lib/rlang/parser/wgenerator.rb', line 615 def string_dynamic_new(wnode, string) klass = wnode.find_current_class_or_module() data_label = "#{klass.name}_string_#{@static_count += 1}" # Note : data_stg is nil if string is empty data_stg = self.allocate_string_static_data(string, data_label) string_new_source = STRING_NEW_TMPL % { ptr: data_stg ? data_stg.address : 0, length: string.length } #puts string_new_source;exit self.parser.parse(string_new_source, wnode) end |
#string_static_new(wnode, string) ⇒ Object
Static new string object
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 |
# File 'lib/rlang/parser/wgenerator.rb', line 596 def string_static_new(wnode, string) klass = wnode.find_current_class_or_module() data_label = "#{klass.name}_string_#{@static_count += 1}" # Allocate string data statically # Note : data_stg is nil if string is empty data_stg = self.allocate_string_static_data(string, data_label) # align on :I32 boundary # then allocate the String object attributes # and set them up DAta.align(4) data_len = DAta.append("#{data_label}_len".to_sym, string.length, WType::DEFAULT) data_ptr = DAta.append("#{data_label}_ptr".to_sym, data_stg ? data_stg.address : 0, WType::DEFAULT) # Generate address wnode (wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: data_len.address) wn_object_addr.wtype = WType.new(:String) wn_object_addr end |
#then(wnode) ⇒ Object
968 969 970 971 |
# File 'lib/rlang/parser/wgenerator.rb', line 968 def then(wnode) (wn = WNode.new(:insn, wnode)).c(:then) wn end |
#while(wnode) ⇒ Object
978 979 980 981 982 983 |
# File 'lib/rlang/parser/wgenerator.rb', line 978 def while(wnode) (wnb = WNode.new(:insn, wnode)).c(:block, label: wnb.set_label) (wnl = WNode.new(:insn, wnb)).c(:loop, label: wnl.set_label) (wnbi = WNode.new(:insn, wnl)).c(:br_if, label: wnb.label) return wnb,wnbi,wnl end |
#while_cond(wnode, wnode_cond_exp) ⇒ Object
This is a post processing of the while exp wnode because br_if requires to negate the original while condition
988 989 990 991 992 993 |
# File 'lib/rlang/parser/wgenerator.rb', line 988 def while_cond(wnode, wnode_cond_exp) wn_eqz = WNode.new(:insn, wnode) wn_eqz.c(:eqz, wasm_type: wnode_cond_exp.wasm_type) wnode_cond_exp.reparent_to(wn_eqz) wn_eqz end |