package gov.nasa.jpf.vm.va;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
import cmu.conditional.ChoiceFactory;
import cmu.conditional.Conditional;
import cmu.conditional.One;
import de.fosd.typechef.featureexpr.FeatureExpr;
import de.fosd.typechef.featureexpr.FeatureExprFactory;
import gov.nasa.jpf.vm.MJIEnv;
import gov.nasa.jpf.vm.Types;
/**
* Stack implementation where locals are separated from stack.<br>
* Locals: Conditional[]<br>
* Stack: Conditional -Stack-
*
* @author Jens Meinicke
*
*/
public class StackHandler implements Cloneable, IStackHandler {
/** Locals are directly accessed with index **/
protected Conditional<Entry>[] locals;
protected Conditional<Stack> stack;
protected int length = 0;
public FeatureExpr stackCTX;
@Override
public int getStackWidth() {
return stack.toList().size();
}
@Override
public String toString() {
StringBuilder string = new StringBuilder();
string.append("Locals: [");
for (int i = 0; i < locals.length; i++) {
if (locals[i] == null) {
string.append("null");
} else {
string.append(locals[i]);
}
string.append(" ");
}
string.append("] \nStack: ");
string.append(stack);
return string.toString();
}
protected static final One<Entry> nullValue = new One<>(new Entry(MJIEnv.NULL, false));
@SuppressWarnings("unchecked")
public StackHandler(FeatureExpr ctx, int nLocals, int nOperands) {
if (ctx == null) {
// if loading class inside jetty, the ctx is null
System.err.println("CAUTIOUS! CTX == NULL, creating True");
ctx = FeatureExprFactory.True();
// throw new RuntimeException("CTX == NULL");
}
length = nLocals + nOperands;
locals = new Conditional[nLocals];
Arrays.fill(locals, nullValue);
stack = new One<>(new Stack(nOperands));
stackCTX = ctx;
}
@SuppressWarnings("unchecked")
public StackHandler() {
stack = new One<>(new Stack(0));
locals = new Conditional[0];
stackCTX = FeatureExprFactory.True();
}
@SuppressWarnings("unchecked")
public StackHandler(FeatureExpr ctx, Stack stack, Entry[] locals) {
stackCTX = ctx;
this.stack = new One<>(stack);
this.locals = new Conditional[locals.length];
for (int i = 0; i < locals.length; i++) {
this.locals[i] = new One<>(locals[i]);
}
length = stack.slots.length + locals.length;
}
@Override
public FeatureExpr getCtx() {
return stackCTX;
}
@Override
public void setCtx(FeatureExpr ctx) {
stackCTX = ctx;
}
@Override
@SuppressWarnings("unchecked")
public StackHandler clone() {
StackHandler clone = new StackHandler();
// clone.setCtx(stackCTX); // TODO ThreadStopTest.testStopRunning() fails
clone.length = length;
clone.locals = new Conditional[locals.length];
for (int i = 0; i < locals.length; i++) {
Conditional<Entry> local = locals[i];
if (local != null) {
clone.locals[i] = local.map(entry -> entry.copy());
}
}
clone.stack = stack.map(stack -> stack.copy());
return clone;
}
/*
* ############################################################
* Handling of local variables
* ############################################################
*/
@Override
public void pushLocal(final FeatureExpr ctx, final int index) {
Conditional<Entry> value = locals[index];
if (value == null) {
value = new One<>(new Entry(MJIEnv.NULL, false));
}
value.mapf(ctx, (FeatureExpr ctx1, Entry entry) -> push(ctx1, entry.value, entry.isRef));
}
@Override
public void pushLongLocal(FeatureExpr ctx, int index) {
Conditional<Entry> value = locals[index];
if (value == null) {
value = new One<>(new Entry(0, false));
}
value.mapf(ctx, (FeatureExpr ctx1, Entry entry) -> push(ctx1, entry.value, false));
value = locals[index + 1];
if (value == null) {
value = new One<>(new Entry(0, false));
}
value.mapf(ctx, (FeatureExpr ctx1, Entry entry) -> push(ctx1, entry.value, false));
}
@Override
public void storeOperand(final FeatureExpr ctx, final int index) {
if (Conditional.isTautology(ctx)) {
locals[index] = popEntry(ctx, true);
} else {
if (locals[index] == null) {
locals[index] = new One<>(new Entry(MJIEnv.NULL, false));
}
locals[index] = ChoiceFactory.create(ctx, popEntry(ctx, true), locals[index]).simplify();
}
}
private Conditional<Entry> popEntry(FeatureExpr ctx, final boolean copyRef) {
Conditional<Entry> result = stack.simplify(ctx).mapf(ctx, (FeatureExpr f, Stack s) -> {
Stack clone = s.copy();
boolean ref = copyRef ? clone.isRef(0) : false;
int res = clone.pop();
if (stackCTX.equivalentTo(f)) {
stack = new One<>(clone);
} else {
stack = ChoiceFactory.create(f, new One<>(clone), stack);
}
return new One<>(new Entry(res, ref));
}).simplify();
stack = stack.simplify();
return result;
}
@Override
public void storeLongOperand(final FeatureExpr ctx, final int index) {
stack.mapf(ctx, (FeatureExpr f, Stack stack) -> {
if (Conditional.isContradiction(f)) {
return;
}
locals[index + 1] = ChoiceFactory.create(f, popEntry(f, false), locals[index + 1]);
locals[index] = ChoiceFactory.create(f, popEntry(f, false), locals[index]);
});
locals[index] = locals[index].simplify();
locals[index + 1] = locals[index + 1].simplify();
stack = stack.simplify();
}
@Override
public void setLocal(FeatureExpr ctx, final int index, final Conditional<Integer> value, final boolean isRef) {
value.mapf(ctx, (FeatureExpr x, Integer value1) -> setLocal(x, index, value1, isRef));
}
@Override
public void setLocal(final FeatureExpr ctx, final int index, final int value, final boolean isRef) {
if (Conditional.isTautology(ctx)) {
locals[index] = new One<>(new Entry(value, isRef));
} else {
if (locals[index] == null) {
locals[index] = new One<>(new Entry(0, false));
}
locals[index] = ChoiceFactory.create(ctx, new One<>(new Entry(value, isRef)), locals[index]).simplify();
}
}
@Override
public Conditional<Integer> getLocal(FeatureExpr ctx, final int index) {
if (index < 0) {
return new One<>(-1);
}
if (index < locals.length) {
if (locals[index] == null) {
return One.MJIEnvNULL;
}
return locals[index].simplify(ctx).map(x -> {
if (x == null) {
return MJIEnv.NULL;
}
return x.value;
}).simplifyValues();
} else {
final int i = index - locals.length;
return stack.map(stack -> stack.get(i));
}
}
@Override
public Object getLocal(int index) {
return locals[index].simplify(getCtx());
}
// TODO change to conditional
@Override
public boolean isRefLocal(FeatureExpr ctx, final int index) {
if (index < 0) {
return false;
}
if (index < locals.length) {
if (locals[index] == null) {
return false;
}
// TODO check calls of isRefLocal
for (boolean b : locals[index].simplify(ctx).map(y -> y.isRef).toList()) {
if (b) {
return true;
}
}
return false;
// return locals[index].simplify(ctx).map(new IsRefLocal()).simplifyValues().getValue();
} else {
final int i = index - locals.length;
return stack.simplify(ctx).map(stack -> stack.isRefIndex(i)).simplifyValues().getValue();
}
}
/*
* #######################################################
* Handling of the stack
* ########################################################
*/
@Override
public <T> void push(final FeatureExpr ctx, final T value) {
push(ctx, value, false);
}
@Override
@SuppressWarnings("unchecked")
public void push(final FeatureExpr ctx, final Object value, final boolean isRef) {
if (value instanceof Conditional) {
((Conditional<Object>) value).mapf(ctx, (FeatureExpr ctx1, Object value1) -> push(ctx1, value1, isRef));
return;
}
stack = stack.mapf(ctx, (FeatureExpr f, Stack stack) -> {
if (Conditional.isContradiction(f)) {
return new One<>(stack);
}
final Stack clone = stack.copy();
if (value instanceof Integer) {
clone.push((Integer) value, isRef);
} else if (value instanceof Long) {
long v1 = ((Long) value).longValue();
clone.push((int) (v1 >> 32), isRef);
clone.push((int) v1, isRef);
} else if (value instanceof Double) {
long v2 = Double.doubleToLongBits((Double) value);
clone.push((int) (v2 >> 32), isRef);
clone.push((int) v2, isRef);
} else if (value instanceof Float) {
clone.push(Float.floatToIntBits((Float) value), isRef);
} else if (value instanceof Byte) {
clone.push(((Byte) value).intValue(), isRef);
} else if (value instanceof Short) {
clone.push((int) (Short) value, isRef);
} else if (value == null) {
clone.push(MJIEnv.NULL, isRef);
} else {
throw new RuntimeException(value + " of type " + value.getClass() + " cannot be pushed to the stack.");
}
if (stackCTX.equivalentTo(f)) {
return new One<>(clone);
}
return ChoiceFactory.create(ctx, new One<>(clone), new One<>(stack));
}).simplify();
}
@Override
public Conditional<Integer> pop(final FeatureExpr ctx) {
return pop(ctx, Type.INT);
}
@Override
public Conditional<Long> popLong(final FeatureExpr ctx) {
return pop(ctx, Type.LONG);
}
@Override
public Conditional<Double> popDouble(final FeatureExpr ctx) {
return pop(ctx, Type.DOUBLE);
}
@Override
public Conditional<Float> popFloat(final FeatureExpr ctx) {
return pop(ctx, Type.FLOAT);
}
@Override
public <T> Conditional<T> pop(final FeatureExpr ctx, final Type t) {
@SuppressWarnings("unchecked")
Conditional<T> result = stack.simplify(ctx).mapf(ctx, (FeatureExpr f, Stack s) -> {
Stack clone = s.copy();
Number res;
final int lo = clone.pop();
switch (t) {
case INT:
res = Integer.valueOf(lo);
break;
case DOUBLE:
res = Types.intsToDouble(lo, clone.pop());
break;
case FLOAT:
res = Types.intToFloat(lo);
break;
case LONG:
res = Types.intsToLong(lo, clone.pop());
break;
default:
return null;
}
if (stackCTX.equivalentTo(f)) {
stack = new One<>(clone);
} else {
stack = ChoiceFactory.create(f, new One<>(clone), stack);
}
return (Conditional<T>) new One<>(res);
}).simplifyValues();
stack = stack.simplify();
return result;
}
@Override
public void pop(FeatureExpr ctx, final int n) {
stack = stack.mapf(ctx, (FeatureExpr f, Stack s) -> {
if (Conditional.isContradiction(f)) {
return new One<>(s);
}
Stack clone = s.copy();
for (int i = n; i > 0; i--) {
clone.pop();
}
if (Conditional.isTautology(f)) {
return new One<>(clone);
}
return ChoiceFactory.create(f, new One<>(clone), new One<>(s));
}).simplify();
}
@Override
public Conditional<Integer> peek(FeatureExpr ctx) {
return peek(ctx, 0);
}
@Override
public Conditional<Integer> peek(FeatureExpr ctx, final int offset) {
return peek(ctx, offset, Type.INT);
}
@Override
public Conditional<Double> peekDouble(FeatureExpr ctx, final int offset) {
return peek(ctx, offset, Type.DOUBLE);
}
@Override
public Conditional<Long> peekLong(FeatureExpr ctx, final int offset) {
return peek(ctx, offset, Type.LONG);
}
@Override
public Conditional<Float> peekFloat(FeatureExpr ctx, final int offset) {
return peek(ctx, offset, Type.FLOAT);
}
protected <T> Conditional<T> peek(FeatureExpr ctx, final int offset, final Type t) {
return stack.simplify(ctx).map(new Function<Stack, T>() {
@SuppressWarnings("unchecked")
@Override
public T apply(final Stack stack) {
switch (t) {
case DOUBLE:
return (T) (Double) Types.intsToDouble(stack.peek(offset), stack.peek(offset + 1));
case FLOAT:
return (T) (Float) Types.intToFloat(stack.peek(offset));
case INT:
return (T) stack.peek(offset);
case LONG:
return (T) (Long) Types.intsToLong(stack.peek(offset), stack.peek(offset + 1));
default:
return null;
}
}
}).simplifyValues();
}
@Override
public boolean isRef(final FeatureExpr ctx, final int offset) {// change to Conditional<Boolean>
return stack.simplify(ctx).map(y -> y.isRef(offset)).simplifyValues().getValue();
}
@Override
public void set(final FeatureExpr ctx, final int offset, final int value, final boolean isRef) {
stack = stack.mapf(ctx, (FeatureExpr f, Stack stack) -> {
if (f.isContradiction()) {
return new One<>(stack);
}
Stack clone = stack.copy();
clone.set(offset, value, isRef);
if (f.isTautology()) {
return new One<>(clone);
}
return ChoiceFactory.create(ctx, new One<>(clone), new One<>(stack));
}).simplify();
}
@Override
public Conditional<Integer> getTop() {
return stack.map(y -> y.top);
}
@Override
public void setTop(final FeatureExpr ctx, final int i) {
stack = stack.mapf(ctx, (FeatureExpr f, Stack stack) -> {
if (Conditional.isContradiction(f)) {
return new One<>(stack);
}
Stack clone = stack.copy();
clone.top = i;
if (ctx.equals(f)) {
return new One<>(clone);
}
if (Conditional.isTautology(f)) {
return new One<>(clone);
}
return ChoiceFactory.create(ctx, new One<>(clone), new One<>(stack));
}).simplify();
}
@Override
public void clear(final FeatureExpr ctx) {
stack = stack.mapf(ctx, (FeatureExpr f, Stack stack) -> {
if (Conditional.isContradiction(f)) {
return new One<>(stack);
}
Stack clone = stack.copy();
clone.clear();
if (Conditional.isTautology(f)) {
return new One<>(clone);
}
return ChoiceFactory.create(ctx, new One<>(clone), new One<>(stack));
}).simplify();
}
@Override
public int[] getSlots() {
return getSlots(FeatureExprFactory.True());
}
@Override
public int[] getSlots(FeatureExpr ctx) {
int[] slots = new int[length];
int i = 0;
for (Conditional<Entry> l : locals) {
if (l == null) {
slots[i++] = MJIEnv.NULL;
continue;
}
slots[i++] = l.simplify(ctx).getValue(true).value;
}
for (int o : stack.simplify(ctx).getValue(true).getSlots()) {
slots[i++] = o;
}
return slots;
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (!(o instanceof StackHandler)) {
return false;
}
StackHandler other = (StackHandler) o;
for (int i = 0; i < locals.length; i++) {
Conditional<Entry> l1 = locals[i];
Conditional<Entry> l2 = other.locals[i];
if (l1 == l2) {
continue;
}
if (l1 == null) {
return false;
}
if (!l1.equals(l2)) {
return false;
}
}
if (!other.stack.equals(stack)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return 42;
}
@Override
public boolean hasAnyRef(FeatureExpr ctx) {
for (Conditional<Entry> local : locals) {
if (local == null) {
continue;
}
for (Entry entry : local.simplify(ctx).toList()) {
if (entry.isRef) {
return true;
}
}
}
for (Stack s : stack.simplify(ctx).toList()) {
if (s.hasAnyRef()) {
return true;
}
}
return false;
}
/*
* Stack Instructions
*/
@Override
public void dup_x1(final FeatureExpr ctx) {
function(ctx, StackInstruction.DUP_X1);
}
@Override
public void dup2_x2(final FeatureExpr ctx) {
function(ctx, StackInstruction.DUP2_X2);
}
@Override
public void dup2_x1(final FeatureExpr ctx) {
function(ctx, StackInstruction.DUP2_X1);
}
@Override
public void dup2(final FeatureExpr ctx) {
function(ctx, StackInstruction.DUP2);
}
@Override
public void dup(final FeatureExpr ctx) {
function(ctx, StackInstruction.DUP);
}
@Override
public void dup_x2(final FeatureExpr ctx) {
function(ctx, StackInstruction.DUP_X2);
}
@Override
public void swap(final FeatureExpr ctx) {
function(ctx, StackInstruction.SWAP);
}
void function(final FeatureExpr ctx, final StackInstruction instruction) {
stack = stack.mapf(ctx, (FeatureExpr f, Stack stack) -> {
if (Conditional.isContradiction(f)) {
return new One<>(stack);
}
Stack clone = stack.copy();
switch (instruction) {
case DUP_X1:
clone.dup_x1();
break;
case DUP2_X2:
clone.dup2_x2();
break;
case DUP:
clone.dup();
break;
case DUP2:
clone.dup2();
break;
case DUP2_X1:
clone.dup2_x1();
break;
case DUP_X2:
clone.dup_x2();
break;
case SWAP:
clone.swap();
break;
default:
throw new RuntimeException(instruction + "not supported");
}
if (Conditional.isTautology(f)) {
return new One<>(clone);
}
if (stackCTX.equivalentTo(f)) {
return new One<>(clone);
}
return ChoiceFactory.create(ctx, new One<>(clone), new One<>(stack));
}).simplify();
}
@Override
public int getLength() {
return length;
}
@Override
public Conditional<Stack> getStack() {
return stack;
}
@Override
public Set<Integer> getAllReferences() {
Set<Integer> references = new HashSet<>();
for (Conditional<Entry> cl : locals) {
for (Entry l: cl.toList()) {
if (l.isRef) {
references.add(l.value);
}
}
}
for (Stack s: stack.toList()) {
references.addAll(s.getReferences());
}
return references;
}
@Override
public int getLocalWidth() {
int width = -locals.length;
for (Conditional<Entry> local : locals) {
width += local.simplify(getCtx()).toMap().size();
}
return width;
}
@Override
public String getMaxLocal() {
StringBuilder builder = new StringBuilder();
for (Conditional<Entry> local : locals) {
int size = local.simplify(getCtx()).toMap().size();
builder.append(local.simplify(getCtx()));
builder.append(":");
builder.append(size);
builder.append('\n');
}
return builder.toString();
}
@Override
public void IINC(FeatureExpr ctx, int index, final int increment) {
locals[index] = locals[index].mapf(ctx, (FeatureExpr ctx1, Entry y) -> {
if (Conditional.isContradiction(ctx1)) {
return new One<>(y);
}
Entry copy = new Entry(y.value + increment, y.isRef);
if (Conditional.isTautology(ctx1)) {
return new One<>(copy);
}
return ChoiceFactory.create(ctx1, new One<>(copy), new One<>(y));
}).simplify();
}
}