From 2dcb2a9a72c911a10f13333b52d6f2217dc6c57e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20R=C3=BCger?= Date: Mon, 30 Sep 2019 17:09:13 +0300 Subject: [PATCH] Introduce singleton types Just for future, as this gives us a way to know immediately in the type, which represent normal, and which singleton classes Also instantiate singleton class lazily (with singleton type) This makes the type of class single, ie unique, immediately when it is used, ie methods or variables defined. Fixes a design mistake, where all singletonn classes shared the same type, and thus unique methods per class were impossible (Also some misc in commit) --- lib/mom/method_compiler.rb | 2 +- lib/parfait/class.rb | 8 +++- lib/parfait/singleton_class.rb | 11 ++++-- lib/parfait/type.rb | 38 ++++++++++++++----- lib/parfait/vool_method.rb | 1 + lib/risc/collector.rb | 3 ++ lib/risc/parfait_boot.rb | 6 +-- lib/vool/class_method_expression.rb | 2 +- lib/vool/method_expression.rb | 2 +- lib/vool/statements.rb | 6 +-- test/parfait/test_class.rb | 16 +++++++- test/parfait/test_singleton_class.rb | 6 +++ test/parfait/type/test_basic.rb | 13 ++----- test/risc/interpreter/calling/test_minus.rb | 2 +- test/risc/test_collector.rb | 4 +- test/risc/test_interpreter.rb | 2 +- test/risc/test_linker.rb | 2 +- test/rubyx/parfait/test_integer.rb | 11 ++++-- test/rubyx/rt_parfait/test_object.rb | 2 +- .../class_send/test_class_send_inherited.rb | 2 +- test/vool/class_send/test_send_class.rb | 24 ++++++++++-- test/vool/test_class_method_expression.rb | 13 ++++++- 22 files changed, 124 insertions(+), 52 deletions(-) diff --git a/lib/mom/method_compiler.rb b/lib/mom/method_compiler.rb index 90deb321..85c56c05 100644 --- a/lib/mom/method_compiler.rb +++ b/lib/mom/method_compiler.rb @@ -69,7 +69,7 @@ module Mom return ["arg#{index}".to_sym] end index = @callable.frame_type.variable_index(name) - raise "no such local or argument #{name}" unless index + raise "no such local or argument #{name} for #{callable.name}:#{callable.frame_type.hash}" unless index return ["local#{index}".to_sym] end diff --git a/lib/parfait/class.rb b/lib/parfait/class.rb index 95be1300..4fdc2423 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 , :single_class + attr_reader :name , :super_class_name def self.type_length 6 @@ -30,7 +30,11 @@ module Parfait super(instance_type) @name = name @super_class_name = superclass - @single_class = SingletonClass.new( self , self.type || @name) + end + + def single_class + return @single_class if @single_class + @single_class = SingletonClass.new( self ) end def rxf_reference_name diff --git a/lib/parfait/singleton_class.rb b/lib/parfait/singleton_class.rb index a5b1eef9..e0646a25 100644 --- a/lib/parfait/singleton_class.rb +++ b/lib/parfait/singleton_class.rb @@ -25,16 +25,21 @@ module Parfait 8 end - def initialize( clazz , clazz_type) - raise "No type for #{clazz.name}" unless clazz_type - super( clazz_type ) + def initialize( clazz ) + clazz_hash = clazz.type.to_hash @clazz = clazz + super( Type.for_hash(clazz_hash , self , 1) ) + @clazz.set_type( @instance_type ) end def rxf_reference_name @clazz.name end + def name + :"#{clazz.name}.Single" + end + def inspect "SingletonClass(#{@clazz.name})" end diff --git a/lib/parfait/type.rb b/lib/parfait/type.rb index 9acd2cc4..4cac23f7 100644 --- a/lib/parfait/type.rb +++ b/lib/parfait/type.rb @@ -4,7 +4,7 @@ module Parfait # you want to store values by name (instance variable names). # # One could (like mri), store the names in each object, but that is wasteful in both -# time and space. +# time and space (time for access, space to store implicitly known names ). # Instead we store only the values, and access them by index (bit like c++). # The Type allows the mapping of names to index. @@ -17,20 +17,27 @@ module Parfait # for every Type instance. # But, as we want every Object to have a class, the Type carries that class. -# So the type of type has an entry "object_class" +# So the type of type has an entry "object_class", ir Type has an instance object_class. # But Objects must also be able to carry methods themselves (ruby calls singleton_methods) -# and those too are stored in the Type (both type and class include behaviour) +# and those too are stored in the Type. Those type instances are called singleton +# types, in analogy to the singleton classes they represent. +# In other words, "usually" a type represents a whole group of objects (instances of a +# class at the time the type was the instance_type). But for Singletons, ie objects +# that have a singleton class, the type is only for that object. -# The object is an "List" (memory location) of values of length n +# An object is an "List" (memory location) of values of length n # The Type is a list of n names and n types that describe the values stored in an # actual object. # Together they turn the object into a hash like structure # For types to be a useful concept, they have to be unique and immutable. Any "change", # like adding a name/type pair, will result in a new type instance. +# Type identity can be checked by the hash function, so two types are the same when their +# hashes are the same. The hash is made up of hashing all instance names and the class +# name. -# The Type class carries a hash of types of the systems, which is used to ensure that +# The Space class carries a hash of types of the systems, which is used to ensure that # there is only one instance of every type. Hash and equality are defined on type # for this to work. @@ -39,24 +46,31 @@ module Parfait attr_reader :object_class , :names , :types , :methods def self.type_length - 5 + 6 end - def self.for_hash( hash , object_class = :Object) + # This is the default way to create new type, because we add it to the + # global list, to space. + # The hash (actually the keys of the hash) and the object_class define the + # identity of the type, which can be checked with the hash function. + # single is by default 0, meaning you have to specify explicitly (1) for + # it to be a "singleton" type (see class description) + def self.for_hash( hash , object_class = :Object , single = 0) name = object_class if(object_class.is_a?(Symbol)) object_class = Object.object_space.get_class_by_name(object_class) end raise "No such class #{name}" unless object_class hash = {type: object_class.name }.merge(hash) unless hash[:type] - new_type = Type.new( object_class , hash) + new_type = Type.new( object_class , hash , single) Object.object_space.add_type(new_type) end # should not be called directly. Use Type.for_hash instead, that adds the - # type to the global list - def initialize( object_class , hash ) + # type to the global list and does sym->class conversion if neccessary + def initialize( object_class , hash , single ) super() + @is_single = single @object_class = object_class @methods = nil @names = List.new @@ -68,6 +82,10 @@ module Parfait end end + def is_single? + @is_single == 1 + end + def class_name @object_class&.name end diff --git a/lib/parfait/vool_method.rb b/lib/parfait/vool_method.rb index c166156b..450c0bbb 100644 --- a/lib/parfait/vool_method.rb +++ b/lib/parfait/vool_method.rb @@ -20,6 +20,7 @@ module Parfait @args_type = args_type @frame_type = frame_type @source = source + #raise source.to_s if name == :type_length raise "Name must be symbol" unless name.is_a?(Symbol) raise "args_type must be type" unless args_type.is_a?(Parfait::Type) raise "frame_type must be type" unless frame_type.is_a?(Parfait::Type) diff --git a/lib/risc/collector.rb b/lib/risc/collector.rb index 95ba9aee..131d776e 100644 --- a/lib/risc/collector.rb +++ b/lib/risc/collector.rb @@ -67,6 +67,9 @@ module Risc def self.position!( objekt ) return false if Position.set?(objekt) return true if objekt.is_a? ::Integer + return true if objekt.is_a? ::NilClass + return true if objekt.is_a? ::TrueClass + return true if objekt.is_a? ::FalseClass return true if objekt.is_a?( Risc::Label) #puts "ADD #{objekt.class.name}" unless objekt.is_a?( Parfait::Object) or objekt.is_a?( Symbol) diff --git a/lib/risc/parfait_boot.rb b/lib/risc/parfait_boot.rb index 05841fc2..9e3c139b 100644 --- a/lib/risc/parfait_boot.rb +++ b/lib/risc/parfait_boot.rb @@ -29,7 +29,7 @@ module Parfait space = Space.new( ) type_names.each do |name , ivars | ivars[:type] = :Type - instance_type = Type.new(name , ivars) + instance_type = Type.new(name , ivars , 0) space.add_type instance_type space.classes[name] = Class.new(name , nil , instance_type) end @@ -51,7 +51,6 @@ 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.single_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 } @@ -140,7 +139,8 @@ module Parfait true_object: :TrueClass, false_object: :FalseClass , nil_object: :NilClass}, TrueClass: {}, Type: {names: :List , types: :List , - object_class: :Class, methods: :CallableMethod } , + object_class: :Class, methods: :CallableMethod , + is_single: :Object} , VoolMethod: { name: :Word , args_type: :Type , frame_type: :Type } , Word: {char_length: :Integer , next_word: :Word} , } diff --git a/lib/vool/class_method_expression.rb b/lib/vool/class_method_expression.rb index e432df02..c7cd3faf 100644 --- a/lib/vool/class_method_expression.rb +++ b/lib/vool/class_method_expression.rb @@ -41,7 +41,7 @@ module Vool def to_s(depth = 0) arg_str = @args.collect{|a| a.to_s}.join(', ') - at_depth(depth , "def self.#{name}(#{arg_str})\n#{@body.to_s(depth + 1)}end") + at_depth(depth , "def self.#{name}(#{arg_str})\n#{@body.to_s(1)}\nend") end private diff --git a/lib/vool/method_expression.rb b/lib/vool/method_expression.rb index 0590f15a..de90548a 100644 --- a/lib/vool/method_expression.rb +++ b/lib/vool/method_expression.rb @@ -50,7 +50,7 @@ module Vool def to_s(depth = 0) arg_str = @args.collect{|a| a.to_s}.join(', ') - at_depth(depth , "def #{name}(#{arg_str})\n#{@body.to_s(depth + 1)}\nend") + at_depth(depth , "def #{name}(#{arg_str})\n#{@body.to_s(1)}\nend") end private diff --git a/lib/vool/statements.rb b/lib/vool/statements.rb index 8d7b9f44..8f5a2d0a 100644 --- a/lib/vool/statements.rb +++ b/lib/vool/statements.rb @@ -72,11 +72,7 @@ module Vool first = stats.shift.to_mom(compiler) while( nekst = stats.shift ) next_mom = nekst.to_mom(compiler) - if next_mom.is_a?(Mom::BlockCompiler) - compiler.block_compilers << next_mom - else - first.append next_mom - end + first.append next_mom end first end diff --git a/test/parfait/test_class.rb b/test/parfait/test_class.rb index c802ba42..fe31bff8 100644 --- a/test/parfait/test_class.rb +++ b/test/parfait/test_class.rb @@ -48,6 +48,20 @@ module Parfait @space.get_class.add_instance_variable(:counter , :Integer) assert before != @space.get_class.instance_type.hash end - + def test_has_single + assert_equal SingletonClass , @try.single_class.class + end + def test_before_not_single_type + assert_equal false , @try.type.is_single? + end + def test_single_type_not_class + hash_after = @try.single_class.instance_type.hash + assert_equal @try.type.hash , hash_after + end + def test_single_type_not_class_before + hash_before = @try.type.hash + hash_after = @try.single_class.instance_type.hash + refute_equal hash_before , hash_after + end end end diff --git a/test/parfait/test_singleton_class.rb b/test/parfait/test_singleton_class.rb index 8e4ce436..1332099e 100644 --- a/test/parfait/test_singleton_class.rb +++ b/test/parfait/test_singleton_class.rb @@ -49,5 +49,11 @@ module Parfait @try.add_instance_variable(:counter , :Integer) assert_equal @try.clazz.type , @try.instance_type end + def test_name + assert_equal :"Try.Single" , @try.name + end + def test_type_is_single + assert_equal true , @try.instance_type.is_single? + end end end diff --git a/test/parfait/type/test_basic.rb b/test/parfait/type/test_basic.rb index f10f8f46..8a2b5546 100644 --- a/test/parfait/type/test_basic.rb +++ b/test/parfait/type/test_basic.rb @@ -13,35 +13,29 @@ module Parfait def test_type_index assert_equal @mess.get_type , @mess.get_internal_word(Parfait::TYPE_INDEX) , "mess" end - def test_type_is_first type = @mess.get_type assert_equal 0 , type.variable_index(:type) end - def test_length assert @mess assert @mess.get_type assert_equal 31 , @mess.get_type.instance_length , @mess.get_type.inspect end - def test_names assert @type.names end def test_types assert @type.types end - def test_type_length assert_equal 31 , @mess.get_type.instance_length , @mess.get_type.inspect end - def test_type_length_index type = @mess.get_type.get_type assert_equal 4 , type.variable_index(:methods) assert_equal type.object_class , type.get_internal_word(3) end - def test_no_index_below_0 type = @mess.get_type names = type.names @@ -50,12 +44,10 @@ module Parfait assert type.variable_index(n) >= 0 end end - def test_attribute_set @mess.set_receiver( 55) assert_equal 55 , @mess.receiver end - def test_variable_index assert_equal 1 , @type.variable_index(:next_message) end @@ -68,7 +60,6 @@ module Parfait def test_type_for assert_equal :Message , @type.type_for(:next_message) end - def test_remove_me type = @mess.get_type assert_equal type , @mess.get_internal_word(0) @@ -83,5 +74,9 @@ module Parfait int_class = @space.get_type_by_class_name(:Integer) assert_equal :Integer, int_class.object_class.name end + def test_create_single + single = Type.for_hash( {} , :Object ,1) + assert_equal true , single.is_single? + end end end diff --git a/test/risc/interpreter/calling/test_minus.rb b/test/risc/interpreter/calling/test_minus.rb index bfd2a8b5..dfc1a566 100644 --- a/test/risc/interpreter/calling/test_minus.rb +++ b/test/risc/interpreter/calling/test_minus.rb @@ -38,7 +38,7 @@ module Risc ret = main_ticks(49) assert_equal FunctionReturn , ret.class assert_equal :r3 , ret.register.symbol - assert_equal 38236 , @interpreter.get_register(ret.register) + assert_equal 36540 , @interpreter.get_register(ret.register) end end end diff --git a/test/risc/test_collector.rb b/test/risc/test_collector.rb index 96a8a16d..fec8e424 100644 --- a/test/risc/test_collector.rb +++ b/test/risc/test_collector.rb @@ -38,7 +38,7 @@ module Risc end def len - 1479 + 1426 end def test_collect_all_types @@ -70,7 +70,7 @@ module Risc end def len - 2959 + 2906 end end end diff --git a/test/risc/test_interpreter.rb b/test/risc/test_interpreter.rb index c39f363d..7d133fd3 100644 --- a/test/risc/test_interpreter.rb +++ b/test/risc/test_interpreter.rb @@ -54,7 +54,7 @@ module Risc end def test_pc @interpreter.tick - assert_equal t = 37800 , @interpreter.pc + assert_equal t = 36104 , @interpreter.pc @interpreter.tick assert_equal t + 4 , @interpreter.pc end diff --git a/test/risc/test_linker.rb b/test/risc/test_linker.rb index 758c7826..8a254c93 100644 --- a/test/risc/test_linker.rb +++ b/test/risc/test_linker.rb @@ -24,7 +24,7 @@ module Risc assert_equal 0 , Position.get(@linker.cpu_init).at end def test_cpu_at - assert_equal "0x941c" , Position.get(@linker.cpu_init.first).to_s + assert_equal "0x8d7c" , Position.get(@linker.cpu_init.first).to_s end def test_cpu_label assert_equal Position , Position.get(@linker.cpu_init.first).class diff --git a/test/rubyx/parfait/test_integer.rb b/test/rubyx/parfait/test_integer.rb index fd78654c..3dd3f0cf 100644 --- a/test/rubyx/parfait/test_integer.rb +++ b/test/rubyx/parfait/test_integer.rb @@ -1,7 +1,7 @@ require_relative "../helper" module RubyX - class TestIntegerCompile# < MiniTest::Test + class TestIntegerCompile < MiniTest::Test include ParfaitHelper def setup @compiler = compiler @@ -26,14 +26,17 @@ module RubyX assert_equal :Data8 , vool[3].name end def test_mom - mom = @compiler.ruby_to_mom source + vool = @compiler.ruby_to_vool source + vool.to_parfait + #puts vool + mom = vool.to_mom(nil) assert_equal Mom::MomCollection , mom.class end - def test_risc + def est_risc risc = compiler.ruby_to_risc source assert_equal Risc::RiscCollection , risc.class end - def test_binary + def est_binary risc = compiler.ruby_to_binary source , :interpreter assert_equal Risc::Linker , risc.class end diff --git a/test/rubyx/rt_parfait/test_object.rb b/test/rubyx/rt_parfait/test_object.rb index 1d08d9a7..b50b93f6 100644 --- a/test/rubyx/rt_parfait/test_object.rb +++ b/test/rubyx/rt_parfait/test_object.rb @@ -48,7 +48,7 @@ module RubyX end end end - class TestObjectRtTest #< Minitest::Test + class TestObjectRtTest < Minitest::Test self.class.include ParfaitHelper include Risc::Ticker diff --git a/test/vool/class_send/test_class_send_inherited.rb b/test/vool/class_send/test_class_send_inherited.rb index 037f87ab..50d133a9 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 :Class, @ins.next(2).method.self_type.object_class.name + assert_equal :"Space.Single", @ins.next(2).method.self_type.object_class.name end end end diff --git a/test/vool/class_send/test_send_class.rb b/test/vool/class_send/test_send_class.rb index c904abca..af17b105 100644 --- a/test/vool/class_send/test_send_class.rb +++ b/test/vool/class_send/test_send_class.rb @@ -4,9 +4,25 @@ module Vool class TestSendClassMom < MiniTest::Test include VoolCompile + def class_main + <<-eos + class Space + def self.one_plus(one) + return 1 + 1 + end + end + class Space + def main(arg) + return Space.one_plus(1) + end + end + eos + end + def setup - @compiler = compile_main( "Object.get_internal_word(0)" , "Object.get" ) - @ins = @compiler.mom_instructions.next + source = "class Integer < Data4;def +(other);X.int_operator(:+);end;end;" + class_main + ret = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(source) + @ins = ret.compilers.find_compiler_name(:main).mom_instructions.next end def test_array @@ -37,10 +53,10 @@ module Vool def test_call_is assert_equal SimpleCall, @ins.next(2).class assert_equal Parfait::CallableMethod, @ins.next(2).method.class - assert_equal :get_internal_word, @ins.next(2).method.name + assert_equal :one_plus, @ins.next(2).method.name end def test_call_has_right_receiver - assert_equal "Class_Type", @ins.next(2).method.self_type.name + assert_equal "Space.Single_Type", @ins.next(2).method.self_type.name end end end diff --git a/test/vool/test_class_method_expression.rb b/test/vool/test_class_method_expression.rb index 0fd09c35..7e08951a 100644 --- a/test/vool/test_class_method_expression.rb +++ b/test/vool/test_class_method_expression.rb @@ -5,7 +5,7 @@ module Vool include VoolCompile def class_code - "class Space;def self.meth;return 1 ; end;end" + "class Space;def self.meth; return meth(22 + 22) ; end;end" end def setup Parfait.boot!(Parfait.default_test_options) @@ -33,5 +33,16 @@ module Vool m = clazz.single_class.instance_type.get_method(:meth) assert m , "no type method :meth" end + def as_mom + @clazz.to_parfait + @clazz.to_mom(nil) + end + def test_mom + assert_equal :meth , as_mom.method_compilers.callable.name + end + def test_mom_frame + callable = as_mom.method_compilers.callable + assert callable.frame_type.names.last.to_s.start_with?("tmp_") , "no tmp_ variable #{callable.frame_type.names}" + end end end