oracle.toplink.libraries.asm.util.CheckCodeAdapter Maven / Gradle / Ivy
The newest version!
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000,2002,2003 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 oracle.toplink.libraries.asm.util;
import oracle.toplink.libraries.asm.Label;
import oracle.toplink.libraries.asm.CodeAdapter;
import oracle.toplink.libraries.asm.CodeVisitor;
import oracle.toplink.libraries.asm.Constants;
import oracle.toplink.libraries.asm.Attribute;
import oracle.toplink.libraries.asm.Type;
import java.util.HashMap;
/**
* A {@link CodeAdapter CodeAdapter} that checks that its methods are properly
* used. More precisely this code adapter checks each instruction individually
* (i.e., each visit method checks some preconditions based only on its
* arguments - such as the fact that the given opcode is correct for a given
* visit method), but does not check the sequence of instructions.
* For example, in a method whose signature is void m (), the invalid
* instruction IRETURN, or the invalid sequence IADD L2I will not be
* detected by this code adapter.
*
* @author Eric Bruneton
*/
public class CheckCodeAdapter extends CodeAdapter {
/**
* true if the visitMaxs method has been called.
*/
private boolean end;
/**
* The already visited labels. This map associate Integer values to Label
* keys.
*/
private HashMap labels;
/**
* Code of the visit method to be used for each opcode.
*/
private final static int[] TYPE = new int[] {
0, //NOP
0, //ACONST_NULL
0, //ICONST_M1
0, //ICONST_0
0, //ICONST_1
0, //ICONST_2
0, //ICONST_3
0, //ICONST_4
0, //ICONST_5
0, //LCONST_0
0, //LCONST_1
0, //FCONST_0
0, //FCONST_1
0, //FCONST_2
0, //DCONST_0
0, //DCONST_1
1, //BIPUSH
1, //SIPUSH
7, //LDC
-1, //LDC_W
-1, //LDC2_W
2, //ILOAD
2, //LLOAD
2, //FLOAD
2, //DLOAD
2, //ALOAD
-1, //ILOAD_0
-1, //ILOAD_1
-1, //ILOAD_2
-1, //ILOAD_3
-1, //LLOAD_0
-1, //LLOAD_1
-1, //LLOAD_2
-1, //LLOAD_3
-1, //FLOAD_0
-1, //FLOAD_1
-1, //FLOAD_2
-1, //FLOAD_3
-1, //DLOAD_0
-1, //DLOAD_1
-1, //DLOAD_2
-1, //DLOAD_3
-1, //ALOAD_0
-1, //ALOAD_1
-1, //ALOAD_2
-1, //ALOAD_3
0, //IALOAD
0, //LALOAD
0, //FALOAD
0, //DALOAD
0, //AALOAD
0, //BALOAD
0, //CALOAD
0, //SALOAD
2, //ISTORE
2, //LSTORE
2, //FSTORE
2, //DSTORE
2, //ASTORE
-1, //ISTORE_0
-1, //ISTORE_1
-1, //ISTORE_2
-1, //ISTORE_3
-1, //LSTORE_0
-1, //LSTORE_1
-1, //LSTORE_2
-1, //LSTORE_3
-1, //FSTORE_0
-1, //FSTORE_1
-1, //FSTORE_2
-1, //FSTORE_3
-1, //DSTORE_0
-1, //DSTORE_1
-1, //DSTORE_2
-1, //DSTORE_3
-1, //ASTORE_0
-1, //ASTORE_1
-1, //ASTORE_2
-1, //ASTORE_3
0, //IASTORE
0, //LASTORE
0, //FASTORE
0, //DASTORE
0, //AASTORE
0, //BASTORE
0, //CASTORE
0, //SASTORE
0, //POP
0, //POP2
0, //DUP
0, //DUP_X1
0, //DUP_X2
0, //DUP2
0, //DUP2_X1
0, //DUP2_X2
0, //SWAP
0, //IADD
0, //LADD
0, //FADD
0, //DADD
0, //ISUB
0, //LSUB
0, //FSUB
0, //DSUB
0, //IMUL
0, //LMUL
0, //FMUL
0, //DMUL
0, //IDIV
0, //LDIV
0, //FDIV
0, //DDIV
0, //IREM
0, //LREM
0, //FREM
0, //DREM
0, //INEG
0, //LNEG
0, //FNEG
0, //DNEG
0, //ISHL
0, //LSHL
0, //ISHR
0, //LSHR
0, //IUSHR
0, //LUSHR
0, //IAND
0, //LAND
0, //IOR
0, //LOR
0, //IXOR
0, //LXOR
8, //IINC
0, //I2L
0, //I2F
0, //I2D
0, //L2I
0, //L2F
0, //L2D
0, //F2I
0, //F2L
0, //F2D
0, //D2I
0, //D2L
0, //D2F
0, //I2B
0, //I2C
0, //I2S
0, //LCMP
0, //FCMPL
0, //FCMPG
0, //DCMPL
0, //DCMPG
6, //IFEQ
6, //IFNE
6, //IFLT
6, //IFGE
6, //IFGT
6, //IFLE
6, //IF_ICMPEQ
6, //IF_ICMPNE
6, //IF_ICMPLT
6, //IF_ICMPGE
6, //IF_ICMPGT
6, //IF_ICMPLE
6, //IF_ACMPEQ
6, //IF_ACMPNE
6, //GOTO
6, //JSR
2, //RET
9, //TABLESWITCH
10, //LOOKUPSWITCH
0, //IRETURN
0, //LRETURN
0, //FRETURN
0, //DRETURN
0, //ARETURN
0, //RETURN
4, //GETSTATIC
4, //PUTSTATIC
4, //GETFIELD
4, //PUTFIELD
5, //INVOKEVIRTUAL
5, //INVOKESPECIAL
5, //INVOKESTATIC
5, //INVOKEINTERFACE
-1, //UNUSED
3, //NEW
1, //NEWARRAY
3, //ANEWARRAY
0, //ARRAYLENGTH
0, //ATHROW
3, //CHECKCAST
3, //INSTANCEOF
0, //MONITORENTER
0, //MONITOREXIT
-1, //WIDE
11, //MULTIANEWARRAY
6, //IFNULL
6, //IFNONNULL
-1, //GOTO_W
-1 //JSR_W
};
/**
* Constructs a new {@link CheckCodeAdapter CheckCodeAdapter} object.
*
* @param cv the code visitor to which this adapter must delegate calls.
*/
public CheckCodeAdapter (final CodeVisitor cv) {
super(cv);
this.labels = new HashMap();
}
public void visitInsn (final int opcode) {
checkEnd();
checkOpcode(opcode, 0);
cv.visitInsn(opcode);
}
public void visitIntInsn (final int opcode, final int operand) {
checkEnd();
checkOpcode(opcode, 1);
switch (opcode) {
case Constants.BIPUSH:
checkSignedByte(operand, "Invalid operand");
break;
case Constants.SIPUSH:
checkSignedShort(operand, "Invalid operand");
break;
//case Constants.NEWARRAY:
default:
if (operand < Constants.T_BOOLEAN || operand > Constants.T_LONG) {
throw new IllegalArgumentException(
"Invalid operand (must be an array type code T_...): " + operand);
}
}
cv.visitIntInsn(opcode, operand);
}
public void visitVarInsn (final int opcode, final int var) {
checkEnd();
checkOpcode(opcode, 2);
checkUnsignedShort(var, "Invalid variable index");
cv.visitVarInsn(opcode, var);
}
public void visitTypeInsn (final int opcode, final String desc) {
checkEnd();
checkOpcode(opcode, 3);
if (desc != null && desc.length() > 0 && desc.charAt(0) == '[') {
checkDesc(desc, false);
} else {
checkInternalName(desc, "type");
}
if (opcode == Constants.NEW && desc.charAt(0) == '[') {
throw new IllegalArgumentException(
"NEW cannot be used to create arrays: " + desc);
}
cv.visitTypeInsn(opcode, desc);
}
public void visitFieldInsn (
final int opcode,
final String owner,
final String name,
final String desc)
{
checkEnd();
checkOpcode(opcode, 4);
checkInternalName(owner, "owner");
checkIdentifier(name, "name");
checkDesc(desc, false);
cv.visitFieldInsn(opcode, owner, name, desc);
}
public void visitMethodInsn (
final int opcode,
final String owner,
final String name,
final String desc)
{
checkEnd();
checkOpcode(opcode, 5);
checkInternalName(owner, "owner");
checkMethodIdentifier(name, "name");
checkMethodDesc(desc);
cv.visitMethodInsn(opcode, owner, name, desc);
}
public void visitJumpInsn (final int opcode, final Label label) {
checkEnd();
checkOpcode(opcode, 6);
checkLabel(label, false, "label");
cv.visitJumpInsn(opcode, label);
}
public void visitLabel (final Label label) {
checkEnd();
checkLabel(label, false, "label");
if (labels.get(label) != null) {
throw new IllegalArgumentException("Already visited label");
} else {
labels.put(label, new Integer(labels.size()));
}
cv.visitLabel(label);
}
public void visitLdcInsn (final Object cst) {
checkEnd();
if (!(cst instanceof Type)) {
checkConstant(cst);
}
cv.visitLdcInsn(cst);
}
public void visitIincInsn (final int var, final int increment) {
checkEnd();
checkUnsignedShort(var, "Invalid variable index");
checkSignedShort(increment, "Invalid increment");
cv.visitIincInsn(var, increment);
}
public void visitTableSwitchInsn (
final int min,
final int max,
final Label dflt,
final Label labels[])
{
checkEnd();
if (max < min) {
throw new IllegalArgumentException(
"Max = " + max + " must be greater than or equal to min = " + min);
}
checkLabel(dflt, false, "default label");
if (labels == null || labels.length != max - min + 1) {
throw new IllegalArgumentException(
"There must be max - min + 1 labels");
}
for (int i = 0; i < labels.length; ++i) {
checkLabel(labels[i], false, "label at index " + i);
}
cv.visitTableSwitchInsn(min, max, dflt, labels);
}
public void visitLookupSwitchInsn (
final Label dflt,
final int keys[],
final Label labels[])
{
checkEnd();
checkLabel(dflt, false, "default label");
if (keys == null || labels == null || keys.length != labels.length) {
throw new IllegalArgumentException(
"There must be the same number of keys and labels");
}
for (int i = 0; i < labels.length; ++i) {
checkLabel(labels[i], false, "label at index " + i);
}
cv.visitLookupSwitchInsn(dflt, keys, labels);
}
public void visitMultiANewArrayInsn (final String desc, final int dims) {
checkEnd();
checkDesc(desc, false);
if (desc.charAt(0) != '[') {
throw new IllegalArgumentException(
"Invalid descriptor (must be an array type descriptor): " + desc);
}
if (dims < 1) {
throw new IllegalArgumentException(
"Invalid dimensions (must be greater than 0): " + dims);
}
if (dims > desc.lastIndexOf('[') + 1) {
throw new IllegalArgumentException(
"Invalid dimensions (must not be greater than dims(desc)): " + dims);
}
cv.visitMultiANewArrayInsn(desc, dims);
}
public void visitTryCatchBlock (
final Label start,
final Label end,
final Label handler,
final String type)
{
checkLabel(start, true, "start label");
checkLabel(end, true, "end label");
checkLabel(handler, true, "handler label");
if (type != null) {
checkInternalName(type, "type");
}
int s = ((Integer)labels.get(start)).intValue();
int e = ((Integer)labels.get(end)).intValue();
if (e <= s) {
throw new IllegalArgumentException(
"Invalid start and end labels (end must be greater than start)");
}
cv.visitTryCatchBlock(start, end, handler, type);
}
public void visitMaxs (final int maxStack, final int maxLocals) {
checkEnd();
end = true;
checkUnsignedShort(maxStack, "Invalid max stack");
checkUnsignedShort(maxLocals, "Invalid max locals");
cv.visitMaxs(maxStack, maxLocals);
}
public void visitLocalVariable (
final String name,
final String desc,
final Label start,
final Label end,
final int index)
{
checkIdentifier(name, "name");
checkDesc(desc, false);
checkLabel(start, true, "start label");
checkLabel(end, true, "end label");
checkUnsignedShort(index, "Invalid variable index");
int s = ((Integer)labels.get(start)).intValue();
int e = ((Integer)labels.get(end)).intValue();
if (e <= s) {
throw new IllegalArgumentException(
"Invalid start and end labels (end must be greater than start)");
}
cv.visitLocalVariable(name, desc, start, end, index);
}
public void visitLineNumber (final int line, final Label start) {
checkUnsignedShort(line, "Invalid line number");
checkLabel(start, true, "start label");
cv.visitLineNumber(line, start);
}
public void visitAttribute (Attribute attr) {
if (attr == null) {
throw new IllegalArgumentException(
"Invalid attribute (must not be null)");
}
}
// ---------------------------------------------------------------------------
/**
* Checks that the visitMaxs method has not been called.
*/
void checkEnd () {
if (end) {
throw new IllegalStateException(
"Cannot visit instructions after visitMaxs has been called.");
}
}
/**
* Checks that the type of the given opcode is equal to the given type.
*
* @param opcode the opcode to be checked.
* @param type the expected opcode type.
*/
static void checkOpcode (final int opcode, final int type) {
if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) {
throw new IllegalArgumentException("Invalid opcode: " + opcode);
}
}
/**
* Checks that the given value is a signed byte.
*
* @param value the value to be checked.
* @param msg an message to be used in case of error.
*/
static void checkSignedByte (final int value, final String msg) {
if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
throw new IllegalArgumentException(
msg + " (must be a signed byte): " + value);
}
}
/**
* Checks that the given value is a signed short.
*
* @param value the value to be checked.
* @param msg an message to be used in case of error.
*/
static void checkSignedShort (final int value, final String msg) {
if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
throw new IllegalArgumentException(
msg + " (must be a signed short): " + value);
}
}
/**
* Checks that the given value is an unsigned short.
*
* @param value the value to be checked.
* @param msg an message to be used in case of error.
*/
static void checkUnsignedShort (final int value, final String msg) {
if (value < 0 || value > 65535) {
throw new IllegalArgumentException(
msg + " (must be an unsigned short): " + value);
}
}
/**
* Checks that the given value is an {@link java.lang.Integer Integer}, a
* {@link java.lang.Float Float}, a {@link java.lang.Long Long}, a {@link
* java.lang.Double Double} or a {@link String String}.
*
* @param cst the value to be checked.
*/
static void checkConstant (final Object cst) {
if (!(cst instanceof Integer) &&
!(cst instanceof Float) &&
!(cst instanceof Long) &&
!(cst instanceof Double) &&
!(cst instanceof String))
{
throw new IllegalArgumentException("Invalid constant: " + cst);
}
}
/**
* Checks that the given string is a valid Java identifier.
*
* @param name the string to be checked.
* @param msg a message to be used in case of error.
*/
static void checkIdentifier (final String name, final String msg) {
checkIdentifier(name, 0, -1, msg);
}
/**
* Checks that the given substring is a valid Java identifier.
*
* @param name the string to be checked.
* @param start index of the first character of the identifier (inclusive).
* @param end index of the last character of the identifier (exclusive). -1 is
* equivalent to name.length() if name is not null.
* @param msg a message to be used in case of error.
*/
static void checkIdentifier (
final String name,
final int start,
final int end,
final String msg)
{
if (name == null || (end == -1 ? name.length() <= start : end <= start)) {
throw new IllegalArgumentException(
"Invalid " + msg + " (must not be null or empty)");
}
if (!Character.isJavaIdentifierStart(name.charAt(start))) {
throw new IllegalArgumentException(
"Invalid " + msg + " (must be a valid Java identifier): " + name);
}
int max = (end == -1 ? name.length() : end);
for (int i = start + 1; i < max; ++i) {
if (!Character.isJavaIdentifierPart(name.charAt(i))) {
throw new IllegalArgumentException(
"Invalid " + msg + " (must be a valid Java identifier): " + name);
}
}
}
/**
* Checks that the given string is a valid Java identifier or is equal to
* '<init>' or '<clinit>'.
*
* @param name the string to be checked.
* @param msg a message to be used in case of error.
*/
static void checkMethodIdentifier (final String name, final String msg) {
if (name == null || name.length() == 0) {
throw new IllegalArgumentException(
"Invalid " + msg + " (must not be null or empty)");
}
if (name.equals("") || name.equals("")) {
return;
}
if (!Character.isJavaIdentifierStart(name.charAt(0))) {
throw new IllegalArgumentException(
"Invalid " + msg +
" (must be a '', '' or a valid Java identifier): " +
name);
}
for (int i = 1; i < name.length(); ++i) {
if (!Character.isJavaIdentifierPart(name.charAt(i))) {
throw new IllegalArgumentException(
"Invalid " + msg +
" (must be '' or '' or a valid Java identifier): " +
name);
}
}
}
/**
* Checks that the given string is a valid internal class name.
*
* @param name the string to be checked.
* @param msg a message to be used in case of error.
*/
static void checkInternalName (final String name, final String msg) {
checkInternalName(name, 0, -1, msg);
}
/**
* Checks that the given substring is a valid internal class name.
*
* @param name the string to be checked.
* @param start index of the first character of the identifier (inclusive).
* @param end index of the last character of the identifier (exclusive). -1 is
* equivalent to name.length() if name is not null.
* @param msg a message to be used in case of error.
*/
static void checkInternalName (
final String name,
final int start,
final int end,
final String msg)
{
if (name == null || name.length() == 0) {
throw new IllegalArgumentException(
"Invalid " + msg + " (must not be null or empty)");
}
int max = (end == -1 ? name.length() : end);
try {
int begin = start;
int slash;
do {
slash = name.indexOf('/', begin + 1);
if (slash == -1 || slash > max) {
slash = max;
}
checkIdentifier(name, begin, slash, null);
begin = slash + 1;
} while (slash != max);
} catch (IllegalArgumentException _) {
throw new IllegalArgumentException(
"Invalid " + msg +
" (must be a fully qualified class name in internal form): " +
name);
}
}
/**
* Checks that the given string is a valid type descriptor.
*
* @param desc the string to be checked.
* @param canBeVoid true if V can be considered valid.
*/
static void checkDesc (final String desc, final boolean canBeVoid) {
int end = checkDesc(desc, 0, canBeVoid);
if (end != desc.length()) {
throw new IllegalArgumentException("Invalid descriptor: " + desc);
}
}
/**
* Checks that a the given substring is a valid type descriptor.
*
* @param desc the string to be checked.
* @param start index of the first character of the identifier (inclusive).
* @param canBeVoid true if V can be considered valid.
* @return the index of the last character of the type decriptor, plus one.
*/
static int checkDesc (
final String desc,
final int start,
final boolean canBeVoid)
{
if (desc == null || start >= desc.length()) {
throw new IllegalArgumentException(
"Invalid type descriptor (must not be null or empty)");
}
int index;
switch (desc.charAt(start)) {
case 'V':
if (canBeVoid) {
return start + 1;
} else {
throw new IllegalArgumentException("Invalid descriptor: " + desc);
}
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
case 'F':
case 'J':
case 'D':
return start + 1;
case '[':
index = start + 1;
while (index < desc.length() && desc.charAt(index) == '[') {
++index;
}
if (index < desc.length()) {
return checkDesc(desc, index, false);
} else {
throw new IllegalArgumentException("Invalid descriptor: " + desc);
}
case 'L':
index = desc.indexOf(';', start);
if (index == -1 || index - start < 2) {
throw new IllegalArgumentException("Invalid descriptor: " + desc);
}
try {
checkInternalName(desc, start + 1, index, null);
} catch (IllegalArgumentException _) {
throw new IllegalArgumentException("Invalid descriptor: " + desc);
}
return index + 1;
default:
throw new IllegalArgumentException("Invalid descriptor: " + desc);
}
}
/**
* Checks that the given string is a valid method descriptor.
*
* @param desc the string to be checked.
*/
static void checkMethodDesc (final String desc) {
if (desc == null || desc.length() == 0) {
throw new IllegalArgumentException(
"Invalid method descriptor (must not be null or empty)");
}
if (desc.charAt(0) != '(' || desc.length() < 3) {
throw new IllegalArgumentException("Invalid descriptor: " + desc);
}
int start = 1;
if (desc.charAt(start) != ')') {
do {
if (desc.charAt(start) == 'V') {
throw new IllegalArgumentException("Invalid descriptor: " + desc);
}
start = checkDesc(desc, start, false);
} while (start < desc.length() && desc.charAt(start) != ')');
}
start = checkDesc(desc, start + 1, true);
if (start != desc.length()) {
throw new IllegalArgumentException("Invalid descriptor: " + desc);
}
}
/**
* Checks that the given label is not null. This method can also check that
* the label has been visited.
*
* @param label the label to be checked.
* @param checkVisited true to check that the label has been visited.
* @param msg a message to be used in case of error.
*/
void checkLabel (
final Label label,
final boolean checkVisited,
final String msg)
{
if (label == null) {
throw new IllegalArgumentException(
"Invalid " + msg + " (must not be null)");
}
if (checkVisited && labels.get(label) == null) {
throw new IllegalArgumentException(
"Invalid " + msg + " (must be visited first)");
}
}
}