package org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.traverse;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.Frame;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.FunctionInstance;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.RVMCore;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.RascalPrimitive;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.Reference;
import org.rascalmpl.value.IBool;
import org.rascalmpl.value.IValue;
import org.rascalmpl.values.uptr.ITree;
public class TraversalState {
/*
* TraversalState contains the complete context for a traversal and provides the following:
* (1) flags that represent the state of the traversal:
* - matched: did a case match?
* - changed: has a replacement been made?
* - leaveVisit: leave the visit due to a return in the excuted case?
*
* (2) The specific traverseOnce function to be executed: 'traverse'
* (3) An interface with the compiled code that executes the cases of the visit:
* - phi: the compiled function
* - refMatched, refChanged, refLeaveVisit refBegin, refEnd: reference variables of the phi function
* to communicate the above fields (matched, changed, leaveVisit) to/from the phi function.
* - access functions (isChanged, setChanged, etc) give a cached view on these fields.
* - In order to reduce garbage, we allocate the call frame of the phi function once and reuse and update
* it on each call.
*/
private final RVMCore rvm; // The RVM we are using
private final DescendantDescriptor descriptor;
// Describes in wich subtree to descend or not
private final boolean isAllwaysTrue;
private boolean matched; // Some rule matched;
private boolean changed; // Original subject has been changed
private boolean leavingVisit; // Return executed in visit case code
private final Reference refMatched; // Reference variable to cummunicate 'matched' with phi
private final Reference refChanged; // Reference variable to cummunicate 'changed' with phi
private final Reference refLeaveVisit; // Reference variable to cummunicate 'leavingVisit' with phi
private final Reference refBegin; // Reference variable to cummunicate 'begin' with phi (not cached in field)
private final Reference refEnd; // Reference variable to cummunicate 'end' with phi (not cached in field)
private final Frame frame; // Reused call frame of phi function
private final int stackStartOfOtherLocals;
private final int stackEnd;
ITraverse traverse; // The specific traverseOnce function to be used
public TraversalState(RVMCore rvm, FunctionInstance phi, Reference refMatched, Reference refChanged, Reference refLeaveVisit, Reference refBegin, Reference refEnd, DescendantDescriptor descriptor) {
this.rvm = rvm;
this.refMatched = refMatched;
this.refChanged = refChanged;
this.refLeaveVisit = refLeaveVisit;
this.refBegin = refBegin;
this.refEnd = refEnd;
this.descriptor = descriptor;
this.isAllwaysTrue = descriptor.isAllwaysTrue();
matched = false;
changed = false;
leavingVisit = false;
setRefLeavingVisit(false);
frame = rvm.makeFrameForVisit(phi);
frame.stack[1] = refMatched;
frame.stack[2] = refChanged;
frame.stack[3] = refLeaveVisit;
frame.stack[4] = refBegin;
frame.stack[5] = refEnd;
stackStartOfOtherLocals = 6;
stackEnd = frame.stack.length;
}
public boolean hasMatched() {
return matched;
}
private boolean getRefMatched() {
return ((IBool)refMatched.getValue()).getValue();
}
private void setRefMatched(boolean matched) {
refMatched.setValue(RascalPrimitive.vf.bool(matched));
}
public void setMatched(boolean matched){
this.matched = matched;
}
public boolean hasChanged() {
return changed;
}
public void setChanged(boolean changed) {
this.changed = changed;
}
private boolean getRefChanged(){
return ((IBool)refChanged.getValue()).getValue();
}
private void setRefChanged(boolean changed) {
refChanged.setValue(RascalPrimitive.vf.bool(changed));
}
public void setMatchedAndChanged(boolean matched, boolean changed){
this.matched = matched;
this.changed = changed;
}
public boolean isLeavingVisit(){
return leavingVisit;
}
private boolean getRefLeavingVisit(){
return ((IBool)refLeaveVisit.getValue()).getValue();
}
private void setRefLeavingVisit(boolean leaving) {
refLeaveVisit.setValue(RascalPrimitive.vf.bool(leaving));
}
public int getBegin() {
return ((Integer)refBegin.getValue()).intValue();
}
public void setBegin(int begin) {
refBegin.setValue((Integer)begin);
}
public int getEnd() {
return ((Integer)refEnd.getValue()).intValue();
}
public void setEnd(int end) {
refEnd.setValue((Integer)end);
}
public boolean shouldDescentInAbstractValue(IValue subject){
return isAllwaysTrue || descriptor.shouldDescentInAbstractValue(subject).getValue();
}
public boolean shouldDescentInConcreteValue(ITree subject){
return descriptor.shouldDescentInConcreteValue(subject).getValue();
}
public boolean isConcreteMatch(){
return descriptor.isConcreteMatch();
}
/**
* @param subject argument for phi function
* @return result of phi(subject)
*/
public IValue execute(IValue subject){
setRefMatched(matched); // Copy current values to reference variables
setRefChanged(changed);
frame.stack[0] = subject; // Set subject argument in frame
// stack[1] ... stack[5] are the reference parameters
// Clear rest of stack to avoid unwanted initializations of other locals or temporaries
for(int i = stackStartOfOtherLocals; i < stackEnd; i++){
frame.stack[i] = null;
}
IValue res = rvm.executeRVMFunctionInVisit(frame);
matched = getRefMatched(); // Copy values of reference variables back to fields
changed = getRefChanged();
leavingVisit = getRefLeavingVisit();
return res;
}
}