diff --git a/lib/register/boot.rb b/lib/register/boot.rb index 8eb5ff4b..35d21dd6 100644 --- a/lib/register/boot.rb +++ b/lib/register/boot.rb @@ -93,8 +93,8 @@ module Register # helper to create a Type, name is the parfait name, ie :Type def type_for( name , ivars ) l = Parfait::Type.allocate.compile_time_init - l.add_instance_variable :type , name - ivars.each {|n,t| l.add_instance_variable( n , t) } + l.send(:private_add_instance_variable , :type , name) + ivars.each {|n,t| l.send(:private_add_instance_variable, n , t) } l end diff --git a/lib/soml/compiler/class_field.rb b/lib/soml/compiler/class_field.rb index 1b14e051..7e811c7f 100644 --- a/lib/soml/compiler/class_field.rb +++ b/lib/soml/compiler/class_field.rb @@ -10,7 +10,7 @@ module Soml index = for_class.instance_type.variable_index(statement.name) #raise "class field already defined:#{name} for class #{for_class.name}" if index #puts "Define field #{name} on class #{for_class.name}" - index = for_class.instance_type.add_instance_variable( statement.name , statement.type ) + for_class.instance_type.add_instance_variable( statement.name , statement.type ) return nil # statements don't reurn values, only expressions end diff --git a/lib/typed/parfait/type.rb b/lib/typed/parfait/type.rb index 35b3bd40..d9a7877c 100644 --- a/lib/typed/parfait/type.rb +++ b/lib/typed/parfait/type.rb @@ -40,10 +40,31 @@ module Parfait include Indexed self.offset(3) - def initialize( object_class ) + def self.new_for_hash( object_class , hash) + new_type = Type.new( object_class , hash) + code = new_type.hash + Space.object_space.types[code] = new_type + new_type + end + + def self.hash_code_for( dict ) + index = 1 + hash = 0 + dict.each do |name , type| + item_hash = name.hash + type.hash + hash += item_hash + (item_hash / 256 ) * index + index += 1 + end + hash + end + + def initialize( object_class , hash = nil) super() - add_instance_variable :type ,:Type + private_add_instance_variable :type ,:Type self.object_class = object_class + hash.each do |name , type| + private_add_instance_variable name , type + end if hash end def == other @@ -51,17 +72,18 @@ module Parfait end # add the name of an instance variable - # The index will be returned and can subsequently be searched with index_of - # The index of the name is the index of the data in the object - # - # TODO , later we would need to COPY the type to keep the old constant - # but now we are concerned with booting, ie getting a working structure - def add_instance_variable name , type - raise "Name shouldn't be nil" unless name - raise "Value Type shouldn't be nil" unless type - self.push(name) - self.push(type) - self.get_length + # Type objects are immutable, so a new object is returned + # As types are also unique, two same adds will result in identical results + def add_instance_variable( name , type ) + hash = to_hash + hash[name] = type + code = Type.hash_code_for( hash ) + existing = Space.object_space.types[code] + if existing + return existing + else + return Type.new_for_hash( object_class , hash) + end end def instance_names @@ -108,8 +130,26 @@ module Parfait nil # stop resolve recursing up metaclasses end - def hash - h = name.hash + def to_hash + hash = Dictionary.new + each_pair do |name, type | + hash[name] = type + end + hash end + + def hash + Type.hash_code_for( to_hash ) + end + + private + + def private_add_instance_variable( name , type) + raise "Name shouldn't be nil" unless name + raise "Value Type shouldn't be nil" unless type + self.push(name) + self.push(type) + end + end end diff --git a/test/typed/type/test_basic.rb b/test/typed/type/test_basic.rb index ccca952b..cf585007 100644 --- a/test/typed/type/test_basic.rb +++ b/test/typed/type/test_basic.rb @@ -70,7 +70,7 @@ class BasicType < MiniTest::Test def test_add_name type = Parfait::Type.new Register.machine.space.get_class_by_name(:Type) - type.add_instance_variable :boo , :Object + type.send(:private_add_instance_variable, :boo , :Object) assert_equal 2 , type.variable_index(:boo) assert_equal 4 , type.get_length assert_equal :type , type.get(1) diff --git a/test/typed/type/test_hash.rb b/test/typed/type/test_hash.rb new file mode 100644 index 00000000..8c86be6f --- /dev/null +++ b/test/typed/type/test_hash.rb @@ -0,0 +1,49 @@ +require_relative "../helper" + +class TypeHash < MiniTest::Test + + def setup + @types = Register.machine.boot.space.types + @first = @types.values.first + end + + def test_hash + assert_equal Parfait::Dictionary , @types.class + end + + def test_length + assert_equal 16 , @types.length + end + + def test_two_hashs_not_equal + assert @types.keys.last != @types.keys.first + end + + def test_name + assert_equal "Word_Type" , @types.values.first.name + end + + def test_to_hash + hash = @first.to_hash + assert_equal hash[:type] , :Word + assert_equal hash[:char_length] , :Integer + assert_equal 2 , @first.instance_length + end + + def test_hashcode_with_hash + assert_equal @first.hash , Parfait::Type.hash_code_for( @first.to_hash) + end + + def test_second_hash_different + hash2 = @first.to_hash + hash2[:random] = :Type + assert @first.hash != Parfait::Type.hash_code_for( hash2 ) + end + + def test_add_is_different + type = @first.add_instance_variable :random , :Integer + assert type != @first , "new: #{type.inspect} , old: #{@first.inspect}" + assert @first.hash != type.hash + end + +end