Simplify Parfait booting

Since some weeks, Parfait uses instance variables instead of generated attribute getters (that needed type)
This makes it possible to simplify the boot process, getting rid of separate boot Space and class versions.
It is still quite order dependent, but all "normal" ruby code, (less magic) so easier to understand.

Also moved all code that can never run at runtime into the adapter. This included Space and Object new, space as the space will only ever be created at compile time and object, since that is quite different at run-time (which is where i am working towards)
This commit is contained in:
Torsten Rüger 2019-09-22 19:10:47 +03:00
parent a496ea7e4b
commit e61c5d4a55
11 changed files with 105 additions and 174 deletions

View File

@ -22,32 +22,3 @@ require_relative "parfait/type"
require_relative "parfait/cache_entry" require_relative "parfait/cache_entry"
require_relative "parfait/message" require_relative "parfait/message"
require_relative "parfait/space" require_relative "parfait/space"
module Parfait
# temporary shorthand getter for the space
# See implementation, space is now moved to inside the Object class
# (not module anymore), but there is a lot of code (about 100, 50/50 li/test)
# still calling this old version and since it is shorter . . .
def self.object_space
Object.object_space
end
class Object
# redefine the runtime version
def self.new( *args )
object = self.allocate
# have to grab the class, because we are in the ruby class not the parfait one
cl = Parfait.object_space.get_class_by_name( self.name.split("::").last.to_sym)
# and have to set the type before we let the object do anything. otherwise boom
object.set_type cl.instance_type
object.send :initialize , *args
object
end
# Setter fo the boot process, only at runtime.
# only one space exists and it is generated at compile time, not runtime
def self.set_object_space( space )
@object_space = space
end
end
end

View File

@ -30,7 +30,7 @@ module Parfait
super(instance_type) super(instance_type)
@name = name @name = name
@super_class_name = superclass @super_class_name = superclass
@meta_class = MetaClass.new( self ) @meta_class = MetaClass.new( self , self.type || @name)
end end
def rxf_reference_name def rxf_reference_name

View File

@ -25,10 +25,9 @@ module Parfait
8 8
end end
def initialize( clazz ) def initialize( clazz , clazz_type)
type = Object.object_space.get_type_by_class_name(:Object) raise "No type for #{clazz.name}" unless clazz_type
raise "No type for #{clazz.name}" unless type super( clazz_type )
super( type )
@clazz = clazz @clazz = clazz
end end

View File

@ -30,36 +30,6 @@ module Parfait
8 8
end end
def initialize( classes , pages)
@classes = classes
@types = Dictionary.new
classes.each do |name , cl|
add_type(cl.instance_type)
end
@factories = Dictionary.new
[:Integer , :ReturnAddress , :Message].each do |fact_name|
for_type = classes[fact_name].instance_type
page_size = pages[fact_name] || 1024
factory = Factory.new( for_type , page_size )
factory.get_more
factories[ fact_name ] = factory
end
init_message_chain( factories[ :Message ].reserve )
init_message_chain( factories[ :Message ].next_object )
@true_object = Parfait::TrueClass.new
@false_object = Parfait::FalseClass.new
@nil_object = Parfait::NilClass.new
end
def init_message_chain( message )
prev = nil
while(message)
message.initialize
message._set_caller(prev) if prev
prev = message
message = message.next_message
end
end
# return the factory for the given type # return the factory for the given type
# or more exactly the type that has a class_name "name" # or more exactly the type that has a class_name "name"
def get_factory_for(name) def get_factory_for(name)
@ -88,11 +58,6 @@ module Parfait
types[hash] = type types[hash] = type
end end
# get a type by the type hash (the hash is what uniquely identifies the type)
def get_type_for( hash )
@types[hash]
end
# all methods form all types # all methods form all types
def get_all_methods def get_all_methods
methods = [] methods = []

View File

@ -197,7 +197,9 @@ module Parfait
end end
def set_object_class(oc) def set_object_class(oc)
raise "object class should be a class, not #{oc.class}" unless oc.is_a?(Class) unless oc.is_a?(Class) #but during boot a symbol is ok
raise "object class should be a class, not #{oc.class}" unless oc.is_a?(Symbol)
end
@object_class = oc @object_class = oc
end end
@ -267,7 +269,8 @@ module Parfait
def hash def hash
index = 1 index = 1
hash_code = Type.str_hash( object_class.name ) name = object_class.is_a?(Symbol) ? object_class : object_class.name
hash_code = Type.str_hash(name)
each do |name , type| each do |name , type|
item_hash = Type.str_hash(name) + Type.str_hash(type) item_hash = Type.str_hash(name) + Type.str_hash(type)
hash_code += item_hash + (item_hash / 256 ) * index hash_code += item_hash + (item_hash / 256 ) * index

View File

@ -22,9 +22,9 @@ end
require_relative "risc/position/position" require_relative "risc/position/position"
require_relative "risc/platform" require_relative "risc/platform"
require "parfait"
require_relative "risc/parfait_boot" require_relative "risc/parfait_boot"
require_relative "risc/parfait_adapter" require_relative "risc/parfait_adapter"
require "parfait"
require_relative "risc/linker" require_relative "risc/linker"
require_relative "risc/callable_compiler" require_relative "risc/callable_compiler"
require_relative "risc/method_compiler" require_relative "risc/method_compiler"

View File

@ -2,7 +2,63 @@ require_relative "fake_memory"
module Parfait module Parfait
class Object ; end class Object
# redefine the runtime version
def self.new( *args )
object = self.allocate
Parfait.set_type_for(object)
object.send :initialize , *args
object
end
# Setter fo the boot process, only at runtime.
# only one space exists and it is generated at compile time, not runtime
def self.set_object_space( space )
@object_space = space
end
end
def self.set_type_for(object)
return unless(Parfait.object_space)
name = object.class.name.split("::").last.to_sym
# have to grab the class, because we are in the ruby class not the parfait one
cl = Parfait.object_space.get_class_by_name( name )
# and have to set the type before we let the object do anything. otherwise boom
raise "No such class #{name} for #{object}" unless cl
object.set_type cl.instance_type
end
class Space < Object
# Space can only ever be creared at compile time, not runtime
def initialize( )
@classes = Dictionary.new
@types = Dictionary.new
@factories = Dictionary.new
@true_object = Parfait::TrueClass.new
@false_object = Parfait::FalseClass.new
@nil_object = Parfait::NilClass.new
end
def init_mem(pages)
[:Integer , :ReturnAddress , :Message].each do |fact_name|
for_type = classes[fact_name].instance_type
page_size = pages[fact_name] || 1024
factory = Factory.new( for_type , page_size )
factory.get_more
factories[ fact_name ] = factory
end
init_message_chain( factories[ :Message ].reserve )
init_message_chain( factories[ :Message ].next_object )
end
def init_message_chain( message )
prev = nil
while(message)
message.initialize
message._set_caller(prev) if prev
prev = message
message = message.next_message
end
end
end
class DataObject < Object class DataObject < Object
def self.allocate def self.allocate
r = super r = super
@ -35,7 +91,7 @@ module Parfait
# 0 -based index # 0 -based index
def set_internal_word(index , value) def set_internal_word(index , value)
name = Parfait.name_for_index(self , index) name = Parfait.name_for_index(self , index)
raise "no string #{name.class}" unless name.is_a?(Symbol) raise "not sym for #{index} in #{self}:#{self.type}:#{name.class}" unless name.is_a?(Symbol)
instance_eval("@#{name}=value" ) instance_eval("@#{name}=value" )
value value
end end

View File

@ -1,43 +1,11 @@
module Boot
# Booting is complicated, so it is extracted into this file, even it has only one entry point
# a ruby object as a placeholder for the parfait Space during boot
class Space
attr_reader :classes , :types
def initialize
@types = {}
@classes = {}
end
def get_class_by_name(name)
cl = @classes[name]
raise "No class for #{name}" unless cl
cl
end
def get_type_by_class_name(name)
@types[name]
end
end
# another ruby object to shadow the parfait, just during booting.
# all it needs is the type, which we make the Parfait type
class Class
attr_reader :instance_type
def initialize( type)
@instance_type = type
end
end
end
module Parfait module Parfait
# The general idea is that compiling is creating an object graph. Functionally # The general idea is that compiling is creating an object graph. Functionally
# one tends to think of methods, and that is complicated enough, sure. # one tends to think of methods, and that is complicated enough, sure.
# But for an object system the graph includes classes and all instance variables # But for an object system the graph includes classes and all instance variables
# #
# And so we have a chicken and egg problem. At the end of the boot function we want to have a # And so we have a chicken and egg problem. At the end of the boot function we want
# working Space object # to have a working Space object
# But that has instance variables (List and Dictionary) and off course a class. # But that has instance variables (List and Dictionary) and off course a class.
# Or more precisely in rubyx, a Type, that points to a class. # Or more precisely in rubyx, a Type, that points to a class.
# So we need a Type, but that has Type and Class too. hmmm # So we need a Type, but that has Type and Class too. hmmm
@ -47,73 +15,41 @@ module Parfait
# (PPS: The "real" solution is to read a rx-file graph and not do this by hand # (PPS: The "real" solution is to read a rx-file graph and not do this by hand
# That graph can be programatically built and written (with this to boot that process :-)) # That graph can be programatically built and written (with this to boot that process :-))
# There are some helpers below, but the roadmap is something like:
# - create all the Type instances, with their basic types, but no classes # temporary shorthand getter for the space
# - create a BootSpace that has BootClasses , used only during booting # See implementation, space is now moved to inside the Object class
# - create the Class objects and assign them to the types # (not module anymore), but there is a lot of code (about 100, 50/50 li/test)
# - flesh out the types , create the real space # still calling this old version and since it is shorter . . .
# - and finally load the methods def self.object_space
Object.object_space
end
def self.boot!(options) def self.boot!(options)
Parfait::Object.set_object_space( nil ) # in case we are rebooting space = Space.new( )
types = boot_types
boot_boot_space( types )
classes = boot_classes( types )
fix_types( types , classes )
space = Space.new( classes , options )
Parfait::Object.set_object_space( space )
end
# types is where the snake bites its tail. Every chain ends at a type and then it
# goes around (circular references). We create them from the list below, just as empty
# shells, that we pass back, for the BootSpace to be created
def self.boot_types
types = {}
type_names.each do |name , ivars | type_names.each do |name , ivars |
types[name] = Type.allocate ivars[:type] = :Type
instance_type = Type.new(name , ivars)
space.add_type instance_type
space.classes[name] = Class.new(name , nil , instance_type)
end end
type_type = types[:Type] # cant set it before or new will try to take types from it
types.each do |name , type | Parfait::Object.set_object_space( space )
type.set_type(type_type) fix_types
end space.init_mem(options)
types
end
# The BootSpace is an object that holds fake classes, that hold _real_ types
# Once we plug it in we can use .new
# then we need to create the parfait classes and fix the types before creating a Space
def self.boot_boot_space(types)
boot_space = Boot::Space.new
types.each do |name , type|
clazz = Boot::Class.new(type)
boot_space.classes[name] = clazz
boot_space.types[name] = type
end
Parfait::Object.set_object_space( boot_space )
end
# when running code instantiates a class, a type is created automatically
# but even to get our space up, we have already instantiated all types
# so we have to continue and allocate classes and fill the data by hand
# and off cource we can't use space.create_class , but still they need to go there
def self.boot_classes(types)
classes = Dictionary.new
classes.type = types[:Dictionary]
type_names.each do |name , vars|
super_c = super_class_names[name] || :Object
clazz = Class.new(name , super_c , types[name] )
classes[name] = clazz
end
classes
end end
# Types are hollow shells before this, so we need to set the object_class # Types are hollow shells before this, so we need to set the object_class
# and initialize the list variables (which we now can with .new) # and initialize the list variables (which we now can with .new)
def self.fix_types(types , classes) def self.fix_types
type_names.each do |name , ivars | ObjectSpace.each_object(Parfait::Object) { |o| Parfait.set_type_for(o) }
type = types[name] classes = Parfait.object_space.classes
clazz = classes[name] class_type = Parfait.object_space.get_type_by_class_name(:Class)
type.set_object_class( clazz ) types = Parfait.object_space.types
type.init_lists({type: :Type }.merge(ivars)) classes.each do |name , cl|
object_type = Parfait.object_space.get_type_by_class_name(name)
cl.meta_class.set_instance_variable(:@instance_type, class_type)
cl.set_instance_variable( :@instance_type , object_type)
object_type.set_object_class(cl)
end end
end end
@ -196,4 +132,5 @@ module Parfait
# FIXME Now that we use instance variables in parfait, they should be parsed # FIXME Now that we use instance variables in parfait, they should be parsed
# and the type_names generated automatically # and the type_names generated automatically
end end
end end

View File

@ -48,7 +48,7 @@ module Parfait
def test_word_class def test_word_class
word = @space.classes[:Word] word = @space.classes[:Word]
assert word.instance_type assert word.instance_type
t_word = @space.get_type_for(word.instance_type.hash) t_word = @space.types[word.instance_type.hash]
assert_equal word.instance_type.hash , t_word.hash assert_equal word.instance_type.hash , t_word.hash
assert_equal word.instance_type.object_id , t_word.object_id assert_equal word.instance_type.object_id , t_word.object_id
end end

View File

@ -18,7 +18,7 @@ module Parfait
def test_types_hashes def test_types_hashes
types = @space.types types = @space.types
types.each do |has , type| types.each do |has , type|
assert has.is_a?(::Integer) , has.inspect assert has.is_a?(::Integer) , has.class
end end
end end
def test_classes_types_in_space_types def test_classes_types_in_space_types
@ -29,14 +29,14 @@ module Parfait
def test_class_types_are_stored def test_class_types_are_stored
@space.classes.each do |name,clazz| @space.classes.each do |name,clazz|
assert @space.get_type_for(clazz.instance_type.hash) assert @space.types[clazz.instance_type.hash]
end end
end end
def test_class_types_are_identical def test_class_types_are_identical
@space.classes.each do |name , clazz| @space.classes.each do |name , clazz|
cl_type = @space.get_type_for(clazz.instance_type.hash) cl_type = @space.types[clazz.instance_type.hash]
assert_equal cl_type.object_id , clazz.instance_type.object_id assert_equal cl_type.object_id , clazz.instance_type.object_id , name
end end
end end

View File

@ -14,7 +14,7 @@ module Parfait
end end
def test_length def test_length
assert @types.length > 11 assert_equal 27 , @types.length
end end
def test_two_hashs_not_equal def test_two_hashs_not_equal