package gov.nasa.jpf.vm.va; import java.util.Arrays; import java.util.LinkedList; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BiFunction; 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; /** * Alternative stack implementations based on {@link StackHandler}.<br> * Values are cached until it is necessary to push them into a variability-aware {@link Stack}. * * @author Jens Meinicke * */ @SuppressWarnings({"unchecked", "rawtypes"}) public class BufferedStackHandler extends StackHandler implements Cloneable, IStackHandler { private LinkedList<Tuple> buffer = new LinkedList<>(); private FeatureExpr bufferCTX = FeatureExprFactory.True(); private int maxStackSize; @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); string.append("\nBuffered: " + bufferCTX); int i = buffer.size(); for (Tuple e : buffer) { string.append('\n'); string.append(--i); string.append(':'); string.append(e); } return string.toString(); } public BufferedStackHandler(FeatureExpr ctx, int nLocals, int nOperands) { if (ctx == null) { throw new RuntimeException("CTX == NULL"); } length = nLocals + nOperands; maxStackSize = nOperands; locals = new Conditional[nLocals]; Arrays.fill(locals, nullValue); stack = new One<>(new Stack(nOperands)); stackCTX = ctx; } public BufferedStackHandler() { super(); maxStackSize = 0; } @Override public BufferedStackHandler clone() { BufferedStackHandler clone = new BufferedStackHandler(); clone.maxStackSize = maxStackSize; 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(new CopyEntry()); } } clone.buffer = (LinkedList<Tuple>) buffer.clone(); clone.bufferCTX = bufferCTX; clone.stack = stack.map(new CopyStack()); return clone; } static final class CopyEntry implements Function<Entry, Entry> { @Override public Entry apply(final Entry entry) { return entry.copy(); } } static final class CopyStack implements Function<Stack, Stack> { @Override public Stack apply(final Stack stack) { return stack.copy(); } } /* * ####################################################### Handling of the * stack ######################################################## */ public void debufferAll() { final FeatureExpr ctx = bufferCTX; bufferCTX = FeatureExprFactory.False(); while (!buffer.isEmpty()) { final Tuple value = buffer.removeLast(); value.value.mapf(ctx, new BiConsumer<FeatureExpr, Object>() { @Override public void accept(final FeatureExpr ctx, final Object v) { superPush(ctx, v, value.isRef); } }); } bufferCTX = FeatureExprFactory.True(); } public void superPush(FeatureExpr ctx, Object value, boolean isRef) { super.push(ctx, value, isRef); } void addToBuffer(Conditional<?> value, boolean isRef) { buffer.push(new Tuple(value, isRef)); } class Tuple { final Conditional value; final boolean isRef; private Tuple(Conditional value, boolean isRef) { if (value instanceof Conditional) { this.value = value; } else { this.value = new One<>(value); } this.isRef = isRef; } @Override public String toString() { return "Tuple [value=" + value + ", isRef=" + isRef + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (isRef ? 1231 : 1237); return prime * result + ((value == null) ? 0 : value.hashCode()); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Tuple other = (Tuple) obj; if (isRef != other.isRef) return false; if (value == null) { if (other.value != null) return false; } else if (!value.equals(other.value)) return false; return true; } } @Override public void storeOperand(FeatureExpr ctx, int index) { if (!buffer.isEmpty()) { if (bufferCTX.equivalentTo(ctx)) { final Tuple entry = buffer.peek(); Object value = entry.value.getValue(true); if (value instanceof Integer || value instanceof Byte || value instanceof Short|| value instanceof Float) { Conditional newValue = buffer.pop().value.map(new Function<Number, Entry>() { @Override public Entry apply(Number x) { if (x instanceof Float) { return Entry.create(Types.floatToInt((Float)x), entry.isRef); } return Entry.create(x.intValue(), entry.isRef); } }); if (Conditional.isTautology(ctx)) { locals[index] = newValue; } else { if (locals[index] == null) { locals[index] = new One<>(Entry.create(MJIEnv.NULL, false)); } locals[index] = ChoiceFactory.create(ctx, newValue, locals[index]).simplify(); } return; } else { throw new RuntimeException("Type " + value.getClass() + " not supported "); } } debufferAll(); } super.storeOperand(ctx, index); } @Override public void push(FeatureExpr ctx, Object value, boolean isRef) { if (!(value instanceof Conditional)) { push(ctx, new One<>(value), isRef); return; } if (buffer.isEmpty()) { bufferCTX = ctx; addToBuffer(((Conditional) value).simplify(ctx), isRef); } else if (ctx.equivalentTo(bufferCTX)) { addToBuffer(((Conditional) value).simplify(ctx), isRef); } else { debufferAll(); bufferCTX = ctx; addToBuffer(((Conditional) value).simplify(ctx), isRef); } } @Override public <T> Conditional<T> pop(FeatureExpr ctx, Type t) { if (!buffer.isEmpty()) { if (bufferCTX.equivalentTo(ctx)) { final Object value = buffer.peek().value.getValue(true); switch (t) { case DOUBLE: if (value instanceof Double) { return buffer.pop().value; } if (value instanceof Long) { return buffer.pop().value.map(new Function<Long, Double>() { @Override public Double apply(Long x) { return Types.longToDouble(x); } }); } if (value instanceof Integer) { if (buffer.size() >= 2) { final Object value2 = buffer.get(1).value.getValue(true); if (value2 instanceof Integer) { final Conditional pop1 = buffer.pop().value; final Conditional pop2 = buffer.pop().value; return pop1.mapr(new Function<Integer, Conditional<Double>>() { @Override public Conditional<Double> apply(final Integer x1) { return pop2.map(new Function<Integer, Double>() { @Override public Double apply(Integer x2) { return Types.intsToDouble(x1, x2); } }); } }).simplify(); } } break; } throw new RuntimeException("Type " + value.getClass() + " not supported " + t); case FLOAT: if (value instanceof Float) { return buffer.pop().value; } if (value instanceof Integer) { return buffer.pop().value.map(new Function<Integer, Float>() { @Override public Float apply(Integer x) { return Types.intToFloat(x); } }); } throw new RuntimeException("Type " + value.getClass() + " not supported " + t); case INT: if (value instanceof Integer) { return buffer.pop().value; } if (value instanceof Byte || value instanceof Short) { return buffer.pop().value.map(new Function<Number, Integer>() { @Override public Integer apply(Number x) { return x.intValue(); } }); } if (value instanceof Float) { return buffer.pop().value.map(new Function<Float, Integer>() { @Override public Integer apply(Float x) { return Types.floatToInt(x); } }); } throw new RuntimeException("Type " + value.getClass() + " not supported " + t); case LONG: if (value instanceof Long) { return buffer.pop().value; } if (value instanceof Double) { return buffer.pop().value.map(new Function<Double, Long>() { @Override public Long apply(Double x) { return Types.doubleToLong(x); } }); } if (value instanceof Integer) { if (buffer.size() >= 2) { final Object value2 = buffer.get(1).value.getValue(true); if (value2 instanceof Integer) { final Conditional pop1 = buffer.pop().value; final Conditional pop2 = buffer.pop().value; return pop1.mapr(new Function<Integer, Conditional<Long>>() { @Override public Conditional<Long> apply(final Integer x1) { return pop2.map(new Function<Integer, Long>() { @Override public Long apply(Integer x2) { return Types.intsToLong(x1, x2); } }); } }).simplify(); } } break; } throw new RuntimeException("Type " + value.getClass() + " not supported " + t); default: throw new RuntimeException("Type " + value.getClass() + " not supported " + t); } debufferAll(); } else { debufferAll(); } } return super.pop(ctx, t); } @Override public void pop(FeatureExpr ctx, int n) { if (n == 0) { return; } if (!buffer.isEmpty()) { if (buffer.size() >= n && bufferCTX.equivalentTo(ctx)) { while (n > 0) { Object value = buffer.peek().value.getValue(true); if (value instanceof Integer || value instanceof Float || value instanceof Byte || value instanceof Short) { buffer.removeFirst(); n--; continue; } if (value instanceof Double || value instanceof Long) { if (n > 1) { buffer.removeFirst(); buffer.removeFirst(); n = n - 2; continue; } else { debufferAll(); super.pop(ctx, n); return; } } throw new RuntimeException("type " + value.getClass() + " missed"); } } else { debufferAll(); super.pop(ctx, n); } } else { super.pop(ctx, n); } } @Override public Conditional<Integer> peek(FeatureExpr ctx) { if (!buffer.isEmpty()) { if (bufferCTX.equivalentTo(ctx)) { Conditional<Integer> peek = buffer.peek().value; if (peek.getValue(true) instanceof Integer) { return peek; } } debufferAll(); } return super.peek(ctx); } private int getBufferSize() { int size = 0; for (Tuple entry : buffer) { Object type = entry.value.getValue(true); if (type instanceof Integer || type instanceof Float || type instanceof Byte || type instanceof Short) { size++; } else { size += 2; } } return size; } @Override public Conditional<Integer> peek(FeatureExpr ctx, final int offset) { if (!buffer.isEmpty()) { if (getBufferSize() > offset && bufferCTX.equivalentTo(ctx)) { int pointer = offset; int n = 0; while (n <= pointer) { Conditional value = buffer.get(n).value; Object type = value.getValue(true); if (type instanceof Integer) { if (n == offset) { return value; } n++; continue; } if (type instanceof Float) { if (n == offset) { return value.map(new Function<Float, Integer>() { @Override public Integer apply(Float x) { return Types.floatToInt(x.floatValue()); } }); } n++; continue; } if (type instanceof Double || type instanceof Long) { if (n - pointer > 1) { n++; pointer--; continue; } else { debufferAll(); return super.peek(ctx, offset); } } if (type instanceof Byte) { if (n == offset) { return value.map(new Function<Byte, Integer>() { @Override public Integer apply(Byte x) { return (int)x.byteValue(); } }); } n++; continue; } if (type instanceof Short) { if (n == offset) { return value.map(new Function<Short, Integer>() { @Override public Integer apply(Short x) { return (int)x; } }); } n++; continue; } throw new RuntimeException("type " + type.getClass() + " missed"); } } debufferAll(); } return super.peek(ctx, offset); } @Override public <T> Conditional<T> peek(FeatureExpr ctx, final int offset, Type t) { if (!buffer.isEmpty()) { if (getBufferSize() > offset && bufferCTX.equivalentTo(ctx)) { int pointer = offset; int n = 0; while (n <= pointer) { Conditional value = buffer.get(n).value; Object type = value.getValue(true); if (type instanceof Integer) { if (n == offset) { switch (t) { case LONG: if (buffer.size() > n + 1) { final Conditional value2 = buffer.get(n + 1).value; return value.mapr(new Function<Integer, Conditional<Long>>() { @Override public Conditional<Long> apply(final Integer x1) { return value2.map(new Function<Integer, Long>() { @Override public Long apply(Integer x2) { return Types.intsToLong(x1, x2); } }); } }); } else { break; } default: throw new RuntimeException("type " + type.getClass() + " missed" + t); } } n++; continue; } if (type instanceof Float) { if (n == offset) { switch (t) { case FLOAT: return value; case INT: return value.map(new Function<Float, Integer>() { @Override public Integer apply(Float x) { return Types.floatToInt(x.floatValue()); } }); default: break; } } n++; continue; } if (type instanceof Double || type instanceof Long) { if (n - pointer > 1) { n++; pointer--; continue; } else { debufferAll(); return super.peek(ctx, offset, t); } } if (type instanceof Byte) { if (n == offset) { return value.map(new Function<Byte, Integer>() { @Override public Integer apply(Byte x) { return (int)x.byteValue(); } }); } n++; continue; } if (type instanceof Short) { if (n == offset) { return value.map(new Function<Short, Integer>() { @Override public Integer apply(Short x) { return (int)x; } }); } n++; continue; } throw new RuntimeException("type " + type.getClass() + " missed"); } } debufferAll(); } return super.peek(ctx, offset, t); } @Override public void storeLongOperand(FeatureExpr ctx, int index) { debufferAll();// TODO improve super.storeLongOperand(ctx, index); } @Override public void clear(FeatureExpr ctx) { if (!buffer.isEmpty()) { if (bufferCTX.equivalentTo(ctx)) { buffer.clear(); bufferCTX = FeatureExprFactory.True(); } else { debufferAll(); } } super.clear(ctx); } @Override public boolean hasAnyRef(FeatureExpr ctx) { if (!buffer.isEmpty()) { if (bufferCTX.equivalentTo(ctx)) { for (Tuple entry : buffer) { if (entry.isRef) { return true; } } return super.hasAnyRef(ctx); } debufferAll(); } return super.hasAnyRef(ctx); } @Override public void set(FeatureExpr ctx, int offset, int value, boolean isRef) { debufferAll(); super.set(ctx, offset, value, isRef); } @Override public void setLocal(FeatureExpr ctx, int index, Conditional<Integer> value, boolean isRef) { super.setLocal(ctx, index, value, isRef); } @Override public void setLocal(FeatureExpr ctx, int index, int value, boolean isRef) { super.setLocal(ctx, index, value, isRef); } @Override public void pushLocal(FeatureExpr ctx, int index) { Conditional<Entry> value = locals[index].simplify(ctx); if (value == null) { value = new One<>(Entry.create(MJIEnv.NULL, false)); } Boolean isRef = null; for (Entry v : value.toList()) { if (isRef == null) { isRef = v.isRef; } else if (isRef != v.isRef) { debufferAll(); value.mapf(ctx, new BiFunction<FeatureExpr, Entry, Conditional<Object>>() { @Override public Conditional<Object> apply(final FeatureExpr ctx, final Entry entry) { push(ctx, entry.value, entry.isRef); return null; } }); return; } } Conditional<Integer> pushValue = value.map(new Function<Entry, Integer>() { @Override public Integer apply(Entry x) { return x.value; } }); if (buffer.isEmpty()) { bufferCTX = ctx; addToBuffer(( pushValue), isRef); return; } if (ctx.equivalentTo(bufferCTX)) { addToBuffer((pushValue), isRef); return; } else { debufferAll(); bufferCTX = ctx; addToBuffer(( pushValue), isRef); return; } } @Override public void pushLongLocal(FeatureExpr ctx, int index) { Conditional<Entry> entry = locals[index]; if (entry == null) { entry = new One<>(new Entry(0, false)); } push(ctx, entry.mapf(ctx, new BiFunction<FeatureExpr, Entry, Conditional<Integer>>() { @Override public One<Integer> apply(final FeatureExpr ctx, final Entry entry) { return new One<>(entry.value); } }), false); entry = locals[index + 1]; if (entry == null) { entry = new One<>(new Entry(0, false)); } push(ctx, entry.mapf(ctx, new BiFunction<FeatureExpr, Entry, Conditional<Integer>>() { @Override public One<Integer> apply(final FeatureExpr ctx, final Entry entry) { return new One<>(entry.value); } }), false); } @Override public boolean isRef(FeatureExpr ctx, int offset) { if (!buffer.isEmpty()) { if (bufferCTX.equivalentTo(ctx)) { if (buffer.size() > offset) { return buffer.get(offset).isRef; } else { return super.isRef(ctx, offset - buffer.size()); } } debufferAll(); } return super.isRef(ctx, offset); } @Override public boolean isRefLocal(FeatureExpr ctx, int index) { if (index >= locals.length) { debufferAll(); } return super.isRefLocal(ctx, index); } @Override public Conditional<Integer> getTop() { if (!buffer.isEmpty()) { int size = -1; for (Tuple entry : buffer) { Conditional value = entry.value; if (value.getValue(true) instanceof Integer) { size++; continue; } if (value.getValue(true) instanceof Float) { size++; continue; } if (value.getValue(true) instanceof Long) { size += 2; continue; } if (value.getValue(true) instanceof Double) { size += 2; continue; } } Conditional<Integer> stackTop = super.getTop(); if (stackTop.equals(One.valueOf(-1))) { if (Conditional.isTautology(bufferCTX)) { return One.valueOf(size); } return ChoiceFactory.create(bufferCTX, new One<>(size), new One<>(-1)); } final int finalSize = size; return stackTop.mapf(FeatureExprFactory.True(), new BiFunction<FeatureExpr, Integer, Conditional<Integer>>() { @Override public Conditional<Integer> apply(FeatureExpr ctx, Integer y) { FeatureExpr context = bufferCTX.and(ctx); if (Conditional.isContradiction(context)) { return One.valueOf(y); } return ChoiceFactory.create(context, new One<>(y + finalSize + 1), new One<>(y)); } }).simplify(); } return super.getTop(); } // A => A A @Override public void dup(FeatureExpr ctx) { if (!buffer.isEmpty()) { if (bufferCTX.equivalentTo(ctx)) { Tuple peek = buffer.peek(); if (peek.value.getValue(true) instanceof Integer) { buffer.push(peek); return; } debufferAll(); } else { debufferAll(); } } super.dup(ctx); } // A B => A B A B @Override public void dup2(FeatureExpr ctx) { if (!buffer.isEmpty()) { if (buffer.size() >= 2 && bufferCTX.equivalentTo(ctx)) { if (buffer.peek().value.getValue(true) instanceof Integer && buffer.get(1).value.getValue(true) instanceof Integer) { buffer.push(buffer.get(1)); buffer.push(buffer.get(1)); return; } } debufferAll(); } super.dup2(ctx); } // A B => B A @Override public void swap(FeatureExpr ctx) { if (!buffer.isEmpty()) { if (buffer.size() >= 2 && bufferCTX.equivalentTo(ctx)) { if (buffer.peek().value.getValue(true) instanceof Integer && buffer.get(1).value.getValue(true) instanceof Integer) { buffer.add(1, buffer.pop()); return; } } debufferAll(); } super.swap(ctx); } // A B C => .. B C A B C @Override public void dup2_x1(FeatureExpr ctx) { if (!buffer.isEmpty()) { if (buffer.size() >= 3 && bufferCTX.equivalentTo(ctx)) { if (buffer.peek().value.getValue(true) instanceof Integer && buffer.get(1).value.getValue(true) instanceof Integer && buffer.get(2).value.getValue(true) instanceof Integer) { buffer.add(3, buffer.get(1)); buffer.add(3, buffer.peek()); return; } } debufferAll(); } super.dup2_x1(ctx); } // A B C D => .. C D A B C D @Override public void dup2_x2(FeatureExpr ctx) { if (!buffer.isEmpty()) { if (buffer.size() >= 4 && bufferCTX.equivalentTo(ctx)) { if (buffer.peek().value.getValue(true) instanceof Integer && buffer.get(1).value.getValue(true) instanceof Integer && buffer.get(2).value.getValue(true) instanceof Integer && buffer.get(3).value.getValue(true) instanceof Integer) { buffer.add(4, buffer.get(1)); buffer.add(4, buffer.peek()); return; } } debufferAll(); } super.dup2_x2(ctx); } // A B => .. B A B @Override public void dup_x1(FeatureExpr ctx) { if (!buffer.isEmpty()) { if (buffer.size() >= 2) { if (bufferCTX.equivalentTo(ctx)) { if (buffer.peek().value.getValue(true) instanceof Integer && buffer.get(1).value.getValue(true) instanceof Integer) { buffer.add(2, buffer.peek()); return; } } } debufferAll(); } super.dup_x1(ctx); } // A B C => .. C A B C @Override public void dup_x2(FeatureExpr ctx) { if (!buffer.isEmpty()) { if (buffer.size() >= 3 && bufferCTX.equivalentTo(ctx)) { buffer.add(3, buffer.peek()); return; } else { debufferAll(); } } super.dup_x2(ctx); } @Override public Set<Integer> getAllReferences() { Set<Integer> references = super.getAllReferences(); for (Tuple entry : buffer) { if (entry.isRef) { references.addAll(entry.value.toList()); } } return references; } @Override public boolean equals(Object o) { return super.equals(o); } @Override public Conditional<Integer> getLocal(FeatureExpr ctx, int index) { if (index >= locals.length) { debufferAll(); } return super.getLocal(ctx, index); } @Override public int[] getSlots() { debufferAll(); return super.getSlots(); } @Override public int[] getSlots(FeatureExpr ctx) { debufferAll(); return super.getSlots(ctx); } @Override public Conditional<Stack> getStack() { debufferAll(); return super.getStack(); } @Override public int getStackWidth() { // debufferAll(); return super.getStackWidth(); } }