diff --git a/lib/mom/instruction/instruction.rb b/lib/mom/instruction/instruction.rb index f230aa80..6e7bcba0 100644 --- a/lib/mom/instruction/instruction.rb +++ b/lib/mom/instruction/instruction.rb @@ -32,6 +32,7 @@ require_relative "check" require_relative "basic_values" require_relative "simple_call" require_relative "dynamic_call" +require_relative "resolve_method" require_relative "truth_check" require_relative "not_same_check" require_relative "jump" diff --git a/lib/mom/instruction/message_setup.rb b/lib/mom/instruction/message_setup.rb index 742cd64b..0c09e0c7 100644 --- a/lib/mom/instruction/message_setup.rb +++ b/lib/mom/instruction/message_setup.rb @@ -27,17 +27,17 @@ module Mom # Move method name, frame and arguemnt types from the method to the next_message # Get the message from Space and link it. def to_risc(compiler) - build_with(compiler.builder) + build_with(compiler.builder(false)) end # directly called by to_risc # but also used directly in __init def build_with(builder) from = method_source - risc = builder.build_and_return { typed_method << from } - risc << build_message_data(builder) + builder.build { typed_method << from } + build_message_data(builder) builder.compiler.reset_regs - return risc + return builder.built end private @@ -49,7 +49,7 @@ module Mom # also put it into next_message of current message (and reverse) # set name and type data in the message, from the method loaded def build_message_data( builder ) - builder.build_and_return do + builder.build do space << Parfait.object_space next_message << space[:first_message] message[:next_message] << next_message diff --git a/lib/mom/instruction/resolve_method.rb b/lib/mom/instruction/resolve_method.rb new file mode 100644 index 00000000..a91f51af --- /dev/null +++ b/lib/mom/instruction/resolve_method.rb @@ -0,0 +1,67 @@ +module Mom + + # Dynamic method resolution is at the heart of a dynamic language, and here + # is the Mom level instruction to do it. + # + # When the static type can not be determined a CacheEntry is used to store + # type and method of the resolved method. The CacheEntry is shared with + # DynamicCall instruction who is responsible for calling the method in the entry. + # + # This instruction resolves the method, in case the types don't match (and + # at least on first encouter) + # + # This used to be a method, but we don't really need the method setup etc + # + class ResolveMethod < Instruction + attr :cache_entry , :name + + def initialize(name , cache_entry) + @name = name + @cache_entry = cache_entry + end + + # resolve the method name of self, on the given object + # may seem wrong way around at first sight, but we know the type of string. And + # thus resolving this method happens at compile time, whereas any method on an + # unknown self (the object given) needs resolving and that is just what we are doing + # ( ie the snake bites it's tail) + # This method is just a placeholder until boot is over and the real method is + # parsed. + def to_risc( compiler ) + name = @name + cache_entry = @cache_entry + return Risc.label("hi" , "there") + builder = compiler.builder(false) + builder.build do + word << name + + type << cache_entry + type << type[:cached_type] + typed_method << type[:methods] + + add while_start_label + space << Parfait.object_space + space << space[:nil_object] + + space - typed_method + if_zero exit_label + + name << typed_method[:name] + name - word + + if_not_zero false_label + message[:return_value] << typed_method + add Mom::ReturnSequence.new.to_risc(compiler) + + add false_label + typed_method << typed_method[:next_method] + branch while_start_label + + add exit_label + end + Risc::Builtin::Object.emit_syscall( builder , :exit ) + return builder.built + end + + end +end diff --git a/lib/risc/boot.rb b/lib/risc/boot.rb index ff487d9b..21c74969 100644 --- a/lib/risc/boot.rb +++ b/lib/risc/boot.rb @@ -176,7 +176,7 @@ module Risc end obj = space.get_class_by_name(:Word) - [:putstring , :get_internal_byte , :set_internal_byte , :resolve_method].each do |f| + [:putstring , :get_internal_byte , :set_internal_byte ].each do |f| obj.instance_type.add_method Builtin::Word.send(f , nil) end diff --git a/lib/vool/statements/send_statement.rb b/lib/vool/statements/send_statement.rb index 7b21f5bb..ce1c5a7a 100644 --- a/lib/vool/statements/send_statement.rb +++ b/lib/vool/statements/send_statement.rb @@ -94,8 +94,8 @@ module Vool def cache_check(in_method) ok = Mom::Label.new("cache_ok_#{self.object_id}") check = build_condition(ok) # if cached_type != current_type - check << build_type_cache_update # cached_type = current_type - check << build_method_cache_update(in_method)# cached_method = current_type.resolve_method(method.name) + check << Mom::SlotLoad.new([dynamic_call.cache_entry, :cached_type] , [:message , :receiver , :type]) + check << Mom::ResolveMethod.new( @name , dynamic_call.cache_entry ) check << ok end @@ -111,14 +111,5 @@ module Vool current_type = Mom::SlotDefinition.new(:message , [:receiver , :type]) Mom::NotSameCheck.new(cached_type , current_type, ok_label) end - def build_type_cache_update - Mom::SlotLoad.new([dynamic_call.cache_entry, :cached_type] , [:message , :receiver , :type]) - end - def build_method_cache_update(in_method) - receiver = SymbolConstant.new(@name) - resolve = SendStatement.new(:resolve_method , receiver , [@receiver]) - move_method = Mom::SlotLoad.new([dynamic_call.cache_entry, :cached_method] , [:message , :return_value]) - resolve.to_mom(in_method) << move_method - end end end diff --git a/test/parfait/test_space.rb b/test/parfait/test_space.rb index 989059d5..c325188a 100644 --- a/test/parfait/test_space.rb +++ b/test/parfait/test_space.rb @@ -24,7 +24,7 @@ class TestSpace < MiniTest::Test end def test_methods_booted word = @space.get_class_by_name(:Word).instance_type - assert_equal 4 , word.method_names.get_length + assert_equal 3 , word.method_names.get_length assert word.get_method(:putstring) , "no putstring" end diff --git a/test/risc/interpreter/test_dynamic_call.rb b/test/risc/interpreter/test_dynamic_call.rb index 5bec4ce8..1f4837b4 100644 --- a/test/risc/interpreter/test_dynamic_call.rb +++ b/test/risc/interpreter/test_dynamic_call.rb @@ -29,13 +29,10 @@ module Risc IsZero, SlotToReg, OperatorInstruction, IsNotZero, Label, SlotToReg, Branch, Label, LoadConstant, SlotToReg, OperatorInstruction, IsZero, SlotToReg, OperatorInstruction, IsNotZero, - Label, SlotToReg, Branch, Label, LoadConstant, - SlotToReg, OperatorInstruction, IsZero, SlotToReg, OperatorInstruction, - IsNotZero, Label, SlotToReg, Branch, Label, - LoadConstant, SlotToReg, OperatorInstruction, IsZero, SlotToReg, - OperatorInstruction, IsNotZero, Label, SlotToReg, Branch, - Label, LoadConstant, SlotToReg, OperatorInstruction, IsZero, - Label, Transfer, Syscall, NilClass] + RegToSlot, SlotToReg, SlotToReg, RegToSlot, SlotToReg, + SlotToReg, FunctionReturn, SlotToReg, LoadConstant, RegToSlot, + Label, LoadConstant, LoadConstant, SlotToReg, RegToSlot, + RegToSlot, SlotToReg, SlotToReg] #assert_equal 1 , get_return end diff --git a/test/vool/to_mom/send/test_send_cached_simple.rb b/test/vool/to_mom/send/test_send_cached_simple.rb index a9c0a2a5..8a33dbea 100644 --- a/test/vool/to_mom/send/test_send_cached_simple.rb +++ b/test/vool/to_mom/send/test_send_cached_simple.rb @@ -19,15 +19,15 @@ module Vool assert_equal :type , load.right.slots[1] , load end def test_check_resolve_call - assert_equal SimpleCall , @ins.next(5).class , @ins + assert_equal ResolveMethod , @ins.next(3).class , @ins end def test_dynamic_call_last assert_equal DynamicCall , @ins.last.class , @ins end def test_array - check_array [SlotLoad, NotSameCheck, SlotLoad, MessageSetup, ArgumentTransfer, SimpleCall , - SlotLoad, Label, MessageSetup, ArgumentTransfer, DynamicCall] , @ins + check_array [SlotLoad, NotSameCheck, SlotLoad, ResolveMethod, Label, MessageSetup , + ArgumentTransfer, DynamicCall] , @ins end end