From 3db77076146440719056b2fac262d9529b7c875f Mon Sep 17 00:00:00 2001 From: Torsten Ruger Date: Sat, 16 Feb 2019 23:24:16 +0200 Subject: [PATCH] Get a basic MetaClass going Does get created in new, but not in boot. Also not yet used in compiling --- lib/parfait.rb | 1 + lib/parfait/behaviour.rb | 2 +- lib/parfait/class.rb | 6 +- lib/parfait/meta_class.rb | 107 ++++++++++++++++++++ lib/risc/parfait_boot.rb | 3 +- test/parfait/test_meta_class.rb | 50 +++++++++ test/parfait/test_space.rb | 2 +- test/risc/interpreter/calling/test_minus.rb | 2 +- test/risc/test_interpreter.rb | 4 +- test/risc/test_linker.rb | 2 +- test/vool/blocks/test_class_blocks.rb | 6 +- 11 files changed, 175 insertions(+), 10 deletions(-) create mode 100644 lib/parfait/meta_class.rb create mode 100644 test/parfait/test_meta_class.rb diff --git a/lib/parfait.rb b/lib/parfait.rb index c7abe770..35ab3f39 100644 --- a/lib/parfait.rb +++ b/lib/parfait.rb @@ -30,6 +30,7 @@ require_relative "parfait/data_object" require_relative "parfait/integer" require_relative "parfait/behaviour" require_relative "parfait/class" +require_relative "parfait/meta_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 4f979a0d..183b4f0e 100644 --- a/lib/parfait/behaviour.rb +++ b/lib/parfait/behaviour.rb @@ -45,7 +45,7 @@ module Parfait raise "resolve_method #{m_name}.#{m_name.class}" unless m_name.is_a?(Symbol) method = get_instance_method(m_name) return method if method - if( super_class_name != :Object ) + if( super_class_name && super_class_name != :Object ) method = self.super_class.resolve_method(m_name) end method diff --git a/lib/parfait/class.rb b/lib/parfait/class.rb index 8a76c8e1..45e8da89 100644 --- a/lib/parfait/class.rb +++ b/lib/parfait/class.rb @@ -18,10 +18,11 @@ module Parfait class Class < Object include Behaviour - attr :type, :instance_type , :name , :instance_methods , :super_class_name + attr :type, :instance_type , :name , :instance_methods + attr :super_class_name , :meta_class def self.type_length - 5 + 6 end def initialize( name , superclass , instance_type) @@ -30,6 +31,7 @@ module Parfait self.super_class_name = superclass self.instance_methods = List.new set_instance_type( instance_type ) + self.meta_class = MetaClass.new( self ) end def rxf_reference_name diff --git a/lib/parfait/meta_class.rb b/lib/parfait/meta_class.rb new file mode 100644 index 00000000..f2948855 --- /dev/null +++ b/lib/parfait/meta_class.rb @@ -0,0 +1,107 @@ +# +# 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. + +# 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 +# is the meta_class of. +# This is slightnly 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. + +# This is still a first version, this change is not implemeted, also classes at boot don't +# have metaclasses yet, so still a bit TODO + +# 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. + +module Parfait + class MetaClass < Object + include Behaviour + + attr :type, :instance_type , :instance_methods , :clazz + + def self.type_length + 4 + end + + def initialize( clazz ) + super() + self.clazz = clazz + self.instance_methods = List.new + set_instance_type( clazz.get_type() ) + end + + def rxf_reference_name + clazz.name + end + + def inspect + "MetaClass(#{clazz.name})" + end + + # no superclass, return nil to signal + def super_class_name + nil + end + + def add_method_for(name , type , frame , body ) + method = Parfait::VoolMethod.new(name , type , frame , body ) + add_method( method ) + method + end + + def add_method(method) + raise "Must be untyped method #{method}" unless method.is_a? Parfait::VoolMethod + instance_methods.push(method) + end + + def get_method(name) + instance_methods.find{|m| m.name == name } + end + + # adding an instance changes the instance_type to include that variable + def add_instance_variable( name , type) + self.instance_type = instance_type.add_instance_variable( name , type ) + end + + # setting the type generates all methods for this type + # (or will do, once we store the methods code to do that) + def set_instance_type( type ) + raise "type must be type #{type}" unless type.is_a?(Type) + self.instance_type = type + end + + # return the super class, but raise exception if either the super class name + # or the super classs is nil. + # Use only for non Object base class + def super_class! + raise "No super_class for class #{name}" unless super_class_name + s = super_class + raise "superclass not found for class #{name} (#{super_class_name})" unless s + s + end + + # return the super class + # we only store the name, and so have to resolve. + # Nil name means no superclass, and so nil is a valid return value + def super_class + return nil unless super_class_name + Parfait.object_space.get_class_by_name(super_class_name) + end + + # ruby 2.1 list (just for reference, keep at bottom) + #:allocate, :new, :superclass + + # + modules + # :<, :<=, :>, :>=, :included_modules, :include?, :name, :ancestors, :instance_methods, :public_instance_methods, + # :protected_instance_methods, :private_instance_methods, :constants, :const_get, :const_set, :const_defined?, + # :const_missing, :class_variables, :remove_class_variable, :class_variable_get, :class_variable_set, + # :class_variable_defined?, :public_constant, :private_constant, :singleton_class?, :include, :prepend, + # :module_exec, :class_exec, :module_eval, :class_eval, :method_defined?, :public_method_defined?, + # :private_method_defined?, :protected_method_defined?, :public_class_method, :private_class_method, :autoload, + # :autoload?, :instance_method, :public_instance_method + + end +end diff --git a/lib/risc/parfait_boot.rb b/lib/risc/parfait_boot.rb index 2511edaf..71e9d7d9 100644 --- a/lib/risc/parfait_boot.rb +++ b/lib/risc/parfait_boot.rb @@ -146,7 +146,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 }, + name: :Word, super_class_name: :Word , meta_class: :MetaClass}, DataObject: {}, Data4: {}, Data8: {}, @@ -158,6 +158,7 @@ module Parfait Message: { next_message: :Message, receiver: :Object, frame: :NamedList , return_address: :Integer, return_value: :Object, caller: :Message , method: :TypedMethod , arguments: :NamedList }, + MetaClass: {instance_methods: :List, instance_type: :Type, clazz: :Class }, NamedList: {}, NilClass: {}, Object: {}, diff --git a/test/parfait/test_meta_class.rb b/test/parfait/test_meta_class.rb new file mode 100644 index 00000000..3a284194 --- /dev/null +++ b/test/parfait/test_meta_class.rb @@ -0,0 +1,50 @@ +require_relative "helper" + +module Parfait + class TestMetaClass < ParfaitTest + + def setup + super + @try = @space.create_class( :Try , :Object).meta_class + end + + def test_type_forclass + assert_equal "Class(Space)" , @space.get_type.object_class.inspect + assert_equal :Space , @space.get_type.object_class.name + end + def test_new_superclass_name + assert_equal :Object , @try.clazz.super_class_name + end + def test_new_superclass + assert_equal "Class(Try)" , @try.clazz.inspect + assert_equal "MetaClass(Try)" , @try.inspect + end + def test_new_methods + assert_equal @try.method_names.class, @try.instance_methods.class + assert_equal @try.method_names.get_length , @try.instance_methods.get_length + end + def test_remove_nothere + assert !@try.remove_instance_method(:foo) + end + def test_resolve + assert_nil @try.resolve_method :foo + end + def test_remove_method + assert_equal false , @try.remove_instance_method( :foo) + end + def test_add_nil_method_raises + 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 + 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 + end + + end +end diff --git a/test/parfait/test_space.rb b/test/parfait/test_space.rb index 249194d3..375f5709 100644 --- a/test/parfait/test_space.rb +++ b/test/parfait/test_space.rb @@ -6,7 +6,7 @@ module Parfait def classes [:BinaryCode,:Block,:CacheEntry,:Callable,:CallableMethod,:Class, :DataObject,:Data4,:Data8,:Data16,:Dictionary,:Factory, :Integer,:FalseClass, - :List,:Message,:NamedList,:NilClass,:Object,:ReturnAddress, + :List,:Message, :MetaClass, :NamedList,:NilClass,:Object,:ReturnAddress, :Space,:TrueClass,:Type,:VoolMethod,:Word] end diff --git a/test/risc/interpreter/calling/test_minus.rb b/test/risc/interpreter/calling/test_minus.rb index eac087e5..d1f419ea 100644 --- a/test/risc/interpreter/calling/test_minus.rb +++ b/test/risc/interpreter/calling/test_minus.rb @@ -41,7 +41,7 @@ module Risc ret = main_ticks(68) assert_equal FunctionReturn , ret.class assert_equal :r1 , ret.register.symbol - assert_equal 22284 , @interpreter.get_register(ret.register) + assert_equal 24204 , @interpreter.get_register(ret.register) end end end diff --git a/test/risc/test_interpreter.rb b/test/risc/test_interpreter.rb index 89077d93..8e77f8c2 100644 --- a/test/risc/test_interpreter.rb +++ b/test/risc/test_interpreter.rb @@ -54,7 +54,7 @@ module Risc end def test_pc1 @interpreter.tick - assert_equal 21688 , @interpreter.pc + assert_equal 23672 , @interpreter.pc end def test_tick2 @interpreter.tick @@ -68,7 +68,7 @@ module Risc def test_pc2 @interpreter.tick @interpreter.tick - assert_equal 21692 , @interpreter.pc + assert_equal 23676 , @interpreter.pc end def test_tick_14_jump 14.times {@interpreter.tick} diff --git a/test/risc/test_linker.rb b/test/risc/test_linker.rb index 03917738..fb506201 100644 --- a/test/risc/test_linker.rb +++ b/test/risc/test_linker.rb @@ -25,7 +25,7 @@ module Risc assert_equal 0 , Position.get(@linker.cpu_init).at end def test_cpu_at - assert_equal "0x4f1c" , Position.get(@linker.cpu_init.first).to_s + assert_equal "0x569c" , 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/vool/blocks/test_class_blocks.rb b/test/vool/blocks/test_class_blocks.rb index b90e762b..3dd436e1 100644 --- a/test/vool/blocks/test_class_blocks.rb +++ b/test/vool/blocks/test_class_blocks.rb @@ -16,8 +16,12 @@ module VoolBlocks begin vool.to_mom(nil) rescue => err - assert err.message.include?("Blocks") + assert err.message.include?("Blocks") , err.message end end + def test_assign_compiles + vool = Ruby::RubyCompiler.compile( as_class_method("val = 0") ).to_vool + assert_equal Mom::MomCompiler , vool.to_mom(nil).class + end end end