From 3a365c779a8cf98a8ea5590c0ab2d3fd8dc86fc1 Mon Sep 17 00:00:00 2001 From: Torsten Ruger Date: Sat, 10 Mar 2018 18:47:36 +0530 Subject: [PATCH] setup to resolve method dynamically --- lib/mom/dynamic_call.rb | 23 ++++---- lib/vool/statements/send_statement.rb | 53 ++++++++----------- .../to_mom/send/test_send_cached_simple.rb | 20 +++---- 3 files changed, 40 insertions(+), 56 deletions(-) diff --git a/lib/mom/dynamic_call.rb b/lib/mom/dynamic_call.rb index 196ca7a5..07e1960f 100644 --- a/lib/mom/dynamic_call.rb +++ b/lib/mom/dynamic_call.rb @@ -2,23 +2,22 @@ module Mom # A dynamic call calls a method at runtime. This off course implies that we don't know the # method at compile time and so must "find" it. Resolving, or finding the method, is a - # a seperate step though, and here we assume there is a Method instance in some variable + # a seperate step though, and here we assume that we know this Method instance. # - # The only argument given is the variable's name. - # The instruction thus load the variable, finds the jump address from it and jumps there - # (ie calls). Calls are after all just jumps with the intent to return. Return addresses - # are setup in the preamble. + # Both (to be called) Method instance and the type of receiver are stored as + # variables here. The type is used to check before calling. # - # As a side note: All argument setup/handling is outside the scope of this Instruction - # and assumed to be done beforehand. - # Also, in an ideal world we would check that the variable actually holds a Method - # but at the momeent we just assume it. + # Setting up the method is not part of the instructions scope. That setup + # includes the type check and any necccessay method resolution. + # See vool send statement # class DynamicCall < Instruction - attr_reader :method_var_name + attr :cached_type + attr :cached_method - def initialize(method_var_name) - @method_var_name = method_var_name + def initialize(type = nil, method = nil) + @cached_type = type + @cached_method = method end end diff --git a/lib/vool/statements/send_statement.rb b/lib/vool/statements/send_statement.rb index ca85400f..0cb72b05 100644 --- a/lib/vool/statements/send_statement.rb +++ b/lib/vool/statements/send_statement.rb @@ -1,10 +1,21 @@ module Vool + # Sending in a dynamic language is off course not as simple as just calling. + # The function that needs to be called depends after all on the receiver, + # and no guarantees can be made on what that is. + # + # It helps to know that usually (>99%) the class of the receiver does not change. + # Our stategy then is to cache the functions and only dynamically determine it in + # case of a miss (the 1%, and first invocation) + # + # As cache key we must use the type of the object (which is the first word of _every_ object) + # as that is constant, and function implementations depend on the type (not class) class SendStatement < Statement attr_reader :name , :receiver , :arguments def initialize(name , receiver , arguments ) @name , @receiver , @arguments = name , receiver , arguments @arguments ||= [] + @dynamic = nil end def collect(arr) @@ -15,17 +26,6 @@ module Vool super end - # Sending in a dynamic language is off course not as simple as just calling. - # The function that needs to be called depends after all on the receiver, - # and no guarantees can be made on what that is. - # - # It helps to know that usually (>99%) the class of the receiver does not change. - # Our stategy then is to cache the functions and only dynamically determine it in - # case of a miss (the 1%, and first invocation) - # - # As cache key we must use the type of the object (which is the first word of _every_ object) - # as that is constant, and function implementations depend on the type (not class) - # # A Send breaks down to 2 steps: # - Setting up the next message, with receiver, arguments, and (importantly) return address # - a CachedCall , or a SimpleCall, depending on wether the receiver type can be determined @@ -63,7 +63,6 @@ module Vool # - check the cached type and if neccessary update # - call the cached method def cached_call(in_method) - create_tmps(in_method) Mom::Statements.new( cache_check(in_method) + call_cached_method(in_method) ) end @@ -78,7 +77,7 @@ module Vool # if cached_type != current_type # cached_type = current_type # cached_method = current_type.resolve_method(method.name) - if_true = [build_type_cache_update , build_method_cache_update] + if_true = [*build_type_cache_update , *build_method_cache_update(in_method)] #@if_true.to_mom( in_method ) #find and assign [Mom::IfStatement.new( build_condition , if_true )] end @@ -86,34 +85,24 @@ module Vool # this may look like a simple_call, but the difference is that we don't know # the method until run-time. Alas the setup is the same def call_cached_method(in_method) - message_setup(in_method) << Mom::DynamicCall.new(method_var_name) - end - private - # cached type and method are stored in the frame as local variables. - # this creates the varables in the frame. Names are method_var_name and type_var_name - def create_tmps(in_method) - in_method.create_tmp + @dynamic = Mom::DynamicCall.new() + message_setup(in_method) << @dynamic end - # we store the (one!) cached mathod in the frame, under the name that this - # method returns - def method_var_name - "cached_method_#{object_id}" - end - def type_var_name - "cached_type_#{object_id}" - end private def build_condition - cached_type = Mom::SlotDefinition.new(:message , [:frame , type_var_name]) + cached_type = Mom::SlotDefinition.new(@dynamic , [:cached_type]) current_type = Mom::SlotDefinition.new(:message , [:self , :type]) Mom::NotSameCheck.new(cached_type , current_type) end def build_type_cache_update - 1 + [Mom::SlotMove.new([@dynamic, :cached_type] , [:self , :type])] end - def build_method_cache_update - 1 + def build_method_cache_update(in_method) + receiver = StringStatement.new(@name) + resolve = SendStatement.new(:resolve_method , receiver , [SelfStatement.new]) + move_method = Mom::SlotMove.new([@dynamic, :cached_method] , [:self , :return]) + resolve.to_mom(in_method) << move_method end end 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 199a0f36..5865dcf1 100644 --- a/test/vool/to_mom/send/test_send_cached_simple.rb +++ b/test/vool/to_mom/send/test_send_cached_simple.rb @@ -22,15 +22,16 @@ module Vool def test_if_condition_set assert_equal Mom::NotSameCheck , @first.condition.class , @first end - def test_if_true_set - assert @first.if_true , @first + def test_if_true_moves_type + assert_equal @first.if_true[0].class, Mom::SlotMove , @first.to_rxf end - def test_if_true_not_empty - assert @first.if_true.first , @first.to_rxf - end - def test_if_true_not_empty - assert @first.if_true.first , @first.to_rxf + def test_if_true_resolves + assert_equal @first.if_true[1] , 2, @first.if_true.to_rxf end + + + + def test_setup_second assert_equal Mom::MessageSetup , @second.class , @second.to_rxf end @@ -43,11 +44,6 @@ module Vool assert_equal Mom::DynamicCall , @fourth.class , @fourth.to_rxf end - def test_call_third - assert @fourth.cached_method.start_with?("cached_") , @fourth.to_rxf - assert @fourth.cached_type.start_with?("cached_") , @fourth.to_rxf - end - def est_receiver_move_class assert_equal Mom::SlotConstant, @first.class end