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/message"
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)
@name = name
@super_class_name = superclass
@meta_class = MetaClass.new( self )
@meta_class = MetaClass.new( self , self.type || @name)
end
def rxf_reference_name

View File

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

View File

@ -30,36 +30,6 @@ module Parfait
8
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
# or more exactly the type that has a class_name "name"
def get_factory_for(name)
@ -88,11 +58,6 @@ module Parfait
types[hash] = type
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
def get_all_methods
methods = []

View File

@ -197,7 +197,9 @@ module Parfait
end
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
end
@ -267,7 +269,8 @@ module Parfait
def hash
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|
item_hash = Type.str_hash(name) + Type.str_hash(type)
hash_code += item_hash + (item_hash / 256 ) * index

View File

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

View File

@ -2,7 +2,63 @@ require_relative "fake_memory"
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
def self.allocate
r = super
@ -35,7 +91,7 @@ module Parfait
# 0 -based index
def set_internal_word(index , value)
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" )
value
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
# The general idea is that compiling is creating an object graph. Functionally
# 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
#
# And so we have a chicken and egg problem. At the end of the boot function we want to have a
# working Space object
# And so we have a chicken and egg problem. At the end of the boot function we want
# to have a working Space object
# 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.
# 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
# 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
# - create a BootSpace that has BootClasses , used only during booting
# - create the Class objects and assign them to the types
# - flesh out the types , create the real space
# - and finally load the methods
# 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
def self.boot!(options)
Parfait::Object.set_object_space( nil ) # in case we are rebooting
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 = {}
space = Space.new( )
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
type_type = types[:Type]
types.each do |name , type |
type.set_type(type_type)
end
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
# cant set it before or new will try to take types from it
Parfait::Object.set_object_space( space )
fix_types
space.init_mem(options)
end
# 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)
def self.fix_types(types , classes)
type_names.each do |name , ivars |
type = types[name]
clazz = classes[name]
type.set_object_class( clazz )
type.init_lists({type: :Type }.merge(ivars))
def self.fix_types
ObjectSpace.each_object(Parfait::Object) { |o| Parfait.set_type_for(o) }
classes = Parfait.object_space.classes
class_type = Parfait.object_space.get_type_by_class_name(:Class)
types = Parfait.object_space.types
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
@ -196,4 +132,5 @@ module Parfait
# FIXME Now that we use instance variables in parfait, they should be parsed
# and the type_names generated automatically
end
end

View File

@ -48,7 +48,7 @@ module Parfait
def test_word_class
word = @space.classes[:Word]
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.object_id , t_word.object_id
end

View File

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

View File

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