by reacting to the change _before it happens, we can move any BinaryCode out of the way So when Instruction are inserted and code gets inserted, we don't need to set up the correct listener explicitly (which is tricky across mathods and changing chains), but instead just move anything that is in the way along
189 lines
5.6 KiB
Ruby
189 lines
5.6 KiB
Ruby
require "util/eventable"
|
|
|
|
module Risc
|
|
# Positions are very different during compilation and run-time.
|
|
# At run-time they are inherrent to the object, and fixed.
|
|
# While during compilation we can move things about, and do not use the
|
|
# objects memory position at all.
|
|
#
|
|
# Furthermore, there are differnet kind of positions during compilation.
|
|
# Off course the object position as hinted above, but also instruction
|
|
# positions, that do not reflect the position of the object, but of the
|
|
# assembled instruction in the binary.
|
|
#
|
|
# The Position module keeps a hash of all compile time positions.
|
|
#
|
|
# While the (different)Position objects transmit the change that (re) positioning
|
|
# entails to affected objects.
|
|
|
|
class Position
|
|
include Util::Logging
|
|
log_level :info
|
|
|
|
include Util::Eventable
|
|
|
|
attr_reader :at , :object
|
|
|
|
# initialize with a given object, first parameter
|
|
# The object ill be the key in global position map
|
|
# Give an integer as the actual position, where -1
|
|
# which means no legal position known
|
|
def initialize(object , pos )
|
|
@at = pos
|
|
@object = object
|
|
Position.set_to(self , pos)
|
|
end
|
|
|
|
# utility to register events of type :position_changed
|
|
# can give an object and a PositionListener will be created for it
|
|
def position_listener(listener)
|
|
unless listener.class.name.include?("Listener")
|
|
listener = PositionListener.new(listener)
|
|
end
|
|
register_event(:position_changed , listener)
|
|
end
|
|
|
|
# When instruction get inserted, we have to move listeners around, remove given
|
|
def remove_position_listener(list)
|
|
unregister_event(:position_changed, list)
|
|
end
|
|
|
|
# utility to get all registered listeners to the :position_changed event
|
|
# returns an array
|
|
def position_listeners
|
|
event_table[:position_changed]
|
|
end
|
|
|
|
#look for InstructionListener and return its code if found
|
|
def get_code
|
|
listener = event_table.find{|one| one.class == InstructionListener}
|
|
return nil unless listener
|
|
listener.code
|
|
end
|
|
|
|
def valid?
|
|
@at != -1
|
|
end
|
|
|
|
def set(int)
|
|
return int if int == self.at
|
|
trigger_changing( int )
|
|
Position.set_to(self , int)
|
|
@at = int
|
|
trigger_changed
|
|
int
|
|
end
|
|
|
|
# helper to fire the event that the position is about to change
|
|
# the argument is the new position (as int)
|
|
def trigger_changing( to )
|
|
event_table[:position_changed].each { |handler| handler.position_changing( self , to) }
|
|
end
|
|
|
|
# helper to fire the event that the position has changed
|
|
# Note: set checks if the position actually has changed, before fireing
|
|
# but during insert it is helpful to trigger just to set the next
|
|
def trigger_changed
|
|
trigger(:position_changed , self )
|
|
end
|
|
|
|
def trigger_inserted
|
|
event_table[:position_changed].each { |handler| handler.position_inserted( self) }
|
|
end
|
|
|
|
def +(offset)
|
|
offset = offset.at if offset.is_a?(Position)
|
|
@at + offset
|
|
end
|
|
|
|
def -(offset)
|
|
offset = offset.at if offset.is_a?(Position)
|
|
@at - offset
|
|
end
|
|
|
|
def <(right)
|
|
right = right.at if right.is_a?(Position)
|
|
@at < right
|
|
end
|
|
|
|
def >(right)
|
|
right = right.at if right.is_a?(Position)
|
|
@at > right
|
|
end
|
|
|
|
def to_s
|
|
"0x#{@at.to_s(16)}"
|
|
end
|
|
|
|
def object_class
|
|
return :object if @object.is_a?(Parfait::Object)
|
|
return :object if @object.class.name.include?("Test")
|
|
:instruction
|
|
end
|
|
|
|
def next_slot
|
|
return -1 if at < 0
|
|
slot = at + object.padded_length
|
|
self.log.debug "Next Slot @#{at.to_s(16)} for #{object.class}(#{object.padded_length.to_s(16)}) == #{(slot).to_s(16)}"
|
|
slot
|
|
end
|
|
|
|
## class level forward and reverse cache
|
|
@positions = {}
|
|
@reverse_cache = {}
|
|
|
|
def self.positions
|
|
@positions
|
|
end
|
|
|
|
def self.clear_positions
|
|
@positions = {}
|
|
@reverse_cache = {}
|
|
end
|
|
|
|
def self.at( int )
|
|
@reverse_cache[int]
|
|
end
|
|
|
|
def self.set?(object)
|
|
self.positions.has_key?(object)
|
|
end
|
|
|
|
# get a position from the cache (object -> position)
|
|
# unless it's a label, then get the position of it's next
|
|
def self.get(object)
|
|
pos = self.positions[object]
|
|
if pos == nil
|
|
str = "position accessed but not initialized, "
|
|
str += "0x#{object.object_id.to_s(16)}\n"
|
|
str += "for #{object.class} "
|
|
str += "byte_length #{object.byte_length}" if object.respond_to?(:byte_length)
|
|
str += " for #{object.to_s[0...130]}"
|
|
raise str
|
|
end
|
|
pos
|
|
end
|
|
|
|
# populate the position caches (forward and revese) with the given position
|
|
# forward caches object -> position
|
|
# reverse caches position.at > position
|
|
# Labels do not participatein reverse cache
|
|
def self.set_to( position , to)
|
|
postest = Position.positions[position.object] unless to < 0
|
|
raise "Mismatch #{position}" if postest and postest != position
|
|
@reverse_cache.delete(position.at) unless position.object.is_a?(Label)
|
|
testing = self.at( position.at ) unless position.at < 0
|
|
if testing and testing.object_class != position.object_class
|
|
raise "Mismatch (at #{to.to_s(16)}) new:#{position} #{position.object.class} , was:#{testing}#{testing.object.class}"
|
|
end
|
|
self.positions[position.object] = position
|
|
@reverse_cache[to] = position unless position.object.is_a?(Label)
|
|
log.debug "Set #{position} to 0x#{to.to_s(16)} for #{position.object.class} #{position.object.object_id.to_s(16)}"
|
|
position
|
|
end
|
|
end
|
|
end
|
|
require_relative "position_listener"
|
|
require_relative "instruction_listener"
|
|
require_relative "code_listener"
|