use of org.sonar.java.bytecode.cfg.Instruction in project sonar-java by SonarSource.
the class BytecodeEGWalker method handleMethodInvocation.
private boolean handleMethodInvocation(Instruction instruction) {
boolean isStatic = instruction.opcode == Opcodes.INVOKESTATIC;
int arity = isStatic ? instruction.arity() : (instruction.arity() + 1);
ProgramState.Pop pop = programState.unstackValue(arity);
Preconditions.checkState(pop.values.size() == arity, "Arguments mismatch for INVOKE");
// TODO use constraintManager.createMethodSymbolicValue to create relational SV for equals
programState = pop.state;
SymbolicValue returnSV = instruction.hasReturnValue() ? constraintManager.createSymbolicValue(instruction) : null;
String signature = instruction.fieldOrMethod.completeSignature();
MethodBehavior methodInvokedBehavior = behaviorCache.get(signature);
enqueueUncheckedExceptions();
// FIXME : empty yields here should not happen, for now act as if behavior was not resolved.
if (methodInvokedBehavior != null && methodInvokedBehavior.isComplete() && !methodInvokedBehavior.yields().isEmpty()) {
List<SymbolicValue> stack = Lists.reverse(pop.values);
if (!isStatic) {
// remove "thisSV" from stack before trying to apply any yield, as it should not match with arguments
stack = stack.subList(1, stack.size());
}
List<SymbolicValue> arguments = stack;
methodInvokedBehavior.happyPathYields().forEach(yield -> yield.statesAfterInvocation(arguments, Collections.emptyList(), programState, () -> returnSV).forEach(ps -> {
checkerDispatcher.methodYield = yield;
if (ps.peekValue() != null) {
ps = setDoubleOrLong(ps, ps.peekValue(), instruction.isLongOrDoubleValue());
}
checkerDispatcher.addTransition(ps);
checkerDispatcher.methodYield = null;
}));
methodInvokedBehavior.exceptionalPathYields().forEach(yield -> {
Type exceptionType = yield.exceptionType(semanticModel);
yield.statesAfterInvocation(arguments, Collections.emptyList(), programState, () -> constraintManager.createExceptionalSymbolicValue(exceptionType)).forEach(ps -> {
ps.storeExitValue();
enqueueExceptionHandlers(exceptionType, ps);
});
});
return true;
}
if (methodInvokedBehavior != null) {
methodInvokedBehavior.getDeclaredExceptions().forEach(exception -> {
Type exceptionType = semanticModel.getClassType(exception);
ProgramState ps = programState.stackValue(constraintManager.createExceptionalSymbolicValue(exceptionType));
enqueueExceptionHandlers(exceptionType, ps);
});
}
if (instruction.hasReturnValue()) {
programState = programState.stackValue(returnSV);
programState = setDoubleOrLong(returnSV, instruction.isLongOrDoubleValue());
}
return false;
}
use of org.sonar.java.bytecode.cfg.Instruction in project sonar-java by SonarSource.
the class BytecodeEGWalker method executeInstruction.
@VisibleForTesting
void executeInstruction(Instruction instruction) {
if (!checkerDispatcher.executeCheckPreStatement(instruction)) {
return;
}
ProgramState.Pop pop;
SymbolicValue sv;
switch(instruction.opcode) {
case NOP:
break;
case ACONST_NULL:
programState = programState.stackValue(SymbolicValue.NULL_LITERAL);
break;
case ICONST_M1:
case ICONST_0:
case ICONST_1:
case ICONST_2:
case ICONST_3:
case ICONST_4:
case ICONST_5:
case LCONST_0:
case LCONST_1:
case FCONST_0:
case FCONST_1:
case FCONST_2:
case DCONST_0:
case DCONST_1:
sv = constraintManager.createSymbolicValue(instruction);
programState = setDoubleOrLong(sv, instruction.isLongOrDoubleValue());
programState = programState.stackValue(sv).addConstraint(sv, ObjectConstraint.NOT_NULL);
if (instruction.opcode == ICONST_1 || instruction.opcode == LCONST_1 || instruction.opcode == FCONST_1 || instruction.opcode == DCONST_1) {
programState = programState.addConstraint(sv, BooleanConstraint.TRUE);
}
if (instruction.opcode == ICONST_0 || instruction.opcode == LCONST_0 || instruction.opcode == FCONST_0 || instruction.opcode == DCONST_0) {
programState = programState.addConstraint(sv, BooleanConstraint.FALSE).addConstraint(sv, DivisionByZeroCheck.ZeroConstraint.ZERO);
} else {
programState = programState.addConstraint(sv, DivisionByZeroCheck.ZeroConstraint.NON_ZERO);
}
break;
case BIPUSH:
case SIPUSH:
sv = constraintManager.createSymbolicValue(instruction);
programState = programState.stackValue(sv).addConstraint(sv, ObjectConstraint.NOT_NULL).addConstraint(sv, DivisionByZeroCheck.ZeroConstraint.NON_ZERO);
if (instruction.operand == 0) {
programState = programState.addConstraint(sv, BooleanConstraint.FALSE).addConstraint(sv, DivisionByZeroCheck.ZeroConstraint.ZERO);
} else if (instruction.operand == 1) {
programState = programState.addConstraint(sv, BooleanConstraint.TRUE);
}
break;
case LDC:
sv = constraintManager.createSymbolicValue(instruction);
programState = programState.stackValue(sv).addConstraint(sv, ObjectConstraint.NOT_NULL);
programState = setDoubleOrLong(sv, instruction.isLongOrDoubleValue());
break;
case ILOAD:
case LLOAD:
case FLOAD:
case DLOAD:
case ALOAD:
SymbolicValue value = programState.getValue(instruction.operand);
Preconditions.checkNotNull(value, "Loading a symbolic value unindexed");
programState = programState.stackValue(value);
break;
case IALOAD:
case LALOAD:
case FALOAD:
case DALOAD:
case AALOAD:
case BALOAD:
case CALOAD:
case SALOAD:
sv = constraintManager.createSymbolicValue(instruction);
programState = programState.unstackValue(2).state.stackValue(sv);
if (instruction.opcode != AALOAD) {
programState = programState.addConstraint(sv, ObjectConstraint.NOT_NULL);
}
programState = setDoubleOrLong(sv, instruction.isLongOrDoubleValue());
break;
case ISTORE:
case LSTORE:
case FSTORE:
case DSTORE:
case ASTORE:
pop = popStack(1, instruction.opcode);
programState = pop.state.put(instruction.operand, pop.values.get(0));
break;
case IASTORE:
case LASTORE:
case FASTORE:
case DASTORE:
case AASTORE:
case BASTORE:
case CASTORE:
case SASTORE:
programState = programState.unstackValue(3).state;
break;
case POP:
programState = programState.unstackValue(1).state;
break;
case POP2:
sv = programState.peekValue();
Preconditions.checkNotNull(sv, "POP2 on empty stack");
pop = isDoubleOrLong(sv) ? popStack(1, instruction.opcode) : popStack(2, instruction.opcode);
programState = pop.state;
break;
case DUP:
sv = programState.peekValue();
Preconditions.checkNotNull(sv, "DUP on empty stack");
programState = programState.stackValue(sv);
break;
case DUP_X1:
pop = popStack(2, instruction.opcode);
programState = stackValues(pop, 0, 1, 0);
break;
case DUP_X2:
sv = programState.peekValue(1);
if (isDoubleOrLong(sv)) {
pop = popStack(2, instruction.opcode);
programState = stackValues(pop, 0, 1, 0);
} else {
pop = popStack(3, instruction.opcode);
programState = stackValues(pop, 0, 2, 1, 0);
}
break;
case DUP2:
sv = programState.peekValue();
Preconditions.checkNotNull(sv, "DUP2 needs at least 1 value on stack");
if (isDoubleOrLong(sv)) {
pop = popStack(1, instruction.opcode);
programState = stackValues(pop, 0, 0);
} else {
pop = popStack(2, instruction.opcode);
programState = stackValues(pop, 1, 0, 1, 0);
}
break;
case DUP2_X1:
sv = programState.peekValue();
Preconditions.checkNotNull(sv, "DUP2_X1 needs at least 1 value on stack");
if (isDoubleOrLong(sv)) {
pop = popStack(2, instruction.opcode);
programState = stackValues(pop, 0, 1, 0);
} else {
pop = popStack(3, instruction.opcode);
programState = stackValues(pop, 1, 0, 2, 1, 0);
}
break;
case DUP2_X2:
if (isDoubleOrLong(programState.peekValue()) && isDoubleOrLong(programState.peekValue(1))) {
pop = popStack(2, instruction.opcode);
programState = stackValues(pop, 0, 1, 0);
} else if (isDoubleOrLong(programState.peekValue(2))) {
pop = popStack(3, instruction.opcode);
programState = stackValues(pop, 1, 0, 2, 1, 0);
} else if (isDoubleOrLong(programState.peekValue())) {
pop = popStack(3, instruction.opcode);
programState = stackValues(pop, 0, 2, 1, 0);
} else {
pop = popStack(4, instruction.opcode);
programState = stackValues(pop, 1, 0, 3, 2, 1, 0);
}
break;
case SWAP:
pop = popStack(2, instruction.opcode);
programState = pop.state.stackValue(pop.values.get(0)).stackValue(pop.values.get(1));
break;
case IADD:
case LADD:
case FADD:
case DADD:
case ISUB:
case LSUB:
case FSUB:
case DSUB:
case IMUL:
case LMUL:
case FMUL:
case DMUL:
case IDIV:
case LDIV:
case FDIV:
case DDIV:
case IREM:
case LREM:
case FREM:
case DREM:
case ISHL:
case LSHL:
case ISHR:
case LSHR:
case IUSHR:
case LUSHR:
pop = popStack(2, instruction.opcode);
sv = constraintManager.createSymbolicValue(instruction);
programState = pop.state.stackValue(sv).addConstraint(sv, ObjectConstraint.NOT_NULL);
programState = setDoubleOrLong(sv, instruction.isLongOrDoubleValue());
break;
case INEG:
case LNEG:
case FNEG:
case DNEG:
pop = popStack(1, instruction.opcode);
sv = constraintManager.createSymbolicValue(instruction);
programState = pop.state.stackValue(sv).addConstraint(sv, ObjectConstraint.NOT_NULL);
programState = setDoubleOrLong(sv, instruction.isLongOrDoubleValue());
break;
case IAND:
case LAND:
case IOR:
case LOR:
case IXOR:
case LXOR:
pop = popStack(2, instruction.opcode);
sv = constraintManager.createBinarySymbolicValue(instruction, pop.valuesAndSymbols);
programState = pop.state.stackValue(sv).addConstraint(sv, ObjectConstraint.NOT_NULL);
programState = setDoubleOrLong(sv, instruction.isLongOrDoubleValue());
break;
case IINC:
int index = instruction.operand;
SymbolicValue existing = programState.getValue(index);
Preconditions.checkNotNull(existing, "Local variable " + index + " not found");
sv = constraintManager.createSymbolicValue(instruction);
programState = programState.put(index, sv).addConstraint(sv, ObjectConstraint.NOT_NULL);
break;
case I2L:
case I2D:
case F2L:
case F2D:
sv = programState.peekValue();
Preconditions.checkNotNull(sv, "%s needs value on stack", instruction);
programState = setDoubleOrLong(sv, true);
break;
case L2I:
case L2F:
case D2I:
case D2F:
sv = programState.peekValue();
Preconditions.checkNotNull(sv, "%s needs value on stack", instruction);
programState = setDoubleOrLong(sv, false);
break;
case D2L:
case I2F:
case L2D:
case F2I:
case I2B:
case I2C:
case I2S:
break;
case LCMP:
case FCMPL:
case FCMPG:
case DCMPL:
case DCMPG:
pop = popStack(2, instruction.opcode);
sv = constraintManager.createSymbolicValue(instruction);
programState = pop.state.stackValue(sv).addConstraint(sv, ObjectConstraint.NOT_NULL);
break;
case IRETURN:
case LRETURN:
case FRETURN:
case DRETURN:
case ARETURN:
programState.storeExitValue();
programState = programState.unstackValue(1).state;
break;
case RETURN:
// do nothing
break;
case GETSTATIC:
// TODO SONARJAVA-2510 associated symbolic value with symbol
sv = constraintManager.createSymbolicValue(instruction);
programState = programState.stackValue(sv);
programState = setDoubleOrLong(sv, instruction.isLongOrDoubleValue());
break;
case PUTSTATIC:
pop = programState.unstackValue(1);
programState = pop.state;
break;
case GETFIELD:
pop = popStack(1, instruction.opcode);
sv = constraintManager.createSymbolicValue(instruction);
programState = pop.state.stackValue(sv);
programState = setDoubleOrLong(sv, instruction.isLongOrDoubleValue());
break;
case PUTFIELD:
pop = popStack(2, instruction.opcode);
programState = pop.state;
break;
case INVOKEVIRTUAL:
case INVOKESPECIAL:
case INVOKESTATIC:
case INVOKEINTERFACE:
if (handleMethodInvocation(instruction)) {
// when yields are available, do not execute post check on this node
return;
}
break;
case INVOKEDYNAMIC:
pop = popStack(instruction.arity(), instruction.opcode);
Preconditions.checkState(instruction.hasReturnValue(), "Lambda should always evaluate to target functional interface");
SymbolicValue lambdaTargetInterface = new SymbolicValue();
programState = pop.state.stackValue(lambdaTargetInterface).addConstraint(lambdaTargetInterface, ObjectConstraint.NOT_NULL);
break;
case NEW:
sv = constraintManager.createSymbolicValue(instruction);
programState = programState.stackValue(sv).addConstraint(sv, ObjectConstraint.NOT_NULL).addConstraint(sv, new TypedConstraint(instruction.className));
break;
case ARRAYLENGTH:
pop = popStack(1, instruction.opcode);
sv = constraintManager.createSymbolicValue(instruction);
programState = pop.state.stackValue(sv);
break;
case NEWARRAY:
case ANEWARRAY:
pop = popStack(1, instruction.opcode);
sv = constraintManager.createSymbolicValue(instruction);
programState = pop.state.stackValue(sv).addConstraint(sv, ObjectConstraint.NOT_NULL);
break;
case ATHROW:
if (!(programState.peekValue() instanceof SymbolicValue.ExceptionalSymbolicValue)) {
// create exceptional SV if not already on top of the stack (e.g. throw new MyException(); )
pop = popStack(1, instruction.opcode);
sv = pop.values.get(0);
TypedConstraint typedConstraint = programState.getConstraint(sv, TypedConstraint.class);
Type type = typedConstraint != null ? typedConstraint.getType(semanticModel) : Symbols.unknownType;
programState = pop.state.stackValue(constraintManager.createExceptionalSymbolicValue(type));
}
programState.storeExitValue();
break;
case CHECKCAST:
Preconditions.checkState(programState.peekValue() != null, "CHECKCAST needs 1 value on stack");
break;
case INSTANCEOF:
pop = popStack(1, instruction.opcode);
SymbolicValue.InstanceOfSymbolicValue instanceOf = new SymbolicValue.InstanceOfSymbolicValue();
instanceOf.computedFrom(pop.valuesAndSymbols);
programState = pop.state.stackValue(instanceOf);
break;
case MONITORENTER:
case MONITOREXIT:
pop = popStack(1, instruction.opcode);
programState = pop.state;
break;
case MULTIANEWARRAY:
Instruction.MultiANewArrayInsn multiANewArrayInsn = (Instruction.MultiANewArrayInsn) instruction;
pop = popStack(multiANewArrayInsn.dim, instruction.opcode);
SymbolicValue arrayRef = new SymbolicValue();
programState = pop.state.stackValue(arrayRef).addConstraint(arrayRef, ObjectConstraint.NOT_NULL);
break;
default:
throw new IllegalStateException("Instruction not handled. " + instruction);
}
checkerDispatcher.executeCheckPostStatement(instruction);
}
Aggregations