GitLab will continue to be upgraded from 11.4.5-ce.0 on November 25th 2019 at 4.00pm (AEDT) to 5.00pm (AEDT) due to Critical Security Patch Availability. During the update, GitLab and Mattermost services will not be available.

Commit 91c4af20 authored by Stefan Marr's avatar Stefan Marr

Changing to new object model of core-lib

Introduction of AbstractObject without fields and classes.
Methods are not Arrays anymore to simplify and avoid use of 'object fields'.
Frames are not entirely implementation specific.

- clean out superficial comments
- added missing primitives
Signed-off-by: 's avatarStefan Marr <git@stefan-marr.de>
parent 9bd2c541
Subproject commit 555c692765b3853feb7fa3cdcd260440d9ec1422
Subproject commit b2d362a1f4ffdb6a2a6ad036eb8d582073ddad45
......@@ -67,7 +67,7 @@ class ClassGenerationContext(object):
result_class.set_instance_invokables(self._universe.new_array_from_list(self._class_methods))
result_class.set_name(self._universe.symbol_for(ccname))
super_mclass = super_class.get_class()
super_mclass = super_class.get_class(self._universe)
result_class.set_super_class(super_mclass)
# Allocate the resulting class
......@@ -86,6 +86,6 @@ class ClassGenerationContext(object):
system_class.set_instance_fields(self._universe.new_array_from_list(self._instance_fields))
# class-bound == class-instance-bound
super_mclass = system_class.get_class()
super_mclass = system_class.get_class(self._universe.nilObject)
super_mclass.set_instance_invokables(self._universe.new_array_from_list(self._class_methods))
super_mclass.set_instance_fields(self._universe.new_array_from_list(self._class_fields))
from som.vm.universe import error_print, error_println
from som.vm.universe import error_print, error_println, get_current
from som.interpreter.bytecodes import bytecode_as_str, bytecode_length, Bytecodes
def dump(clazz):
......@@ -61,7 +61,7 @@ def dump_method(m, indent):
constant = m.get_constant(b)
error_println("(index: " + str(m.get_bytecode(b + 1)) +
") value: (" +
str(constant.get_class().get_name()) +
str(constant.get_class(get_current()).get_name()) +
") " + str(constant))
elif bytecode == Bytecodes.push_global:
error_println("(index: " + str(m.get_bytecode(b + 1)) +
......
......@@ -29,21 +29,13 @@ class MethodGenerationContext(object):
return empty_primitive(self._signature.get_string(), universe)
def assemble(self, universe):
# create a method instance with the given number of bytecodes and literals
num_literals = len(self._literals)
num_locals = len(self._locals)
num_locals = len(self._locals)
meth = universe.new_method(self._signature,
len(self._bytecode),
num_literals,
self._literals,
universe.new_integer(num_locals),
universe.new_integer(self._compute_stack_depth()))
# copy literals into the method
i = 0
for l in self._literals:
meth.set_indexable_field(i, l)
i += 1
# copy bytecodes into method
i = 0
......
......@@ -89,7 +89,7 @@ class Parser(object):
if super_name.get_string() != "nil":
super_class = self._universe.load_class(super_name)
cgenc.set_instance_fields_of_super(super_class.get_instance_fields())
cgenc.set_class_fields_of_super(super_class.get_class().get_instance_fields())
cgenc.set_class_fields_of_super(super_class.get_class(self._universe).get_instance_fields())
else:
# WARNING:
# We hardcode here the field names for Class
......
from rpython.rlib import jit
from som.vmobjects.array import Array
# Frame layout:
#
......@@ -12,128 +11,105 @@ from som.vmobjects.array import Array
# | ... |
# +-----------------+
#
class Frame(Array):
# Static field indices and number of frame fields
NUMBER_OF_FRAME_FIELDS = Array.NUMBER_OF_OBJECT_FIELDS
_immutable_fields_ = ["_method", "_context"]
class Frame(object):
_immutable_fields_ = ["_method", "_context", "_stack"]
def __init__(self, nilObject, num_elements, method, context, previous_frame):
Array.__init__(self, nilObject, num_elements)
self._stack_pointer = 0
self._bytecode_index = 0
self._method = method
self._context = context
self._previous_frame = previous_frame if previous_frame else nilObject
self._stack = [nilObject] * num_elements
self._stack_pointer = 0
self._bytecode_index = 0
self._previous_frame = previous_frame
def get_previous_frame(self):
# Get the previous frame by reading the field with previous frame index
return self._previous_frame
def clear_previous_frame(self, nilObject):
# Set the previous frame to nil
self._previous_frame = nilObject
def clear_previous_frame(self):
self._previous_frame = None
def has_previous_frame(self, nilObject):
return self._previous_frame != nilObject
def has_previous_frame(self):
return self._previous_frame is not None
def is_bootstrap_frame(self, nilObject):
return not self.has_previous_frame(nilObject)
def is_bootstrap_frame(self):
return not self.has_previous_frame()
def get_context(self):
# Get the context by reading the field with context index
return self._context
def has_context(self, nilObject):
return self._context != nilObject
def has_context(self):
return self._context is not None
@jit.unroll_safe
def _get_context(self, level):
# Get the context frame at the given level
""" Get the context frame at the given level """
frame = self
# Iterate through the context chain until the given level is reached
for _ in range(level, 0, -1):
# Get the context of the current frame
frame = frame.get_context()
# Return the found context
return frame
@jit.unroll_safe
def get_outer_context(self, nilObject):
# Compute the outer context of this frame
def get_outer_context(self):
""" Compute the outer context of this frame """
frame = self
# Iterate through the context chain until null is reached
while frame.has_context(nilObject):
while frame.has_context():
frame = frame.get_context()
# Return the outer context
return frame
def get_method(self):
# Get the method by reading the field with method index
return self._method
def get_number_of_arguments(self):
return self.get_method().get_number_of_arguments()
def _get_default_number_of_fields(self):
# Return the default number of fields in a frame
return self.NUMBER_OF_FRAME_FIELDS
return self._method.get_number_of_arguments()
def pop(self):
# Pop an object from the expression stack and return it
stack_pointer = self.get_stack_pointer()
self.set_stack_pointer(stack_pointer - 1)
return self.get_indexable_field(stack_pointer)
""" Pop an object from the expression stack and return it """
stack_pointer = self._stack_pointer
self._stack_pointer = stack_pointer - 1
return self._stack[stack_pointer]
def push(self, value):
# Push an object onto the expression stack
stack_pointer = self.get_stack_pointer() + 1
self.set_indexable_field(stack_pointer, value)
self.set_stack_pointer(stack_pointer)
def get_stack_pointer(self):
# Get the current stack pointer for this frame
return jit.promote(self._stack_pointer)
def set_stack_pointer(self, value):
# Set the current stack pointer for this frame
self._stack_pointer = value
""" Push an object onto the expression stack """
stack_pointer = self._stack_pointer + 1
self._stack[stack_pointer] = value
self._stack_pointer = stack_pointer
def reset_stack_pointer(self):
""" Set the stack pointer to its initial value thereby clearing
the stack """
# arguments are stored in front of local variables
# Set the stack pointer to its initial value thereby clearing the stack
self.set_stack_pointer(self.get_number_of_arguments() +
self._stack_pointer = (self.get_number_of_arguments() +
self.get_method().get_number_of_locals().get_embedded_integer() - 1)
def get_bytecode_index(self):
# Get the current bytecode index for this frame
return self._bytecode_index
def set_bytecode_index(self, value):
# Set the current bytecode index for this frame
self._bytecode_index = value
def get_stack_element(self, index):
# Get the stack element with the given index
# (an index of zero yields the top element)
return self.get_indexable_field(self.get_stack_pointer() - index)
return self._stack[self._stack_pointer - index]
def set_stack_element(self, index, value):
# Set the stack element with the given index to the given value
# (an index of zero yields the top element)
self.set_indexable_field(self.get_stack_pointer() - index, value)
self._stack[self._stack_pointer - index] = value
def _get_local(self, index):
return self.get_indexable_field(self.get_number_of_arguments() + index)
return self._stack[self.get_number_of_arguments() + index]
def _set_local(self, index, value):
self.set_indexable_field(self.get_number_of_arguments() + index, value)
self._stack[self.get_number_of_arguments() + index] = value
def get_local(self, index, context_level):
# Get the local with the given index in the given context
......@@ -149,14 +125,14 @@ class Frame(Array):
context = self._get_context(context_level)
# Get the argument with the given index
return context.get_indexable_field(index)
return context._stack[index]
def set_argument(self, index, context_level, value):
# Get the context
context = self._get_context(context_level)
# Set the argument with the given index to the given value
context.set_indexable_field(index, value)
context._stack[index] = value
@jit.unroll_safe
def copy_arguments_from(self, frame):
......@@ -165,7 +141,7 @@ class Frame(Array):
# - copy them into the argument area of the current frame
num_args = self.get_method().get_number_of_arguments()
for i in range(0, num_args):
self.set_indexable_field(i, frame.get_stack_element(num_args - 1 - i))
self._stack[i] = frame.get_stack_element(num_args - 1 - i)
def print_stack_trace(self, nilObject):
# Print a stack trace starting in this frame
......@@ -174,5 +150,5 @@ class Frame(Array):
std_println(" %d @ %s" % (self.get_bytecode_index(),
self.get_method().get_signature().get_string()))
if self.has_previous_frame(nilObject):
self.get_previous_frame().print_stack_trace(nilObject)
if self.has_previous_frame():
self.get_previous_frame().print_stack_trace()
from som.interpreter.bytecodes import bytecode_length, Bytecodes
from som.vmobjects.clazz import Class
from rpython.rlib import jit
......@@ -122,16 +123,16 @@ class Interpreter(object):
result = frame.pop()
# Compute the context for the non-local return
context = frame.get_outer_context(self._universe.nilObject)
context = frame.get_outer_context()
# Make sure the block context is still on the stack
if not context.has_previous_frame(self._universe.nilObject):
if not context.has_previous_frame():
# Try to recover by sending 'escapedBlock:' to the sending object
# this can get a bit nasty when using nested blocks. In this case
# the "sender" will be the surrounding block and not the object
# that actually sent the 'value' message.
block = frame.get_argument(0, 0)
sender = frame.get_previous_frame().get_outer_context(self._universe.nilObject).get_argument(0, 0)
sender = frame.get_previous_frame().get_outer_context().get_argument(0, 0)
# pop the frame of the currently executing block...
self._pop_frame()
......@@ -141,7 +142,7 @@ class Interpreter(object):
return
# Unwind the frames
while self.get_frame() != context:
while self.get_frame() is not context:
self._pop_frame()
# Pop the top frame and push the result
......@@ -158,7 +159,7 @@ class Interpreter(object):
receiver = frame.get_stack_element(num_args - 1)
# Send the message
self._send(signature, receiver.get_class(), bytecode_index)
self._send(signature, receiver.get_class(self._universe), bytecode_index)
def start(self):
......@@ -241,7 +242,7 @@ class Interpreter(object):
def get_self(self):
# Get the self object from the interpreter
return self.get_frame().get_outer_context(self._universe.nilObject).get_argument(0, 0)
return self.get_frame().get_outer_context().get_argument(0, 0)
def _send(self, selector, receiver_class, bytecode_index):
# First try the inline cache
......@@ -283,7 +284,7 @@ class Interpreter(object):
self.set_frame(self._frame.get_previous_frame())
# Destroy the previous pointer on the old top frame
result.clear_previous_frame(self._universe.nilObject)
result.clear_previous_frame()
# Return the popped frame
return result
......
......@@ -6,6 +6,26 @@ def _new(ivkbl, frame, interpreter):
rcvr = frame.pop()
frame.push(interpreter.get_universe().new_instance(rcvr))
def _name(ivkbl, frame, interpreter):
rcvr = frame.pop()
frame.push(rcvr.get_name())
def _super_class(ivkbl, frame, interpreter):
rcvr = frame.pop()
frame.push(rcvr.get_super_class())
def _methods(ivkbl, frame, interpreter):
rcvr = frame.pop()
frame.push(rcvr.get_instance_invokables())
def _fields(ivkbl, frame, interpreter):
rcvr = frame.pop()
frame.push(rcvr.get_instance_fields())
class ClassPrimitives(Primitives):
def install_primitives(self):
self._install_instance_primitive(Primitive("new", self._universe, _new))
\ No newline at end of file
self._install_instance_primitive(Primitive("new", self._universe, _new))
self._install_instance_primitive(Primitive("name", self._universe, _name))
self._install_instance_primitive(Primitive("superclass", self._universe, _super_class))
self._install_instance_primitive(Primitive("methods", self._universe, _methods))
self._install_instance_primitive(Primitive("fields", self._universe, _fields))
from som.primitives.primitives import Primitives
from som.vmobjects.primitive import Primitive
def _holder(ivkbl, frame, interpreter):
rcvr = frame.pop()
frame.push(rcvr.get_holder())
def _signature(ivkbl, frame, interpreter):
rcvr = frame.pop()
frame.push(rcvr.get_signature())
class MethodPrimitives(Primitives):
def install_primitives(self):
self._install_instance_primitive(Primitive("holder", self._universe, _holder))
self._install_instance_primitive(Primitive("signature", self._universe, _signature))
......@@ -2,6 +2,7 @@ from rpython.rlib.objectmodel import compute_identity_hash
from som.primitives.primitives import Primitives
from som.vmobjects.object import Object
from som.vmobjects.primitive import Primitive
from som.vmobjects.array import Array
......@@ -20,9 +21,12 @@ def _hashcode(ivkbl, frame, interpreter):
def _objectSize(ivkbl, frame, interpreter):
rcvr = frame.pop()
size = rcvr.get_number_of_fields()
if isinstance(rcvr, Array):
size += rcvr.get_number_of_indexable_fields()
size = 0
if isinstance(rcvr, Object):
size = rcvr.get_number_of_fields()
elif isinstance(rcvr, Array):
size = rcvr.get_number_of_indexable_fields()
frame.push(interpreter.get_universe().new_integer(size))
......@@ -30,7 +34,7 @@ def _perform(ivkbl, frame, interpreter):
selector = frame.pop()
rcvr = frame.get_stack_element(0)
invokable = rcvr.get_class().lookup_invokable(selector)
invokable = rcvr.get_class(interpreter.get_universe()).lookup_invokable(selector)
invokable.invoke(frame, interpreter)
def _performInSuperclass(ivkbl, frame, interpreter):
......@@ -49,7 +53,7 @@ def _performWithArguments(ivkbl, frame, interpreter):
for i in range(0, args.get_number_of_indexable_fields()):
frame.push(args.get_indexable_field(i))
invokable = rcvr.get_class().lookup_invokable(selector)
invokable = rcvr.get_class(interpreter.get_universe()).lookup_invokable(selector)
invokable.invoke(frame, interpreter)
def _instVarAt(ivkbl, frame, interpreter):
......@@ -71,7 +75,7 @@ def _halt(ivkbl, frame, interpreter):
def _class(ivkbl, frame, interpreter):
rcvr = frame.pop()
frame.push(rcvr.get_class())
frame.push(rcvr.get_class(interpreter.get_universe()))
......
from som.primitives.primitives import Primitives
from som.vmobjects.primitive import Primitive
def _holder(ivkbl, frame, interpreter):
rcvr = frame.pop()
frame.push(rcvr.get_holder())
def _signature(ivkbl, frame, interpreter):
rcvr = frame.pop()
frame.push(rcvr.get_signature())
class PrimitivePrimitives(Primitives):
def install_primitives(self):
self._install_instance_primitive(Primitive("holder", self._universe, _holder))
self._install_instance_primitive(Primitive("signature", self._universe, _signature))
......@@ -21,4 +21,4 @@ class Primitives(object):
def _install_class_primitive(self, primitive):
# Install the given primitive as an instance primitive in the class of
# the holder class
self._holder.get_class().add_instance_primitive(primitive)
self._holder.get_class(self._universe).add_instance_primitive(primitive)
......@@ -20,11 +20,12 @@ def _length(ivkbl, frame, interpreter):
def _equals(ivkbl, frame, interpreter):
op1 = frame.pop()
op2 = frame.pop() # rcvr
if op1.get_class() == interpreter.get_universe().stringClass:
universe = interpreter.get_universe()
if op1.get_class(universe) == universe.stringClass:
if op1.get_embedded_string() == op2.get_embedded_string():
frame.push(interpreter.get_universe().trueObject)
frame.push(universe.trueObject)
return
frame.push(interpreter.get_universe().falseObject)
frame.push(universe.falseObject)
def _substring(ivkbl, frame, interpreter):
end = frame.pop()
......
......@@ -20,7 +20,7 @@ class Shell(object):
std_println("SOM Shell. Type \"quit\" to exit.\n");
# Create a fake bootstrap frame
current_frame = self._interpreter.push_new_frame(self._bootstrap_method, self._universe.nilObject)
current_frame = self._interpreter.push_new_frame(self._bootstrap_method, None)
# Remember the first bytecode index, e.g. index of the halt instruction
bytecode_index = current_frame.get_bytecode_index()
......
This diff is collapsed.
class AbstractObject(object):
def send(self, selector_string, arguments, universe, interpreter):
# Turn the selector string into a selector
selector = universe.symbol_for(selector_string)
# Push the receiver onto the stack
interpreter.get_frame().push(self)
# Push the arguments onto the stack
for arg in arguments:
interpreter.get_frame().push(arg)
# Lookup the invokable
invokable = self.get_class(universe).lookup_invokable(selector)
# Invoke the invokable
invokable.invoke(interpreter.get_frame(), interpreter)
def send_does_not_understand(self, selector, universe, interpreter):
# Compute the number of arguments
number_of_arguments = selector.get_number_of_signature_arguments()
frame = interpreter.get_frame()
frame.print_stack_trace(universe.nilObject)
# Allocate an array with enough room to hold all arguments
arguments_array = universe.new_array_with_length(number_of_arguments)
# Remove all arguments and put them in the freshly allocated array
i = number_of_arguments - 1
while i >= 0:
arguments_array.set_indexable_field(i, frame.pop())
i -= 1
args = [selector, arguments_array]
self.send("doesNotUnderstand:arguments:", args, universe, interpreter)
def send_unknown_global(self, global_name, universe, interpreter):
arguments = [global_name]
self.send("unknownGlobal:", arguments, universe, interpreter)
def send_escaped_block(self, block, universe, interpreter):
arguments = [block]
self.send("escapedBlock:", arguments, universe, interpreter)
def get_class(self, universe):
raise NotImplementedError("Subclasses need to implement get_class(universe).")
def is_invokable(self):
return False
def __str__(self):
from som.vm.universe import get_current
return "a " + self.get_class(get_current()).get_name().get_string()
from som.vmobjects.object import Object
from som.vmobjects.abstract_object import AbstractObject
class Array(Object):
class Array(AbstractObject):
_immutable_fields_ = ["_indexable_fields"]
def __init__(self, nilObject, number_of_indexable_fields):
Object.__init__(self, nilObject)
AbstractObject.__init__(self)
# Private array of indexable fields
self._indexable_fields = [nilObject] * number_of_indexable_fields
......@@ -41,3 +41,6 @@ class Array(Object):
# Copy all indexable fields from this array to the destination array
for i in range(self.get_number_of_indexable_fields()):
destination.set_indexable_field(i, self.get_indexable_field(i))
def get_class(self, universe):
return universe.arrayClass
from som.vmobjects.object import Object
from som.vmobjects.abstract_object import AbstractObject
class BigInteger(Object):
class BigInteger(AbstractObject):
_immutable_fields_ = ["_embedded_biginteger"]
def __init__(self, nilObject, value):
Object.__init__(self, nilObject)
def __init__(self, value):
AbstractObject.__init__(self)
self._embedded_biginteger = value
def get_embedded_biginteger(self):
......@@ -14,3 +14,6 @@ class BigInteger(Object):
def get_embedded_value(self):
"""This Method is polymorphic with Integer"""
return self._embedded_biginteger
def get_class(self, universe):
return universe.bigintegerClass
from rpython.rlib import jit
from som.vmobjects.object import Object
from som.vmobjects.abstract_object import AbstractObject
from som.vmobjects.primitive import Primitive
class Block(Object):
NUMBER_OF_BLOCK_FIELDS = Object.NUMBER_OF_OBJECT_FIELDS
class Block(AbstractObject):
_immutable_fields_ = ["_method", "_context"]
def __init__(self, nilObject, method, context):
Object.__init__(self, nilObject)
self._number_of_arguments = 0
def __init__(self, method, context):
AbstractObject.__init__(self)
self._method = method
self._context = context
def get_method(self):
# Get the method of this block by reading the field with method index
return jit.promote(self._method)
def get_context(self):
# Get the context of this block by reading the field with context index
return self._context
def _get_default_number_of_fields(self):
# Return the default number of fields for a block
return self.NUMBER_OF_BLOCK_FIELDS
def get_class(self, universe):
return universe.blockClasses[self._method.get_number_of_arguments()]
class Evaluation(Primitive):
_immutable_fields_ = ['_number_of_arguments']
......
from som.vmobjects.object import Object
from som.primitives.primitives import Primitives
class Class(Object):
# Static field indices and number of class fields
SUPER_CLASS_INDEX = Object.NUMBER_OF_OBJECT_FIELDS
NAME_INDEX = 1 + SUPER_CLASS_INDEX
INSTANCE_FIELDS_INDEX = 1 + NAME_INDEX
INSTANCE_INVOKABLES_INDEX = 1 + INSTANCE_FIELDS_INDEX
NUMBER_OF_CLASS_FIELDS = 1 + INSTANCE_INVOKABLES_INDEX
_immutable_fields_ = ["_super_class"
"_name",
"_instance_fields"
"_instance_invokables"]
def __init__(self, universe, number_of_fields=-1):
Object.__init__(self, universe.nilObject, number_of_fields)
self._super_class = universe.nilObject
self._name = None
self._instance_fields = None
self._instance_invokables = None
self._invokables_table = {}
self._universe = universe
def get_super_class(self):
# Get the super class by reading the field with super class index
return self.get_field(self.SUPER_CLASS_INDEX)
return self._super_class
def set_super_class(self, value):
# Set the super class by writing to the field with super class index
self.set_field(self.SUPER_CLASS_INDEX, value)
self._super_class = value
def has_super_class(self):
# Check whether or not this class has a super class
return self.get_field(self.SUPER_CLASS_INDEX) != self._universe.nilObject
return self._super_class is not self._universe.nilObject
def get_name(self):
# Get the name of this class by reading the field with name index
return self.get_field(self.NAME_INDEX)
return self._name
def set_name(self, value):
# Set the name of this class by writing to the field with name index
self.set_field(self.NAME_INDEX, value)
self._name = value
def get_instance_fields(self):
# Get the instance fields by reading the field with the instance fields index
return self.get_field(self.INSTANCE_FIELDS_INDEX)
return self._instance_fields
def set_instance_fields(self, value):
# Set the instance fields by writing to the field with the instance fields index
self.set_field(self.INSTANCE_FIELDS_INDEX, value)
self._instance_fields = value
def get_instance_invokables(self):
# Get the instance invokables by reading the field with the instance
# invokables index
return self.get_field(self.INSTANCE_INVOKABLES_INDEX)
return self._instance_invokables
def set_instance_invokables(self, value):
# Set the instance invokables by writing to the field with the instance
# invokables index
self.set_field(self.INSTANCE_INVOKABLES_INDEX, value)
self._instance_invokables = value
# Make sure this class is the holder of all invokables in the array
for i in range(0, self.get_number_of_instance_invokables()):
......@@ -62,24 +50,18 @@ class Class(Object):
invokable.set_holder(self)