/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package scouter.bytebuddy.jar.asm.tree.analysis;
import java.util.List;
import scouter.bytebuddy.jar.asm.Type;
import scouter.bytebuddy.jar.asm.tree.AbstractInsnNode;
import scouter.bytebuddy.jar.asm.tree.FieldInsnNode;
import scouter.bytebuddy.jar.asm.tree.InvokeDynamicInsnNode;
import scouter.bytebuddy.jar.asm.tree.MethodInsnNode;
import scouter.bytebuddy.jar.asm.Opcodes;
/**
* An extended {@link BasicInterpreter} that checks that bytecode instructions
* are correctly used.
*
* @author Eric Bruneton
* @author Bing Ran
*/
public class BasicVerifier extends BasicInterpreter {
public BasicVerifier() {
super(Opcodes.ASM5);
}
protected BasicVerifier(final int api) {
super(api);
}
@Override
public BasicValue copyOperation(final AbstractInsnNode insn,
final BasicValue value) throws AnalyzerException {
Value expected;
switch (insn.getOpcode()) {
case Opcodes.ILOAD:
case Opcodes.ISTORE:
expected = BasicValue.INT_VALUE;
break;
case Opcodes.FLOAD:
case Opcodes.FSTORE:
expected = BasicValue.FLOAT_VALUE;
break;
case Opcodes.LLOAD:
case Opcodes.LSTORE:
expected = BasicValue.LONG_VALUE;
break;
case Opcodes.DLOAD:
case Opcodes.DSTORE:
expected = BasicValue.DOUBLE_VALUE;
break;
case Opcodes.ALOAD:
if (!value.isReference()) {
throw new AnalyzerException(insn, null, "an object reference",
value);
}
return value;
case Opcodes.ASTORE:
if (!value.isReference()
&& !BasicValue.RETURNADDRESS_VALUE.equals(value)) {
throw new AnalyzerException(insn, null,
"an object reference or a return address", value);
}
return value;
default:
return value;
}
if (!expected.equals(value)) {
throw new AnalyzerException(insn, null, expected, value);
}
return value;
}
@Override
public BasicValue unaryOperation(final AbstractInsnNode insn,
final BasicValue value) throws AnalyzerException {
BasicValue expected;
switch (insn.getOpcode()) {
case Opcodes.INEG:
case Opcodes.IINC:
case Opcodes.I2F:
case Opcodes.I2L:
case Opcodes.I2D:
case Opcodes.I2B:
case Opcodes.I2C:
case Opcodes.I2S:
case Opcodes.IFEQ:
case Opcodes.IFNE:
case Opcodes.IFLT:
case Opcodes.IFGE:
case Opcodes.IFGT:
case Opcodes.IFLE:
case Opcodes.TABLESWITCH:
case Opcodes.LOOKUPSWITCH:
case Opcodes.IRETURN:
case Opcodes.NEWARRAY:
case Opcodes.ANEWARRAY:
expected = BasicValue.INT_VALUE;
break;
case Opcodes.FNEG:
case Opcodes.F2I:
case Opcodes.F2L:
case Opcodes.F2D:
case Opcodes.FRETURN:
expected = BasicValue.FLOAT_VALUE;
break;
case Opcodes.LNEG:
case Opcodes.L2I:
case Opcodes.L2F:
case Opcodes.L2D:
case Opcodes.LRETURN:
expected = BasicValue.LONG_VALUE;
break;
case Opcodes.DNEG:
case Opcodes.D2I:
case Opcodes.D2F:
case Opcodes.D2L:
case Opcodes.DRETURN:
expected = BasicValue.DOUBLE_VALUE;
break;
case Opcodes.GETFIELD:
expected = newValue(Type
.getObjectType(((FieldInsnNode) insn).owner));
break;
case Opcodes.CHECKCAST:
if (!value.isReference()) {
throw new AnalyzerException(insn, null, "an object reference",
value);
}
return super.unaryOperation(insn, value);
case Opcodes.ARRAYLENGTH:
if (!isArrayValue(value)) {
throw new AnalyzerException(insn, null, "an array reference",
value);
}
return super.unaryOperation(insn, value);
case Opcodes.ARETURN:
case Opcodes.ATHROW:
case Opcodes.INSTANCEOF:
case Opcodes.MONITORENTER:
case Opcodes.MONITOREXIT:
case Opcodes.IFNULL:
case Opcodes.IFNONNULL:
if (!value.isReference()) {
throw new AnalyzerException(insn, null, "an object reference",
value);
}
return super.unaryOperation(insn, value);
case Opcodes.PUTSTATIC:
expected = newValue(Type.getType(((FieldInsnNode) insn).desc));
break;
default:
throw new Error("Internal error.");
}
if (!isSubTypeOf(value, expected)) {
throw new AnalyzerException(insn, null, expected, value);
}
return super.unaryOperation(insn, value);
}
@Override
public BasicValue binaryOperation(final AbstractInsnNode insn,
final BasicValue value1, final BasicValue value2)
throws AnalyzerException {
BasicValue expected1;
BasicValue expected2;
switch (insn.getOpcode()) {
case Opcodes.IALOAD:
expected1 = newValue(Type.getType("[I"));
expected2 = BasicValue.INT_VALUE;
break;
case Opcodes.BALOAD:
if (isSubTypeOf(value1, newValue(Type.getType("[Z")))) {
expected1 = newValue(Type.getType("[Z"));
} else {
expected1 = newValue(Type.getType("[B"));
}
expected2 = BasicValue.INT_VALUE;
break;
case Opcodes.CALOAD:
expected1 = newValue(Type.getType("[C"));
expected2 = BasicValue.INT_VALUE;
break;
case Opcodes.SALOAD:
expected1 = newValue(Type.getType("[S"));
expected2 = BasicValue.INT_VALUE;
break;
case Opcodes.LALOAD:
expected1 = newValue(Type.getType("[J"));
expected2 = BasicValue.INT_VALUE;
break;
case Opcodes.FALOAD:
expected1 = newValue(Type.getType("[F"));
expected2 = BasicValue.INT_VALUE;
break;
case Opcodes.DALOAD:
expected1 = newValue(Type.getType("[D"));
expected2 = BasicValue.INT_VALUE;
break;
case Opcodes.AALOAD:
expected1 = newValue(Type.getType("[Ljava/lang/Object;"));
expected2 = BasicValue.INT_VALUE;
break;
case Opcodes.IADD:
case Opcodes.ISUB:
case Opcodes.IMUL:
case Opcodes.IDIV:
case Opcodes.IREM:
case Opcodes.ISHL:
case Opcodes.ISHR:
case Opcodes.IUSHR:
case Opcodes.IAND:
case Opcodes.IOR:
case Opcodes.IXOR:
case Opcodes.IF_ICMPEQ:
case Opcodes.IF_ICMPNE:
case Opcodes.IF_ICMPLT:
case Opcodes.IF_ICMPGE:
case Opcodes.IF_ICMPGT:
case Opcodes.IF_ICMPLE:
expected1 = BasicValue.INT_VALUE;
expected2 = BasicValue.INT_VALUE;
break;
case Opcodes.FADD:
case Opcodes.FSUB:
case Opcodes.FMUL:
case Opcodes.FDIV:
case Opcodes.FREM:
case Opcodes.FCMPL:
case Opcodes.FCMPG:
expected1 = BasicValue.FLOAT_VALUE;
expected2 = BasicValue.FLOAT_VALUE;
break;
case Opcodes.LADD:
case Opcodes.LSUB:
case Opcodes.LMUL:
case Opcodes.LDIV:
case Opcodes.LREM:
case Opcodes.LAND:
case Opcodes.LOR:
case Opcodes.LXOR:
case Opcodes.LCMP:
expected1 = BasicValue.LONG_VALUE;
expected2 = BasicValue.LONG_VALUE;
break;
case Opcodes.LSHL:
case Opcodes.LSHR:
case Opcodes.LUSHR:
expected1 = BasicValue.LONG_VALUE;
expected2 = BasicValue.INT_VALUE;
break;
case Opcodes.DADD:
case Opcodes.DSUB:
case Opcodes.DMUL:
case Opcodes.DDIV:
case Opcodes.DREM:
case Opcodes.DCMPL:
case Opcodes.DCMPG:
expected1 = BasicValue.DOUBLE_VALUE;
expected2 = BasicValue.DOUBLE_VALUE;
break;
case Opcodes.IF_ACMPEQ:
case Opcodes.IF_ACMPNE:
expected1 = BasicValue.REFERENCE_VALUE;
expected2 = BasicValue.REFERENCE_VALUE;
break;
case Opcodes.PUTFIELD:
FieldInsnNode fin = (FieldInsnNode) insn;
expected1 = newValue(Type.getObjectType(fin.owner));
expected2 = newValue(Type.getType(fin.desc));
break;
default:
throw new Error("Internal error.");
}
if (!isSubTypeOf(value1, expected1)) {
throw new AnalyzerException(insn, "First argument", expected1,
value1);
} else if (!isSubTypeOf(value2, expected2)) {
throw new AnalyzerException(insn, "Second argument", expected2,
value2);
}
if (insn.getOpcode() == Opcodes.AALOAD) {
return getElementValue(value1);
} else {
return super.binaryOperation(insn, value1, value2);
}
}
@Override
public BasicValue ternaryOperation(final AbstractInsnNode insn,
final BasicValue value1, final BasicValue value2,
final BasicValue value3) throws AnalyzerException {
BasicValue expected1;
BasicValue expected3;
switch (insn.getOpcode()) {
case Opcodes.IASTORE:
expected1 = newValue(Type.getType("[I"));
expected3 = BasicValue.INT_VALUE;
break;
case Opcodes.BASTORE:
if (isSubTypeOf(value1, newValue(Type.getType("[Z")))) {
expected1 = newValue(Type.getType("[Z"));
} else {
expected1 = newValue(Type.getType("[B"));
}
expected3 = BasicValue.INT_VALUE;
break;
case Opcodes.CASTORE:
expected1 = newValue(Type.getType("[C"));
expected3 = BasicValue.INT_VALUE;
break;
case Opcodes.SASTORE:
expected1 = newValue(Type.getType("[S"));
expected3 = BasicValue.INT_VALUE;
break;
case Opcodes.LASTORE:
expected1 = newValue(Type.getType("[J"));
expected3 = BasicValue.LONG_VALUE;
break;
case Opcodes.FASTORE:
expected1 = newValue(Type.getType("[F"));
expected3 = BasicValue.FLOAT_VALUE;
break;
case Opcodes.DASTORE:
expected1 = newValue(Type.getType("[D"));
expected3 = BasicValue.DOUBLE_VALUE;
break;
case Opcodes.AASTORE:
expected1 = value1;
expected3 = BasicValue.REFERENCE_VALUE;
break;
default:
throw new Error("Internal error.");
}
if (!isSubTypeOf(value1, expected1)) {
throw new AnalyzerException(insn, "First argument", "a "
+ expected1 + " array reference", value1);
} else if (!BasicValue.INT_VALUE.equals(value2)) {
throw new AnalyzerException(insn, "Second argument",
BasicValue.INT_VALUE, value2);
} else if (!isSubTypeOf(value3, expected3)) {
throw new AnalyzerException(insn, "Third argument", expected3,
value3);
}
return null;
}
@Override
public BasicValue naryOperation(final AbstractInsnNode insn,
final List<? extends BasicValue> values) throws AnalyzerException {
int opcode = insn.getOpcode();
if (opcode == Opcodes.MULTIANEWARRAY) {
for (int i = 0; i < values.size(); ++i) {
if (!BasicValue.INT_VALUE.equals(values.get(i))) {
throw new AnalyzerException(insn, null,
BasicValue.INT_VALUE, values.get(i));
}
}
} else {
int i = 0;
int j = 0;
if (opcode != Opcodes.INVOKESTATIC && opcode != Opcodes.INVOKEDYNAMIC) {
Type owner = Type.getObjectType(((MethodInsnNode) insn).owner);
if (!isSubTypeOf(values.get(i++), newValue(owner))) {
throw new AnalyzerException(insn, "Method owner",
newValue(owner), values.get(0));
}
}
String desc = (opcode == Opcodes.INVOKEDYNAMIC) ? ((InvokeDynamicInsnNode) insn).desc
: ((MethodInsnNode) insn).desc;
Type[] args = Type.getArgumentTypes(desc);
while (i < values.size()) {
BasicValue expected = newValue(args[j++]);
BasicValue encountered = values.get(i++);
if (!isSubTypeOf(encountered, expected)) {
throw new AnalyzerException(insn, "Argument " + j,
expected, encountered);
}
}
}
return super.naryOperation(insn, values);
}
@Override
public void returnOperation(final AbstractInsnNode insn,
final BasicValue value, final BasicValue expected)
throws AnalyzerException {
if (!isSubTypeOf(value, expected)) {
throw new AnalyzerException(insn, "Incompatible return type",
expected, value);
}
}
protected boolean isArrayValue(final BasicValue value) {
return value.isReference();
}
protected BasicValue getElementValue(final BasicValue objectArrayValue)
throws AnalyzerException {
return BasicValue.REFERENCE_VALUE;
}
protected boolean isSubTypeOf(final BasicValue value,
final BasicValue expected) {
return value.equals(expected);
}
}