module Boot # 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 Space 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 # another ruby object to shadow the parfait, just during booting. # all it needs is the type, which we make the Parfait type class Class attr_reader :instance_type def initialize( type) @instance_type = type end end end module Parfait # The general idea is that compiling is creating an object graph. Functionally # one tends to think of methods, and that is complicated enough, sure. # But for an object system the graph includes classes and all instance variables # # 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. # Or more precisely in rubyx, a Type, that points to a class. # 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) # (PPS: The "real" solution is to read a rx-file 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: # - create all the Type instances, with their basic types, but no classes # - create a BootSpace that has BootClasses , used only during booting # - create the Class objects and assign them to the types # - flesh out the types , create the real space # - and finally load the methods def self.boot! Parfait.set_object_space( nil ) types = boot_types boot_boot_space( types ) classes = boot_classes( types ) fix_types( types , classes ) space = Space.new( classes ) Parfait.set_object_space( space ) end # 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 self.boot_types types = {} type_names.each do |name , ivars | types[name] = 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 self.boot_boot_space(types) boot_space = Boot::Space.new types.each do |name , type| clazz = Boot::Class.new(type) boot_space.classes[name] = clazz end Parfait.set_object_space( boot_space ) 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 # 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 self.boot_classes(types) classes = Dictionary.new type_names.each do |name , vars| super_c = super_class_names[name] || :Object clazz = Class.new(name , super_c , types[name] ) classes[name] = clazz end classes end # 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 self.fix_types(types , classes) type_names.each do |name , ivars | type = types[name] clazz = classes[name] type.set_object_class( clazz ) type.init_lists({type: :Type }.merge(ivars)) end end # superclasses other than default object def self.super_class_names { Data4: :DataObject , Data8: :DataObject , Data16: :DataObject , Data32: :DataObject , BinaryCode: :Data16 , Integer: :Data4 , Word: :Data8 , List: :Data16 , CallableMethod: :Callable, Block: :Callable, ReturnAddress: :Integer} end # 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 self.type_names {BinaryCode: {next: :BinaryCode} , Block: {binary: :BinaryCode, next: :CallableMethod, arguments_type: :Type , self_type: :Type, frame_type: :Type } , CacheEntry: {cached_type: :Type , cached_method: :CallableMethod } , Callable: {binary: :BinaryCode,next: :Callable , arguments_type: :Type , self_type: :Type, frame_type: :Type } , CallableMethod: {name: :Word, binary: :BinaryCode, arguments_type: :Type , self_type: :Type, frame_type: :Type , next: :CallableMethod} , Class: {instance_methods: :List, instance_type: :Type, name: :Word, super_class_name: :Word }, DataObject: {}, Data4: {}, Data8: {}, Data16: {}, Dictionary: {keys: :List , values: :List } , Integer: {next_integer: :Integer}, FalseClass: {}, List: {indexed_length: :Integer , next_list: :List} , Message: { next_message: :Message, receiver: :Object, frame: :NamedList , return_address: :Integer, return_value: :Object, caller: :Message , method: :TypedMethod , arguments: :NamedList }, NamedList: {}, NilClass: {}, Object: {}, ReturnAddress: {next_integer: :ReturnAddress}, Space: {classes: :Dictionary , types: :Dictionary , next_message: :Message , messages: :Message , next_integer: :Integer, integers: :Integer , next_address: :ReturnAddress ,addresses: :ReturnAddress , true_object: :TrueClass, false_object: :FalseClass , nil_object: :NilClass}, TrueClass: {}, Type: {names: :List , types: :List , object_class: :Class, methods: :CallableMethod } , VoolMethod: { name: :Word , args_type: :Type , frame_type: :Type } , Word: {char_length: :Integer , next_word: :Word} , } end end