rubyx/lib/risc/boot.rb

193 lines
7.7 KiB
Ruby
Raw Normal View History

module Risc
2016-12-06 14:08:29 +01:00
# Booting is complicated, so it is extracted into this file, even it has only one entry point
# a ruby object as a placeholder for the parfait Space during boot
class BootSpace
attr_reader :classes
def initialize
@classes = {}
end
def get_class_by_name(name)
cl = @classes[name]
raise "No class for #{name}" unless cl
cl
end
end
2016-12-30 17:39:49 +01:00
# another ruby object to shadow the parfait, just during booting.
# all it needs is the type, which we make the Parfait type
class BootClass
attr_reader :instance_type
2016-12-30 17:39:49 +01:00
def initialize( type)
@instance_type = type
end
end
class Machine
# The general idea is that compiling is creating an object graph. Functionally
# one tends to think of methods, and that is complicated enough, sure.
2015-07-28 15:18:32 +02:00
# But for an object system the graph includes classes and all instance variables
#
2015-07-28 15:18:32 +02:00
# And so we have a chicken and egg problem. At the end of the boot function we want to have a
# working Space object
# But that has instance variables (List and Dictionary) and off course a class.
2017-08-29 17:38:51 +02:00
# Or more precisely in rubyx, a Type, that points to a class.
2016-02-25 21:03:11 +01:00
# So we need a Type, but that has Type and Class too. hmmm
#
# The way out is to build empty shell objects and stuff the neccessary data into them
# (not use the normal initialize way)
2015-07-28 15:18:32 +02:00
# (PPS: The "real" solution is to read a sof graph and not do this by hand
# That graph can be programatically built and written (with this to boot that process :-))
# There are some helpers below, but the roadmap is something like:
2016-12-06 10:38:09 +01:00
# - create all the Type instances, with their basic types, but no classes
2016-12-30 17:39:49 +01:00
# - create a BootSpace that has BootClasses , used only during booting
2016-12-06 10:38:09 +01:00
# - create the Class objects and assign them to the types
2016-12-30 17:39:49 +01:00
# - flesh out the types , create the real space
# - and finally load the methods
2015-07-28 15:18:32 +02:00
def boot_parfait!
2016-12-31 13:54:34 +01:00
Parfait.set_object_space( nil )
types = boot_types
boot_boot_space( types )
classes = boot_classes( types )
fix_types( types , classes )
space = Parfait::Space.new( classes )
Parfait.set_object_space( space )
2015-07-28 15:18:32 +02:00
2016-12-30 17:39:49 +01:00
#puts Sof.write(space)
boot_functions( space )
end
2015-07-28 15:18:32 +02:00
# types is where the snake bites its tail. Every chain ends at a type and then it
# goes around (circular references). We create them from the list below, just as empty
# shells, that we pass back, for the BootSpace to be created
def boot_types
types = {}
type_names.each do |name , ivars |
types[name] = Parfait::Type.allocate
end
type_type = types[:Type]
types.each do |name , type |
type.set_type(type_type)
end
types
end
# The BootSpace is an object that holds fake classes, that hold _real_ types
# Once we plug it in we can use .new
# then we need to create the parfait classes and fix the types before creating a Space
def boot_boot_space(types)
boot_space = BootSpace.new
types.each do |name , type|
clazz = BootClass.new(type)
boot_space.classes[name] = clazz
end
2016-12-30 17:39:49 +01:00
Parfait.set_object_space( boot_space )
2016-12-07 22:34:45 +01:00
end
# when running code instantiates a class, a type is created automatically
# but even to get our space up, we have already instantiated all types
2015-07-28 15:18:32 +02:00
# so we have to continue and allocate classes and fill the data by hand
# and off cource we can't use space.create_class , but still they need to go there
def boot_classes(types)
classes = Parfait::Dictionary.new
type_names.each do |name , vars|
super_c = super_class_names[name] || :Object
classes[name] = Parfait::Class.new(name , super_c , types[name] )
end
classes
end
2016-12-31 13:54:34 +01:00
# Types are hollow shells before this, so we need to set the object_class
# and initialize the list variables (which we now can with .new)
def fix_types(types , classes)
type_names.each do |name , ivars |
type = types[name]
clazz = classes[name]
type.set_object_class( clazz )
2018-03-25 12:27:15 +02:00
type.init_lists({type: :Type }.merge(ivars))
2016-12-31 13:54:34 +01:00
end
end
2016-12-30 17:39:49 +01:00
# superclasses other than default object
def super_class_names
{ Data4: :DataObject , Data8: :DataObject ,Data16: :DataObject ,
BinaryCode: :Data16 , Integer: :Data4, Word: :Data8 ,
Object: :BasicObject}
end
2015-07-28 15:18:32 +02:00
# the function really just returns a constant (just avoiding the constant)
# unfortuantely that constant condenses every detail about the system, class names
# and all instance variable names. Really have to find a better way
def type_names
2018-03-25 12:27:15 +02:00
{ Word: {char_length: :Integer} ,
List: {indexed_length: :Integer} ,
Message: { next_message: :Message, receiver: :Object, frame: :NamedList ,
return_address: :Integer, return_value: :Integer,
caller: :Message , name: :Word , arguments: :NamedList },
2018-03-31 19:21:27 +02:00
Integer: {next_integer: :Integer},
DataObject: {},
Data4: {},
Data8: {},
2018-03-25 12:27:15 +02:00
TrueClass: {},
FalseClass: {},
NilClass: {},
Object: {},
BinaryCode: {next: :BinaryCode} ,
2018-03-25 12:27:15 +02:00
Space: {classes: :Dictionary , types: :Dictionary ,
2018-03-31 19:21:27 +02:00
first_message: :Message , next_integer: :Integer ,
true_object: :TrueClass,
false_object: :FalseClass , nil_object: :NilClass},
2018-03-25 12:27:15 +02:00
NamedList: {},
Type: {names: :List , types: :List ,
object_class: :Class, methods: :TypedMethod } ,
2018-03-25 12:27:15 +02:00
Class: {instance_methods: :List, instance_type: :Type, name: :Word,
super_class_name: :Word , instance_names: :List },
Dictionary: {keys: :List , values: :List } ,
CacheEntry: {cached_type: :Type , cached_method: :TypedMethod } ,
TypedMethod: {name: :Word, source: :Object, risc_instructions: :Object,
cpu_instructions: :Object, binary: :BinaryCode,
arguments_type: :Type , for_type: :Type, frame_type: :Type ,
next_method: :TypedMethod} ,
}
end
# classes have booted, now create a minimal set of functions
# minimal means only that which can not be coded in ruby
# Methods are grabbed from respective modules by sending the method name. This should return the
# implementation of the method (ie a method object), not actually try to implement it
# (as that's impossible in ruby)
2016-12-30 17:39:49 +01:00
def boot_functions( space )
# very fiddly chicken 'n egg problem. Functions need to be in the right order, and in fact we
2016-12-06 10:38:09 +01:00
# have to define some dummies, just for the others to compile
# TODO go through the virtual parfait layer and adjust function names to what they really are
2017-01-15 13:44:11 +01:00
space_class = space.get_class
space_class.instance_type.add_method Builtin::Space.send(:main, nil)
2015-11-30 15:09:12 +01:00
obj = space.get_class_by_name(:Object)
[ :get_internal_word , :set_internal_word , :_method_missing,
:exit , :__init__].each do |f|
obj.instance_type.add_method Builtin::Object.send(f , nil)
end
obj = space.get_class_by_name(:Word)
[:putstring , :get_internal_byte , :set_internal_byte ].each do |f|
obj.instance_type.add_method Builtin::Word.send(f , nil)
2015-11-19 09:08:41 +01:00
end
obj = space.get_class_by_name(:Integer)
Risc.operators.each do |op|
obj.instance_type.add_method Builtin::Integer.operator_method(op)
end
2018-04-19 21:57:31 +02:00
[:putint, :div4, :div10 , :<, :>].each do |f| #div4 is just a forward declaration
obj.instance_type.add_method Builtin::Integer.send(f , nil)
end
end
end
end