From 6993e337bfc78db7fc1d3901250c13fc69f001b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20R=C3=BCger?= Date: Mon, 23 Sep 2019 20:42:46 +0300 Subject: [PATCH 1/3] Fix meta_class, sis lass instance variables and class methods after some serious recursive thinking it now actually makes sense The key was to change the actual type of the class that the meta_class manages For objects it's (still) ok just to change the instance_type, but since the class object exists and has type, when adding instance variables, that actual type has to change --- lib/parfait/behaviour.rb | 17 +++++++--- lib/parfait/meta_class.rb | 6 ++++ lib/risc/parfait_adapter.rb | 14 ++------ test/parfait/test_meta_class.rb | 17 ++++++---- .../risc/interpreter/class/test_class_inst.rb | 4 +-- .../interpreter/class/test_class_inst_set.rb | 2 +- test/vool/class_send/test_class_instance.rb | 32 +++++++++++++++---- .../class_send/test_class_send_inherited.rb | 2 +- 8 files changed, 60 insertions(+), 34 deletions(-) diff --git a/lib/parfait/behaviour.rb b/lib/parfait/behaviour.rb index 492c3858..66b3032d 100644 --- a/lib/parfait/behaviour.rb +++ b/lib/parfait/behaviour.rb @@ -1,8 +1,17 @@ -# Behaviour is something that has methods, basically class and modules superclass - -# instance_methods is the attribute in the including class that has the methods - module Parfait + # Behaviour is the old smalltalk name for the duperclass of class and meta_class + # + # Classes and meta_classes are in fact very similar, in that they manage + # - the type of instances + # - the methods for instances + # + # The main way they differ is that Classes manage type for a class of objects (ie many) + # whereas meta_class, or singleton_class manages the type of only one object (here a class) + # + # Singleton classes can manage the type/methods of any single object, and in the + # future off course they will, just not yet. Most single objects don't need that, + # only Classes and Modules _always _ do, so that's where we start. + # class Behaviour < Object attr_reader :instance_type , :instance_methods diff --git a/lib/parfait/meta_class.rb b/lib/parfait/meta_class.rb index da4324e7..cbcfb64b 100644 --- a/lib/parfait/meta_class.rb +++ b/lib/parfait/meta_class.rb @@ -43,6 +43,12 @@ module Parfait inspect end + # adding an instance changes the instance_type to include that variable + def add_instance_variable( name , type) + super(name,type) + @clazz.set_type(@instance_type) + end + # Nil name means no superclass, and so nil returned def super_class return nil diff --git a/lib/risc/parfait_adapter.rb b/lib/risc/parfait_adapter.rb index dd527a98..36dfd265 100644 --- a/lib/risc/parfait_adapter.rb +++ b/lib/risc/parfait_adapter.rb @@ -85,14 +85,14 @@ module Parfait # 0 -based index def get_internal_word(index) return @type if index == Parfait::TYPE_INDEX - name = Parfait.name_for_index(self , index) + name = self.type.names[index] return nil unless name instance_eval("@#{name}") end # 0 -based index def set_internal_word(index , value) - name = Parfait.name_for_index(self , index) + name = self.type.names[index] #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 @@ -100,16 +100,6 @@ module Parfait end - def self.name_for_index(object , index) - return :type if index == 0 - clazz = object.class.name.split("::").last.to_sym - cl = self.type_names[clazz] - keys = cl.keys - keys[index - 1] # -1 because type is excluded in the lists (FIX) - # FIXME Now that we use instance variables in parfait, they should be parsed - # and the type_names generated automatically - end - # new list from ruby array to be precise def self.new_list array list = Parfait::List.new diff --git a/test/parfait/test_meta_class.rb b/test/parfait/test_meta_class.rb index 3a284194..71d67b1b 100644 --- a/test/parfait/test_meta_class.rb +++ b/test/parfait/test_meta_class.rb @@ -36,15 +36,18 @@ module Parfait assert_raises{ @try.add_instance_method(nil)} end def test_add_instance_variable_changes_type - before = @space.get_class.instance_type - @space.get_class.add_instance_variable(:counter , :Integer) - assert before != @space.get_class.instance_type + before = @try.instance_type + @try.add_instance_variable(:counter , :Integer) + assert before != @try.instance_type end def test_add_instance_variable_changes_type_hash - before = @space.get_class.instance_type.hash - @space.get_class.add_instance_variable(:counter , :Integer) - assert before != @space.get_class.instance_type.hash + before = @try.instance_type.hash + @try.add_instance_variable(:counter , :Integer) + assert before != @try.instance_type.hash + end + def test_add_instance_variable_changes_class_type + @try.add_instance_variable(:counter , :Integer) + assert_equal @try.clazz.type , @try.instance_type end - end end diff --git a/test/risc/interpreter/class/test_class_inst.rb b/test/risc/interpreter/class/test_class_inst.rb index fdfe4b2d..e549102a 100644 --- a/test/risc/interpreter/class/test_class_inst.rb +++ b/test/risc/interpreter/class/test_class_inst.rb @@ -1,7 +1,7 @@ require_relative "../helper" module Risc - class InterpreterSetters < MiniTest::Test + class InterpreterGetters < MiniTest::Test include Ticker def setup @@ -19,7 +19,7 @@ module Risc MAIN super end - +#Space type is wrong, shold be same as meta_class.instance_type def test_chain #show_main_ticks # get output of what is run_input @string_input diff --git a/test/risc/interpreter/class/test_class_inst_set.rb b/test/risc/interpreter/class/test_class_inst_set.rb index 32b0c2f1..a50b7ef9 100644 --- a/test/risc/interpreter/class/test_class_inst_set.rb +++ b/test/risc/interpreter/class/test_class_inst_set.rb @@ -23,7 +23,7 @@ MAIN super end - def est_chain + def test_chain #show_main_ticks # get output of what is run_input @string_input assert_equal 5 , get_return diff --git a/test/vool/class_send/test_class_instance.rb b/test/vool/class_send/test_class_instance.rb index 71767e59..f34ff75d 100644 --- a/test/vool/class_send/test_class_instance.rb +++ b/test/vool/class_send/test_class_instance.rb @@ -12,7 +12,7 @@ module Vool return @inst end def main(arg) - return Space.one_plus + return Space.some_inst end end eos @@ -20,16 +20,34 @@ module Vool def setup ret = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(class_main) - @ins = ret.compilers.find{|c|c.callable.name==:some_inst}.mom_instructions.next + @compiler = ret.compilers.find{|c|c.callable.name==:some_inst} + @main = ret.compilers.find{|c|c.callable.name==:main} + @ins = @compiler.mom_instructions.next end def test_class_inst - space_class = Parfait.object_space.get_class - assert_equal :Space , space_class.name - names = space_class.meta_class.instance_type.names - assert names.index_of(:inst) , names + space_class = Parfait.object_space.get_class + assert_equal :Space , space_class.name + names = space_class.meta_class.instance_type.names + assert names.index_of(:inst) , names + end + def test_compiler + assert_equal Mom::MethodCompiler, @compiler.class + assert_equal Parfait::Type, @compiler.callable.self_type.class + assert_equal 6, @compiler.callable.self_type.names.index_of(:inst) , @compiler.callable.self_type.names end def test_array - check_array [SlotLoad, ReturnJump, Label, ReturnSequence, Label] , @ins + check_array [SlotLoad, ReturnJump, Label, ReturnSequence, Label] , @ins + end + def test_main_array + check_array [MessageSetup, ArgumentTransfer, SimpleCall, SlotLoad, ReturnJump , + Label, ReturnSequence, Label] , @main.mom_instructions.next + end + def test_main_args + args = @main.mom_instructions.next(2) + assert_equal Parfait::Class , args.receiver.known_object.class + assert_equal :Space , args.receiver.known_object.name + assert_equal :some_inst , args.receiver.known_object.type.method_names.first + assert_equal :inst , args.receiver.known_object.type.names.last end def test_load_inst assert_equal SlotLoad, @ins.class diff --git a/test/vool/class_send/test_class_send_inherited.rb b/test/vool/class_send/test_class_send_inherited.rb index f78641ae..54c431f5 100644 --- a/test/vool/class_send/test_class_send_inherited.rb +++ b/test/vool/class_send/test_class_send_inherited.rb @@ -40,7 +40,7 @@ module Vool assert_equal SimpleCall, @ins.next(2).class assert_equal :one_plus, @ins.next(2).method.name assert_equal Parfait::Type, @ins.next(2).method.self_type.class - assert_equal :Object, @ins.next(2).method.self_type.object_class.name + assert_equal :Class, @ins.next(2).method.self_type.object_class.name end end end From d901c6f27ce1dff6ed8f5ded84f0b44666c8c8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20R=C3=BCger?= Date: Mon, 23 Sep 2019 20:57:33 +0300 Subject: [PATCH 2/3] rename meta to singleton class seems more appropriate, as it is the class for a single object Also seems to be called that on the net (don't remember where the meta came from, but it's gone) --- lib/parfait.rb | 2 +- lib/parfait/behaviour.rb | 6 +++--- lib/parfait/class.rb | 4 ++-- .../{meta_class.rb => singleton_class.rb} | 16 ++++++++-------- lib/risc/parfait_boot.rb | 8 ++++---- lib/vool/class_expression.rb | 6 +++--- lib/vool/class_method_expression.rb | 2 +- lib/vool/variables.rb | 2 +- ...est_meta_class.rb => test_singleton_class.rb} | 6 +++--- test/parfait/test_space.rb | 10 +++++----- test/risc/interpreter/class/test_class_inst.rb | 2 +- test/vool/class_send/test_class_instance.rb | 2 +- 12 files changed, 33 insertions(+), 33 deletions(-) rename lib/parfait/{meta_class.rb => singleton_class.rb} (69%) rename test/parfait/{test_meta_class.rb => test_singleton_class.rb} (90%) diff --git a/lib/parfait.rb b/lib/parfait.rb index 2c37587f..1e0b9cb7 100644 --- a/lib/parfait.rb +++ b/lib/parfait.rb @@ -9,7 +9,7 @@ require_relative "parfait/integer" require_relative "parfait/factory" require_relative "parfait/behaviour" require_relative "parfait/class" -require_relative "parfait/meta_class" +require_relative "parfait/singleton_class" require_relative "parfait/list" require_relative "parfait/word" require_relative "parfait/binary_code" diff --git a/lib/parfait/behaviour.rb b/lib/parfait/behaviour.rb index 66b3032d..99f1f370 100644 --- a/lib/parfait/behaviour.rb +++ b/lib/parfait/behaviour.rb @@ -1,12 +1,12 @@ module Parfait - # Behaviour is the old smalltalk name for the duperclass of class and meta_class + # Behaviour is the old smalltalk name for the duperclass of class and singleton_class # - # Classes and meta_classes are in fact very similar, in that they manage + # Classes and singleton_classes are in fact very similar, in that they manage # - the type of instances # - the methods for instances # # The main way they differ is that Classes manage type for a class of objects (ie many) - # whereas meta_class, or singleton_class manages the type of only one object (here a class) + # whereas singleton_class, or singleton_class manages the type of only one object (here a class) # # Singleton classes can manage the type/methods of any single object, and in the # future off course they will, just not yet. Most single objects don't need that, diff --git a/lib/parfait/class.rb b/lib/parfait/class.rb index 4df67556..b82015b2 100644 --- a/lib/parfait/class.rb +++ b/lib/parfait/class.rb @@ -17,7 +17,7 @@ module Parfait class Class < Behaviour - attr_reader :name , :super_class_name , :meta_class + attr_reader :name , :super_class_name , :singleton_class def self.type_length 6 @@ -30,7 +30,7 @@ module Parfait super(instance_type) @name = name @super_class_name = superclass - @meta_class = MetaClass.new( self , self.type || @name) + @singleton_class = SingletonClass.new( self , self.type || @name) end def rxf_reference_name diff --git a/lib/parfait/meta_class.rb b/lib/parfait/singleton_class.rb similarity index 69% rename from lib/parfait/meta_class.rb rename to lib/parfait/singleton_class.rb index cbcfb64b..a5b1eef9 100644 --- a/lib/parfait/meta_class.rb +++ b/lib/parfait/singleton_class.rb @@ -1,20 +1,20 @@ # -# In many respects a MetaClass is like a Class. We haven't gone to the full ruby/oo level -# yet, where the metaclass is actually a class instance, but someday. +# In many respects a SingletonClass is like a Class. We haven't gone to the full ruby/oo level +# yet, where the singleton_class is actually a class instance, but someday. # A Class in general can be viewed as a way to generate methods for a group of objects. -# A MetaClass serves the same function, but just for one object, the class object that it -# is the meta_class of. +# A SingletonClass serves the same function, but just for one object, the class object that it +# is the singleton_class of. # This is slightly different in the way that the type of the class must actually # change, whereas for a class the instance type changes and only objects generated # henceafter have a different type. -# Another current difference is that a metaclass has no superclass. Also no name. -# There is a one to one relationship between a class instance and it's meta_class instance. +# Another current difference is that a singleton_class has no superclass. Also no name. +# There is a one to one relationship between a class instance and it's singleton_class instance. module Parfait - class MetaClass < Behaviour + class SingletonClass < Behaviour attr_reader :clazz @@ -36,7 +36,7 @@ module Parfait end def inspect - "MetaClass(#{@clazz.name})" + "SingletonClass(#{@clazz.name})" end def to_s diff --git a/lib/risc/parfait_boot.rb b/lib/risc/parfait_boot.rb index 2f26f115..1d9f6217 100644 --- a/lib/risc/parfait_boot.rb +++ b/lib/risc/parfait_boot.rb @@ -51,7 +51,7 @@ module Parfait classes.each do |name , cl| object_type = Parfait.object_space.get_type_by_class_name(name) raise "nil type" unless object_type - cl.meta_class.instance_eval{ @instance_type = class_type} + cl.singleton_class.instance_eval{ @instance_type = class_type} cl.instance_eval{ @instance_type = object_type} cl.instance_eval{ @super_class_name = super_names[name] || :Object} object_type.instance_eval{ @object_class = cl } @@ -85,7 +85,7 @@ module Parfait CallableMethod: :Callable, Block: :Callable, Class: :Behaviour, - MetaClass: :Behaviour , + SingletonClass: :Behaviour , ReturnAddress: :Integer} end @@ -106,7 +106,7 @@ module Parfait arguments_type: :Type , self_type: :Type, frame_type: :Type , name: :Word , blocks: :Block} , Class: {instance_methods: :List, instance_type: :Type, - name: :Word, super_class_name: :Word , meta_class: :MetaClass}, + name: :Word, super_class_name: :Word , singleton_class: :SingletonClass}, DataObject: {}, Data4: {}, Data8: {}, @@ -129,7 +129,7 @@ module Parfait local5: :Object, local6: :Object ,local7: :Object, local8: :Object , local9: :Object ,local10: :Object, local11: :Object , local12: :Object, local13: :Object, local14: :Object, local15: :Object}, - MetaClass: {instance_methods: :List, instance_type: :Type, clazz: :Class }, + SingletonClass: {instance_methods: :List, instance_type: :Type, clazz: :Class }, NilClass: {}, Object: {}, ReturnAddress: {next_integer: :ReturnAddress}, diff --git a/lib/vool/class_expression.rb b/lib/vool/class_expression.rb index 2a1234f1..e6873844 100644 --- a/lib/vool/class_expression.rb +++ b/lib/vool/class_expression.rb @@ -37,7 +37,7 @@ module Vool when MethodExpression node.to_mom(@clazz) when ClassMethodExpression - node.to_mom(@clazz.meta_class) + node.to_mom(@clazz.singleton_class) else raise "Only methods for now #{node.class}:#{node}" end @@ -62,7 +62,7 @@ module Vool end # goes through the code looking for instance variables (and their assignments) - # adding each to the respective type, ie class or meta_class, depending + # adding each to the respective type, ie class or singleton_class, depending # on if they are instance or class instance variables. # # Class variables are deemed a design mistake, ie not implemented (yet) @@ -72,7 +72,7 @@ module Vool when MethodExpression target = @clazz when ClassMethodExpression - target = @clazz.meta_class + target = @clazz.singleton_class else raise "Only methods for now #{node.class}:#{node}" end diff --git a/lib/vool/class_method_expression.rb b/lib/vool/class_method_expression.rb index 15ea9b23..fb673e12 100644 --- a/lib/vool/class_method_expression.rb +++ b/lib/vool/class_method_expression.rb @@ -8,7 +8,7 @@ module Vool end def to_mom(clazz) - raise "not meta" unless clazz.class == Parfait::MetaClass + raise "not singleton" unless clazz.class == Parfait::SingletonClass raise( "no class in #{self}") unless clazz method = clazz.add_instance_method_for(name , make_arg_type , make_frame , body ) #puts "CLass method Class:#{clazz}:#{name}" diff --git a/lib/vool/variables.rb b/lib/vool/variables.rb index 2d34149c..2acaa180 100644 --- a/lib/vool/variables.rb +++ b/lib/vool/variables.rb @@ -49,7 +49,7 @@ module Vool class ModuleName < Expression include Named def ct_type - get_named_class.meta_class.instance_type + get_named_class.singleton_class.instance_type end def to_slot(_) return Mom::SlotDefinition.new( get_named_class, []) diff --git a/test/parfait/test_meta_class.rb b/test/parfait/test_singleton_class.rb similarity index 90% rename from test/parfait/test_meta_class.rb rename to test/parfait/test_singleton_class.rb index 71d67b1b..e7195687 100644 --- a/test/parfait/test_meta_class.rb +++ b/test/parfait/test_singleton_class.rb @@ -1,11 +1,11 @@ require_relative "helper" module Parfait - class TestMetaClass < ParfaitTest + class TestSingletonClass < ParfaitTest def setup super - @try = @space.create_class( :Try , :Object).meta_class + @try = @space.create_class( :Try , :Object).singleton_class end def test_type_forclass @@ -17,7 +17,7 @@ module Parfait end def test_new_superclass assert_equal "Class(Try)" , @try.clazz.inspect - assert_equal "MetaClass(Try)" , @try.inspect + assert_equal "SingletonClass(Try)" , @try.inspect end def test_new_methods assert_equal @try.method_names.class, @try.instance_methods.class diff --git a/test/parfait/test_space.rb b/test/parfait/test_space.rb index 87de0bc6..4256a4a1 100644 --- a/test/parfait/test_space.rb +++ b/test/parfait/test_space.rb @@ -6,7 +6,7 @@ module Parfait def classes [:Behaviour ,:BinaryCode,:Block,:CacheEntry,:Callable,:CallableMethod,:Class, :DataObject,:Data4,:Data8,:Data16,:Data32,:Dictionary,:Factory, :Integer,:FalseClass, - :List,:Message, :MetaClass,:NilClass,:Object,:ReturnAddress, + :List,:Message, :SingletonClass,:NilClass,:Object,:ReturnAddress, :Space,:TrueClass,:Type,:VoolMethod,:Word] end @@ -27,8 +27,8 @@ module Parfait def test_get_class_by_name assert_equal Parfait::Class , space_class.class end - def test_get_meta_class - assert_equal Parfait::MetaClass , space_class.meta_class.class + def test_get_singleton_class + assert_equal Parfait::SingletonClass , space_class.singleton_class.class end def test_get_type_by_class_name assert_equal Parfait::Type , Parfait.object_space.get_type_by_class_name(:Space).class @@ -72,9 +72,9 @@ module Parfait end end end - def test_all_meta + def test_all_singletons @space.classes.each do |name , clazz| - assert clazz.meta_class , clazz.name + assert clazz.singleton_class , clazz.name end end def test_has_factory diff --git a/test/risc/interpreter/class/test_class_inst.rb b/test/risc/interpreter/class/test_class_inst.rb index e549102a..c046c6e7 100644 --- a/test/risc/interpreter/class/test_class_inst.rb +++ b/test/risc/interpreter/class/test_class_inst.rb @@ -19,7 +19,7 @@ module Risc MAIN super end -#Space type is wrong, shold be same as meta_class.instance_type +#Space type is wrong, shold be same as singleton_class.instance_type def test_chain #show_main_ticks # get output of what is run_input @string_input diff --git a/test/vool/class_send/test_class_instance.rb b/test/vool/class_send/test_class_instance.rb index f34ff75d..397ef5ab 100644 --- a/test/vool/class_send/test_class_instance.rb +++ b/test/vool/class_send/test_class_instance.rb @@ -27,7 +27,7 @@ module Vool def test_class_inst space_class = Parfait.object_space.get_class assert_equal :Space , space_class.name - names = space_class.meta_class.instance_type.names + names = space_class.singleton_class.instance_type.names assert names.index_of(:inst) , names end def test_compiler From 915a1b5e90dd3c517f2a760cbb8b814ca465f69d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20R=C3=BCger?= Date: Tue, 24 Sep 2019 12:58:31 +0300 Subject: [PATCH 3/3] Fix word offset which had weirdly been 1 off (not weird that it was off, index bugs are common, but weird that the tests passed anyway) --- lib/arm/translator.rb | 4 ++-- lib/parfait/word.rb | 2 +- test/parfait/test_word2.rb | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/arm/translator.rb b/lib/arm/translator.rb index 791021bd..a4a6e82c 100644 --- a/lib/arm/translator.rb +++ b/lib/arm/translator.rb @@ -147,8 +147,8 @@ module Arm send( name , int_code ) end - def putstring( int_code ) - codes = ArmMachine.add( :r1 , :r1 , Parfait::Word.type_length*4 ) # adjust for object header + def putstring( int_code ) # adjust for object header (0 based, hence -1) + codes = ArmMachine.add( :r1 , :r1 , (Parfait::Word.type_length - 1)*4 ) codes.append ArmMachine.mov( :r0 , 1 ) # write to stdout == 1 syscall(int_code , codes ) end diff --git a/lib/parfait/word.rb b/lib/parfait/word.rb index 79355116..b8ce6ee6 100644 --- a/lib/parfait/word.rb +++ b/lib/parfait/word.rb @@ -139,7 +139,7 @@ module Parfait raise "index not integer #{at.class}" unless at.is_a?(::Integer) raise "index must be positive , not #{at}" if (index < 0) raise "index too large #{at} > #{self.length}" if (index >= self.length ) - return index + 8 # type_length * 4 + return index + Word.type_length * 4 end # compare the word to another diff --git a/test/parfait/test_word2.rb b/test/parfait/test_word2.rb index 67b47ee4..fc8e2e23 100644 --- a/test/parfait/test_word2.rb +++ b/test/parfait/test_word2.rb @@ -98,10 +98,10 @@ module Parfait assert_raises {one.set_char(3 , "s".ord)} end def test_max - str = "123456789012345678901234" + str = "12345678901234567890" one = Parfait.new_word(str) assert_equal str , one.to_string - assert_equal 24 , one.length + assert_equal 20 , one.length end end