package com.sap.runlet.interpreter;
import behavioral.actions.Block;
import com.sap.runlet.abstractinterpreter.StackFrame;
import com.sap.runlet.abstractinterpreter.objects.RunletObject;
import com.sap.runlet.interpreter.signatureimplementations.BlockInterpreter;
import data.classes.AssociationEnd;
import data.classes.ClassTypeDefinition;
import data.classes.NamedValue;
import data.classes.SignatureImplementation;
import data.classes.TypeDefinition;
/**
* Holds one frame of a call stack of an execution thread corresponding to a {@link Block}
* execution. This stack contains all variables that are declared in the block and are
* already in scope (variables are in scope only after their declaration in the block).<p>
*
* Stack frames that represent executions of nested blocks (such as that of a loop
* or an if/else statement) know their parent frame which is then also used to
* resolve variables that are not in scope for the current innermost block. The
* outermost block's frame that corresponds to a method or function invocation does
* not have a parent as no named value from outside such a scope is mapped into the
* method's or function's scope.<p>
*
* The {@link BlockInterpreter} works in lock-step with this class to announce
* block entries and exits.<p>
*
* TODO give stack frames names; creators then can specify them, e.g., when invoking something or creating a thread
*
* @author Axel Uhl (D043530)
*
*/
public class RunletStackFrame
extends StackFrame<AssociationEnd, TypeDefinition, ClassTypeDefinition, SignatureImplementation> {
/**
* Creates a new stack frame for a method or function invocation with no parent stack
* for name resolution.
*/
public RunletStackFrame() {
super();
}
/**
* Create a stack frame that has <tt>parent</tt> as its parent stack frame. This constructor
* is expected to be used when a nested block inside a method or function execution begins
* to execute.
*/
public RunletStackFrame(RunletStackFrame parent) {
super(parent);
}
/**
* Stores the variable into this particular frame. This will hide any definitions under the same
* key in any parent frame. If a definition by that key is already known by this very frame
* (not ascending to parent frames), an exception will be thrown.
*/
public void enterValue(NamedValue variable, RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> value) {
if (variable == null) {
throw new RuntimeException("Cannot create unnamed variable on StackFrame");
}
enterValue(variable.getName(), value);
}
/**
* Retrieves the value for <tt>variable</tt> from this stack frame. If the variable
* is not in scope in the current frame, the parent frame stack is checked one after the
* other until a value is found or the end of the stack has been reached. In that case,
* a {@link RuntimeException} is thrown because the value is undefined.
*/
public RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> getValue(NamedValue variable) {
RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> result;
if (variable == null) {
throw new RuntimeException("No value defined for <null> variable");
} else {
result = getValue(variable.getName());
}
return result;
}
/**
* Tries to locate the variable in this and the connected scope parent frames. If the entry is found,
* it is updated by the <tt>value</tt> provided. Otherwise, the variable is added to this frame and
* set to <tt>value</tt>.
*/
public void setValue(NamedValue variable, RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> value) {
if (variable == null) {
throw new RuntimeException("Cannot create unnamed variable on StackFrame");
}
setValue(variable.getName(), value);
}
@Override
public RunletStackFrame getScopeParent() {
return (RunletStackFrame) super.getScopeParent();
}
}