From e61c5d4a5507e5582cea901a77f5d37a25383704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20R=C3=BCger?= Date: Sun, 22 Sep 2019 19:10:47 +0300 Subject: [PATCH] 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) --- lib/parfait.rb | 29 -------- lib/parfait/class.rb | 2 +- lib/parfait/meta_class.rb | 7 +- lib/parfait/space.rb | 35 --------- lib/parfait/type.rb | 7 +- lib/risc.rb | 2 +- lib/risc/parfait_adapter.rb | 60 +++++++++++++++- lib/risc/parfait_boot.rb | 125 ++++++++------------------------- test/parfait/test_space.rb | 2 +- test/parfait/test_space2.rb | 8 +-- test/parfait/type/test_hash.rb | 2 +- 11 files changed, 105 insertions(+), 174 deletions(-) diff --git a/lib/parfait.rb b/lib/parfait.rb index e7d82079..2c37587f 100644 --- a/lib/parfait.rb +++ b/lib/parfait.rb @@ -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 diff --git a/lib/parfait/class.rb b/lib/parfait/class.rb index 457f1688..4df67556 100644 --- a/lib/parfait/class.rb +++ b/lib/parfait/class.rb @@ -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 diff --git a/lib/parfait/meta_class.rb b/lib/parfait/meta_class.rb index 5155bfa0..da4324e7 100644 --- a/lib/parfait/meta_class.rb +++ b/lib/parfait/meta_class.rb @@ -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 diff --git a/lib/parfait/space.rb b/lib/parfait/space.rb index 8ae1d591..a926771a 100644 --- a/lib/parfait/space.rb +++ b/lib/parfait/space.rb @@ -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 = [] diff --git a/lib/parfait/type.rb b/lib/parfait/type.rb index 6ad902ad..63259ad1 100644 --- a/lib/parfait/type.rb +++ b/lib/parfait/type.rb @@ -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 diff --git a/lib/risc.rb b/lib/risc.rb index e983631c..43e44134 100644 --- a/lib/risc.rb +++ b/lib/risc.rb @@ -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" diff --git a/lib/risc/parfait_adapter.rb b/lib/risc/parfait_adapter.rb index f0623973..618772b9 100644 --- a/lib/risc/parfait_adapter.rb +++ b/lib/risc/parfait_adapter.rb @@ -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 diff --git a/lib/risc/parfait_boot.rb b/lib/risc/parfait_boot.rb index 922c0f1d..e46ada6f 100644 --- a/lib/risc/parfait_boot.rb +++ b/lib/risc/parfait_boot.rb @@ -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 diff --git a/test/parfait/test_space.rb b/test/parfait/test_space.rb index 5795cbf9..87de0bc6 100644 --- a/test/parfait/test_space.rb +++ b/test/parfait/test_space.rb @@ -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 diff --git a/test/parfait/test_space2.rb b/test/parfait/test_space2.rb index b6450ada..ea93ed6b 100644 --- a/test/parfait/test_space2.rb +++ b/test/parfait/test_space2.rb @@ -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 diff --git a/test/parfait/type/test_hash.rb b/test/parfait/type/test_hash.rb index d509b6ce..1a6af7de 100644 --- a/test/parfait/type/test_hash.rb +++ b/test/parfait/type/test_hash.rb @@ -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