2018-05-14 10:55:01 +02:00
|
|
|
module Parfait
|
|
|
|
|
2019-08-06 17:33:27 +02:00
|
|
|
# An Object is conceptually a hash like structure. It is dynamic and
|
2015-05-14 18:54:38 +02:00
|
|
|
# you want to store values by name (instance variable names).
|
|
|
|
#
|
2019-08-06 17:33:27 +02:00
|
|
|
# One could (like mri), store the names in each object, but that is wasteful in both
|
|
|
|
# time and space.
|
|
|
|
# Instead we store only the values, and access them by index (bit like c++).
|
2016-02-25 21:03:11 +01:00
|
|
|
# The Type allows the mapping of names to index.
|
2015-04-08 19:24:50 +02:00
|
|
|
|
2019-08-06 17:33:27 +02:00
|
|
|
# The Type of an object describes the memory layout of the object. In a c analogy,
|
|
|
|
# it is the information defined in a struct.
|
2016-02-25 21:03:11 +01:00
|
|
|
# The Type is a list of the names of instance variables, and their value types (int etc).
|
2015-05-11 17:55:49 +02:00
|
|
|
#
|
2016-02-25 21:03:11 +01:00
|
|
|
# Every object has a Type to describe it, so it's *first* instance variable is **always**
|
|
|
|
# "type". This means the name "type" is the first name in the list
|
|
|
|
# for every Type instance.
|
2015-05-17 13:40:02 +02:00
|
|
|
|
2016-02-25 21:03:11 +01:00
|
|
|
# But, as we want every Object to have a class, the Type carries that class.
|
2016-02-25 20:50:10 +01:00
|
|
|
# So the type of type has an entry "object_class"
|
2015-04-08 19:24:50 +02:00
|
|
|
|
2015-10-26 13:33:36 +01:00
|
|
|
# But Objects must also be able to carry methods themselves (ruby calls singleton_methods)
|
2016-02-25 21:03:11 +01:00
|
|
|
# and those too are stored in the Type (both type and class include behaviour)
|
2015-10-26 13:33:36 +01:00
|
|
|
|
2019-08-06 17:33:27 +02:00
|
|
|
# The 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.
|
2015-05-11 17:55:49 +02:00
|
|
|
# Together they turn the object into a hash like structure
|
2015-04-08 19:24:50 +02:00
|
|
|
|
2019-08-06 17:33:27 +02:00
|
|
|
# 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.
|
2016-12-06 14:08:29 +01:00
|
|
|
|
|
|
|
# The Type 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.
|
|
|
|
|
2016-02-25 20:50:10 +01:00
|
|
|
class Type < Object
|
2016-12-31 14:20:02 +01:00
|
|
|
|
2019-09-09 19:26:54 +02:00
|
|
|
attr_reader :type, :object_class , :names , :types , :methods
|
2018-08-11 18:15:34 +02:00
|
|
|
|
|
|
|
def self.type_length
|
|
|
|
5
|
|
|
|
end
|
2015-10-26 11:22:32 +01:00
|
|
|
|
2016-12-19 13:16:10 +01:00
|
|
|
def self.for_hash( object_class , hash)
|
2016-12-30 18:17:59 +01:00
|
|
|
hash = {type: object_class.name }.merge(hash) unless hash[:type]
|
2016-12-08 11:50:25 +01:00
|
|
|
new_type = Type.new( object_class , hash)
|
2016-12-30 19:47:28 +01:00
|
|
|
Parfait.object_space.add_type(new_type)
|
2016-12-08 11:50:25 +01:00
|
|
|
end
|
|
|
|
|
2016-12-30 17:41:36 +01:00
|
|
|
def initialize( object_class , hash )
|
2015-05-20 16:17:11 +02:00
|
|
|
super()
|
2016-12-29 17:42:38 +01:00
|
|
|
set_object_class( object_class)
|
|
|
|
init_lists( hash )
|
|
|
|
end
|
|
|
|
|
|
|
|
# this part of the init is seperate because at boot time we can not use normal new
|
|
|
|
# new is overloaded to grab the type from space, and before boot, that is not set up
|
|
|
|
def init_lists(hash)
|
2019-09-09 19:26:54 +02:00
|
|
|
@methods = nil
|
|
|
|
@names = List.new
|
|
|
|
@types = List.new
|
2016-12-30 17:41:36 +01:00
|
|
|
raise "No type Type in #{hash}" unless hash[:type]
|
|
|
|
private_add_instance_variable(:type , hash[:type]) #first
|
2019-09-09 19:26:54 +02:00
|
|
|
hash.keys.each do |name |
|
|
|
|
private_add_instance_variable(name , hash[name]) unless name == :type
|
2016-12-27 19:34:11 +01:00
|
|
|
end
|
2016-12-14 12:21:55 +01:00
|
|
|
end
|
|
|
|
|
2018-07-14 10:03:16 +02:00
|
|
|
def class_name
|
2019-09-09 19:26:54 +02:00
|
|
|
@object_class.name
|
2018-07-14 10:03:16 +02:00
|
|
|
end
|
|
|
|
|
2016-12-27 19:34:11 +01:00
|
|
|
def to_s
|
2018-07-27 11:16:06 +02:00
|
|
|
str = "#{class_name}-["
|
|
|
|
first = false
|
2018-08-11 18:15:34 +02:00
|
|
|
names.each do |name|
|
2018-07-27 11:16:06 +02:00
|
|
|
unless(first)
|
|
|
|
first = true
|
|
|
|
str += ":#{name}"
|
|
|
|
else
|
|
|
|
str += ", :#{name}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
str + "]"
|
2016-12-27 19:34:11 +01:00
|
|
|
end
|
|
|
|
|
2016-12-14 12:21:55 +01:00
|
|
|
def method_names
|
|
|
|
names = List.new
|
2019-09-09 19:26:54 +02:00
|
|
|
return names unless @methods
|
2018-08-11 18:15:34 +02:00
|
|
|
methods.each_method do |method|
|
2016-12-14 12:21:55 +01:00
|
|
|
names.push method.name
|
|
|
|
end
|
|
|
|
names
|
|
|
|
end
|
|
|
|
|
2018-03-18 17:39:27 +01:00
|
|
|
def create_method( method_name , arguments , frame)
|
2016-12-30 12:33:07 +01:00
|
|
|
raise "create_method #{method_name}.#{method_name.class}" unless method_name.is_a?(Symbol)
|
2016-12-14 12:21:55 +01:00
|
|
|
#puts "Self: #{self.class} clazz: #{clazz.name}"
|
2018-03-22 16:36:22 +01:00
|
|
|
raise "frame must be a type, not:#{frame}" unless frame.is_a?(Type)
|
2017-01-17 20:25:18 +01:00
|
|
|
found = get_method( method_name )
|
2018-03-22 16:36:22 +01:00
|
|
|
if found
|
2018-03-24 11:21:46 +01:00
|
|
|
#puts "redefining method #{method_name}" #TODO, this surely must get more complicated
|
2018-07-06 19:01:17 +02:00
|
|
|
raise "attempt to redifine method for different type " unless self == found.self_type
|
2018-03-22 16:36:22 +01:00
|
|
|
found.init(arguments , frame)
|
|
|
|
return found
|
|
|
|
else
|
2018-07-30 09:21:43 +02:00
|
|
|
add_method CallableMethod.new( method_name , self , arguments , frame )
|
2018-03-18 17:39:27 +01:00
|
|
|
end
|
2016-12-14 12:21:55 +01:00
|
|
|
end
|
|
|
|
|
2016-12-30 12:33:07 +01:00
|
|
|
def add_method( method )
|
2018-07-07 08:11:09 +02:00
|
|
|
raise "not a method #{method.class} #{method.inspect}" unless method.is_a? CallableMethod
|
2016-12-14 12:21:55 +01:00
|
|
|
raise "syserr #{method.name.class}" unless method.name.is_a? Symbol
|
2018-07-06 19:01:17 +02:00
|
|
|
if self.is_a?(Class) and (method.self_type != self)
|
2016-12-14 12:21:55 +01:00
|
|
|
raise "Adding to wrong class, should be #{method.for_class}"
|
|
|
|
end
|
2018-04-02 15:36:43 +02:00
|
|
|
if get_method( method.name )
|
|
|
|
remove_method(method.name)
|
2016-12-14 12:21:55 +01:00
|
|
|
end
|
2018-08-11 18:15:34 +02:00
|
|
|
method.set_next( methods )
|
2019-09-09 19:26:54 +02:00
|
|
|
@methods = method
|
2016-12-14 12:21:55 +01:00
|
|
|
#puts "#{self.name} add #{method.name}"
|
|
|
|
method
|
|
|
|
end
|
|
|
|
|
2016-12-30 12:33:07 +01:00
|
|
|
def remove_method( method_name )
|
2018-08-11 18:15:34 +02:00
|
|
|
raise "No such method #{method_name} in #{self.name}" unless methods
|
|
|
|
if( methods.name == method_name)
|
2019-09-09 19:26:54 +02:00
|
|
|
@methods = methods.next_callable
|
2018-04-02 15:36:43 +02:00
|
|
|
return true
|
|
|
|
end
|
2018-08-11 18:15:34 +02:00
|
|
|
method = methods
|
2018-08-12 12:10:44 +02:00
|
|
|
while(method && method.next_callable)
|
|
|
|
if( method.next_callable.name == method_name)
|
|
|
|
method.set_next( method.next_callable.next_callable )
|
2018-04-02 15:36:43 +02:00
|
|
|
return true
|
|
|
|
else
|
2018-08-12 12:10:44 +02:00
|
|
|
method = method.next_callable
|
2018-04-02 15:36:43 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
raise "No such method #{method_name} in #{self.name}"
|
2016-12-14 12:21:55 +01:00
|
|
|
end
|
|
|
|
|
2016-12-30 12:33:07 +01:00
|
|
|
def get_method( fname )
|
|
|
|
raise "get_method #{fname}.#{fname.class}" unless fname.is_a?(Symbol)
|
2018-08-11 18:15:34 +02:00
|
|
|
return nil unless methods
|
|
|
|
methods.each_method do |m|
|
2016-12-14 12:21:55 +01:00
|
|
|
return m if(m.name == fname )
|
|
|
|
end
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2017-04-25 08:06:49 +02:00
|
|
|
# resolve according to normal oo logic, ie look up in superclass if not present
|
|
|
|
# NOTE: this will probably not work in future as the code for the superclass
|
2019-02-17 17:16:19 +01:00
|
|
|
# method, being bound to a different type, will assume that types (not the run-time
|
2017-04-25 08:06:49 +02:00
|
|
|
# actual types) layout. Either need to enforce some c++ style upwards compatibility (buuh)
|
|
|
|
# or copy the methods and recompile them for the actual type. (maybe still later dynamically)
|
|
|
|
# But for now we walk up, as it should really just be to object
|
|
|
|
def resolve_method( fname )
|
|
|
|
method = get_method(fname)
|
|
|
|
return method if method
|
|
|
|
sup = object_class.super_class
|
|
|
|
return nil unless sup
|
2018-07-07 08:11:09 +02:00
|
|
|
return nil if object_class.name == :Object
|
2017-04-25 08:06:49 +02:00
|
|
|
sup.instance_type.resolve_method(fname)
|
|
|
|
end
|
|
|
|
|
2018-04-02 15:36:43 +02:00
|
|
|
def methods_length
|
2018-08-11 18:15:34 +02:00
|
|
|
return 0 unless methods
|
2018-04-02 15:36:43 +02:00
|
|
|
len = 0
|
2018-08-11 18:15:34 +02:00
|
|
|
methods.each_method { len += 1}
|
2018-04-02 15:36:43 +02:00
|
|
|
return len
|
|
|
|
end
|
|
|
|
|
2015-06-03 09:01:59 +02:00
|
|
|
def == other
|
|
|
|
self.object_id == other.object_id
|
|
|
|
end
|
|
|
|
|
2015-05-19 19:29:33 +02:00
|
|
|
# add the name of an instance variable
|
2016-12-08 11:50:25 +01:00
|
|
|
# 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 )
|
2016-12-13 17:46:03 +01:00
|
|
|
raise "No nil name" unless name
|
|
|
|
raise "No nil type" unless type
|
2016-12-08 11:50:25 +01:00
|
|
|
hash = to_hash
|
|
|
|
hash[name] = type
|
2018-08-11 18:15:34 +02:00
|
|
|
return Type.for_hash( object_class , hash)
|
2015-05-16 13:01:48 +02:00
|
|
|
end
|
|
|
|
|
2016-12-29 17:42:38 +01:00
|
|
|
def set_object_class(oc)
|
|
|
|
raise "object class should be a class, not #{oc.class}" unless oc.is_a?(Class)
|
2019-09-09 19:26:54 +02:00
|
|
|
@object_class = oc
|
2015-07-20 12:20:43 +02:00
|
|
|
end
|
2015-07-21 14:40:25 +02:00
|
|
|
|
2015-10-26 11:57:54 +01:00
|
|
|
def instance_length
|
2018-08-11 18:15:34 +02:00
|
|
|
names.get_length()
|
2015-10-22 10:02:46 +02:00
|
|
|
end
|
|
|
|
|
2015-11-18 14:36:43 +01:00
|
|
|
# index of the variable when using get_internal_word
|
2018-05-14 10:55:01 +02:00
|
|
|
# (get_internal_word is 0 based and 0 is always the type)
|
2016-12-13 17:46:03 +01:00
|
|
|
def variable_index( name )
|
2018-08-11 18:15:34 +02:00
|
|
|
has = names.index_of(name)
|
2015-10-17 09:03:39 +02:00
|
|
|
return nil unless has
|
2018-05-14 10:55:01 +02:00
|
|
|
raise "internal error #{name}:#{has}" if has < 0
|
2016-12-29 17:42:38 +01:00
|
|
|
has
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_length()
|
2018-08-11 18:15:34 +02:00
|
|
|
names.get_length()
|
2016-12-29 17:42:38 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def name_at( index )
|
2018-08-11 18:15:34 +02:00
|
|
|
raise "No names #{index}" unless names
|
|
|
|
names.get(index)
|
2015-07-21 14:40:25 +02:00
|
|
|
end
|
|
|
|
|
2016-12-13 17:46:03 +01:00
|
|
|
def type_at( index )
|
2018-08-11 18:15:34 +02:00
|
|
|
types.get(index)
|
2015-11-09 22:28:10 +01:00
|
|
|
end
|
|
|
|
|
2018-07-15 11:30:50 +02:00
|
|
|
def type_for( name )
|
|
|
|
index = variable_index(name)
|
|
|
|
return nil unless index
|
|
|
|
type_at(index)
|
|
|
|
end
|
|
|
|
|
2015-10-13 13:46:07 +02:00
|
|
|
def inspect
|
2016-12-29 17:42:38 +01:00
|
|
|
"Type[#{names.inspect}]"
|
2015-10-13 13:46:07 +02:00
|
|
|
end
|
|
|
|
|
2018-05-14 10:55:01 +02:00
|
|
|
def rxf_reference_name
|
2018-08-11 18:15:34 +02:00
|
|
|
"#{object_class.name}_Type"
|
2015-06-19 18:50:53 +02:00
|
|
|
end
|
2018-05-14 10:55:01 +02:00
|
|
|
alias :name :rxf_reference_name
|
2015-10-26 16:24:28 +01:00
|
|
|
|
2016-12-31 13:51:06 +01:00
|
|
|
def each
|
2018-05-14 10:55:01 +02:00
|
|
|
index = 0
|
|
|
|
while( index < get_length() )
|
2016-12-31 13:51:06 +01:00
|
|
|
yield( name_at(index) , type_at(index) )
|
|
|
|
index += 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-04-02 15:36:43 +02:00
|
|
|
def each_method(&block)
|
2018-08-11 18:15:34 +02:00
|
|
|
return unless methods
|
|
|
|
methods.each_method(&block)
|
2018-04-02 15:36:43 +02:00
|
|
|
end
|
2016-12-08 11:50:25 +01:00
|
|
|
def to_hash
|
2016-12-31 13:51:06 +01:00
|
|
|
hash = {}
|
|
|
|
each do |name , type|
|
2018-05-14 10:55:01 +02:00
|
|
|
raise "Name nil #{type}" unless name
|
|
|
|
raise "Type nil #{name}" unless type
|
2016-12-31 13:51:06 +01:00
|
|
|
hash[name] = type
|
2016-12-08 11:50:25 +01:00
|
|
|
end
|
|
|
|
hash
|
|
|
|
end
|
|
|
|
|
2016-12-07 22:35:51 +01:00
|
|
|
def hash
|
2016-12-31 13:51:06 +01:00
|
|
|
index = 1
|
2018-08-11 18:15:34 +02:00
|
|
|
hash_code = Type.str_hash( object_class.name )
|
2016-12-31 13:51:06 +01:00
|
|
|
each do |name , type|
|
|
|
|
item_hash = Type.str_hash(name) + Type.str_hash(type)
|
|
|
|
hash_code += item_hash + (item_hash / 256 ) * index
|
|
|
|
index += 1
|
|
|
|
end
|
2016-12-31 18:53:43 +01:00
|
|
|
hash_code % (2 ** 62)
|
2016-12-31 13:51:06 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.str_hash(str)
|
|
|
|
if RUBY_ENGINE == 'opal'
|
|
|
|
hash = 5381
|
|
|
|
str.to_s.each_char do |c|
|
|
|
|
hash = ((hash << 5) + hash) + c.to_i; # hash * 33 + c without getting bignums
|
|
|
|
end
|
|
|
|
hash % (2 ** 51)
|
|
|
|
else
|
|
|
|
str.hash
|
|
|
|
end
|
2016-12-08 11:50:25 +01:00
|
|
|
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
|
2018-08-11 18:15:34 +02:00
|
|
|
names.push(name)
|
|
|
|
types.push(type)
|
2016-12-07 22:35:51 +01:00
|
|
|
end
|
2016-12-08 11:50:25 +01:00
|
|
|
|
2015-05-11 17:55:49 +02:00
|
|
|
end
|
2015-04-08 19:24:50 +02:00
|
|
|
end
|