turned out to be a rewrite of boot process

and also attribute handling and fake_memory
luckily there are tests and all is green again
This commit is contained in:
Torsten Ruger
2015-07-21 15:40:25 +03:00
parent f8cb33ec5e
commit 2d0424a370
19 changed files with 258 additions and 221 deletions

View File

@ -16,80 +16,114 @@ module Virtual
#
# The way out is to build empty shell objects and stuff the neccessary data into them
# (not use the normal initialize way)
#
# There are some helpers below, but the roadmap is something like:
# - create all the layouts, with thier layouts, but no classes
# - create a space by "hand" , using allocate, not new
# - create the class objects and assign them to the layouts
def boot_space
space_dict = object_with_layout Parfait::Dictionary
space_dict.keys = object_with_layout Parfait::List
space_dict.values = object_with_layout Parfait::List
@space = object_with_layout Parfait::Space
@space.classes = space_dict
Parfait::Space.set_object_space @space
end
def boot_layouts
@layouts = {}
layout_names.each do |name , ivars |
@layouts[name] = layout_for( name , ivars)
end
layout_layout = @layouts[:Layout]
@layouts.each do |name , layout |
layout.set_layout(layout_layout)
end
end
def boot_classes
# when running code instantiates a class, a layout is created automatically
# but even to get our space up, we have already instantiated all layouts
# 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
classes = space.classes
layout_names.each do |name , vars|
cl = object_with_layout Parfait::Class
cl.object_layout = @layouts[name]
@layouts[name].object_class = cl
cl.instance_methods = object_with_layout Parfait::List
# puts "instance_methods is #{cl.instance_methods.class}"
cl.name = name
classes[name] = cl
end
object_class = classes[:Object]
# superclasses other than default object
supers = { :BinaryCode => :Word , :Layout => :List , :Class => :Module ,
:Object => :Kernel , :Kernel => :Value, :Integer => :Value }
layout_names.each do |classname , ivar|
next if classname == :Value # has no superclass
clazz = classes[classname]
super_name = supers[classname]
if super_name
clazz.set_super_class classes[super_name]
else
clazz.set_super_class object_class
end
end
end
def boot_parfait!
@space = Parfait::Space.new
# map from the vm - class_name to the Parfait class (which carries parfait name)
class_mappings = {} #will later become instance variable
values = [ :Value , :Integer , :Kernel , :Object]
value_classes = values.collect { |cl| @space.create_class(cl,nil) }
layouts = { :Word => [] ,
:List => [] ,
# Assumtion is that name is the last of message
:Message => [:next_message , :receiver , :frame , :return_address , :return_value,
:caller , :name ],
:MetaClass => [],
:BinaryCode => [],
:Space => [:classes , :first_message ],
:Frame => [:next_frame ],
:Layout => [:object_class] ,
:Class => [:object_layout ],
:Dictionary => [:keys , :values ] ,
:Method => [:name , :code ,:arg_names , :locals , :tmps ] ,
:Module => [:name , :instance_methods , :super_class , :meta_class ]
}
layouts.each do |name , layout|
class_mappings[name] = @space.create_class(name , nil)
end
value_classes[1].set_super_class( value_classes[0] ) # #set superclass (value) for integer
value_classes[2].set_super_class( value_classes[0] ) # and kernel (TODO is module)
value_classes[3].set_super_class( value_classes[2] ) # and object (TODO hacked to kernel)
class_mappings.each do |name , clazz| # and the rest
clazz.set_super_class(value_classes[3]) # superclasses are object
end
# next create layouts by adding instance variable names to the layouts
class_mappings.each do |name , clazz|
variables = layouts[name]
variables.each do |var_name|
clazz.object_layout.add_instance_variable var_name
end
end
# superclass and layout corrections
supers = { :BinaryCode => :Word , :Layout => :List , :Class => :Module }
supers.each do |classname , superclass_name|
clazz = class_mappings[classname]
super_class = class_mappings[superclass_name]
# set_super_class has no sideeffects, so setting twice ok
clazz.set_super_class super_class
# Add superclass layout too
super_class.object_layout.each do |var|
clazz.object_layout.add_instance_variable var
end
end
# now store the classes so we can hand them out later during object creation
# this can not be done earlier, as parfait objects are all the time created and would
# lookup half created class info
# but it must be done before going through the objects (next step)
@class_mappings = class_mappings
class_mappings[:Integer ] = value_classes[1] #need for further booting
class_mappings[:Kernel ] = value_classes[2] #need for further booting
class_mappings[:Object ] = value_classes[3] #need for further booting
boot_layouts
boot_space
boot_classes
@space.late_init
# add_object @space
class_mappings.values.each {|v| v.init_layout }
# now update the layout on all objects created so far,
# go through objects in space
@objects.each do | o |
o.init_layout
end
#puts Sof.write(@space)
boot_functions!
end
# helper to create a Layout, name is the parfait name, ie :Layout
def layout_for( name , ivars )
l = Parfait::Layout.allocate.fake_init
l.add_instance_variable :layout
ivars.each {|n| l.add_instance_variable n }
l
end
# create an object with layout (ie allocate it and assign layout)
# meaning the lauouts have to be booted, @layouts filled
# here we pass the actual (ruby) class
def object_with_layout(cl)
o = cl.allocate.fake_init
name = cl.name.split("::").last.to_sym
o.set_layout @layouts[name]
o
end
def layout_names
{ :Word => [] ,
:List => [] ,
# Assumtion is that name is the last of message
:Message => [:next_message , :receiver , :frame , :return_address , :return_value,
:caller , :name ],
:MetaClass => [],
:Integer => [],
:Object => [],
:Kernel => [], #fix, kernel is a class, but should be a module
:BinaryCode => [],
:Space => [:classes , :first_message ],
:Frame => [:next_frame ],
:Layout => [:object_class] ,
# TODO fix layouts for inherited classes. Currently only :Class and the
# instances are copied (shame on you)
:Class => [:object_layout , :name , :instance_methods , :super_class , :meta_class],
:Dictionary => [:keys , :values ] ,
:Method => [:name , :code ,:arg_names , :for_class, :locals , :tmps ] ,
:Module => [:name , :instance_methods , :super_class , :meta_class ]
}
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
@ -99,19 +133,19 @@ module Virtual
# very fiddly chicken 'n egg problem. Functions need to be in the right order, and in fact we
# have to define some dummies, just for the other to compile
# TODO go through the virtual parfait layer and adjust function names to what they really are
obj = @class_mappings[:Object ]
obj = @space.get_class_by_name(:Object)
[:main , :_get_instance_variable , :_set_instance_variable].each do |f|
obj.add_instance_method Register::Builtin::Object.send(f , nil)
end
obj = @class_mappings[:Kernel ]
obj = @space.get_class_by_name(:Kernel)
# create dummy main first, __init__ calls it
[:exit,:__send , :__init__ ].each do |f|
obj.add_instance_method Register::Builtin::Kernel.send(f , nil)
end
@class_mappings[:Word].add_instance_method Register::Builtin::Word.send(:putstring , nil)
@space.get_class_by_name(:Word).add_instance_method Register::Builtin::Word.send(:putstring , nil)
obj = @class_mappings[:Integer ]
obj = @space.get_class_by_name(:Integer)
[:putint,:fibo].each do |f|
obj.add_instance_method Register::Builtin::Integer.send(f , nil)
end

View File

@ -42,7 +42,7 @@ module Virtual
def initialize
@parser = Parser::Salama.new
@passes = [ FIRST_PASS ]
@objects = []
@objects = {}
@booted = false
end
attr_reader :passes , :space , :class_mappings , :init , :objects , :booted
@ -92,8 +92,8 @@ module Virtual
# Objects are data and get assembled after functions
def add_object o
return false if @objects.include?(o)
@objects.push o
return false if @objects[o.object_id]
@objects[o.object_id] = o
true
end

View File

@ -4,25 +4,6 @@
# To stay sane, we use the same classes that we use later, but "adapt" them to work in ruby
# This affects mainly memory layout
module FakeMem
def initialize
super()
@memory = [0,nil]
@position = nil
if Virtual.machine.class_mappings
init_layout
else
#puts "No init for #{self.class}:#{self.object_id}"
end
end
def init_layout
vm_name = self.class.name.split("::").last.to_sym
clazz = Virtual.machine.class_mappings[vm_name]
raise "Class not found #{vm_name}" unless clazz
raise "Layout not set #{vm_name}" unless clazz.object_layout
self.set_layout clazz.object_layout
end
end
module Virtual
def self.new_list array
list = Parfait::List.new
@ -39,12 +20,13 @@ class Symbol
include Positioned
include Padding
def init_layout; end
def has_layout?
true
end
def get_layout
Virtual.machine.class_mappings[:Word].object_layout
l = Virtual.machine.space.classes[:Word].object_layout
puts "LL #{l.class}"
l
end
def word_length
padded to_s.length
@ -82,10 +64,15 @@ module Parfait
# but we implement it with ruby array (0 based) and use 0 as type-word
# These are the same functions that Builtin implements at run-time
class Object
include FakeMem
include Padding
include Positioned
def fake_init
@memory = [0,nil]
@position = nil
self # for chaining
end
# these internal functions are _really_ internal
# they respresent the smallest code needed to build larger functionality
# but should _never_ be used outside parfait. in fact that should be impossible
@ -108,10 +95,6 @@ module Parfait
# 1 -based index
def internal_object_set(index , value)
raise "failed init for #{self.class}" unless @memory
#shaddowing layout so we can ignore memory in Sof
if(index == LAYOUT_INDEX)
@layout = value
end
@memory[index] = value
end
def internal_object_grow(length)
@ -174,4 +157,20 @@ module Parfait
string
end
end
## sof related stuff
class Object
# parfait versions are deliberately called different, so we "relay"
# have to put the "@" on the names for sof to take them off again
def instance_variables
get_instance_variables.to_a.collect{ |n| "@#{n}".to_sym }
end
# name comes in as a ruby @var name
def instance_variable_get name
var = get_instance_variable name.to_s[1 .. -1].to_sym
puts "getting #{name} #{var}"
var
end
end
end

View File

@ -11,6 +11,7 @@ module Virtual
return if object.nil?
return unless Virtual.machine.add_object object
#puts "adding #{object.class}"
return unless object.respond_to? :has_layout?
unless object.has_layout?
object.init_layout
end
@ -19,8 +20,8 @@ module Virtual
end
layout = object.get_layout
keep layout
#puts "Layout #{layout.get_object_class.name} #{Machine.instance.objects.include?(layout)}"
layout.each do |name|
#puts "Layout #{layout.object_class.name} #{Machine.instance.objects.include?(layout)}"
layout.object_instance_names.each do |name|
inst = object.instance_variable_get "@#{name}".to_sym
keep inst
end