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:
parent
a496ea7e4b
commit
e61c5d4a55
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 = []
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user