|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package jdk.internal.org.objectweb.asm.util; |
|
|
|
import java.io.PrintWriter; |
|
import java.io.StringWriter; |
|
import java.util.ArrayList; |
|
import java.util.Collections; |
|
import java.util.HashMap; |
|
import java.util.HashSet; |
|
import java.util.List; |
|
import java.util.Map; |
|
import java.util.Set; |
|
import jdk.internal.org.objectweb.asm.AnnotationVisitor; |
|
import jdk.internal.org.objectweb.asm.Attribute; |
|
import jdk.internal.org.objectweb.asm.ConstantDynamic; |
|
import jdk.internal.org.objectweb.asm.Handle; |
|
import jdk.internal.org.objectweb.asm.Label; |
|
import jdk.internal.org.objectweb.asm.MethodVisitor; |
|
import jdk.internal.org.objectweb.asm.Opcodes; |
|
import jdk.internal.org.objectweb.asm.Type; |
|
import jdk.internal.org.objectweb.asm.TypePath; |
|
import jdk.internal.org.objectweb.asm.TypeReference; |
|
import jdk.internal.org.objectweb.asm.tree.MethodNode; |
|
import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer; |
|
import jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException; |
|
import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue; |
|
import jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class CheckMethodAdapter extends MethodVisitor { |
|
|
|
|
|
private enum Method { |
|
VISIT_INSN, |
|
VISIT_INT_INSN, |
|
VISIT_VAR_INSN, |
|
VISIT_TYPE_INSN, |
|
VISIT_FIELD_INSN, |
|
VISIT_METHOD_INSN, |
|
VISIT_JUMP_INSN |
|
} |
|
|
|
|
|
private static final Method[] OPCODE_METHODS = { |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INT_INSN, |
|
Method.VISIT_INT_INSN, |
|
null, |
|
null, |
|
null, |
|
Method.VISIT_VAR_INSN, |
|
Method.VISIT_VAR_INSN, |
|
Method.VISIT_VAR_INSN, |
|
Method.VISIT_VAR_INSN, |
|
Method.VISIT_VAR_INSN, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_VAR_INSN, |
|
Method.VISIT_VAR_INSN, |
|
Method.VISIT_VAR_INSN, |
|
Method.VISIT_VAR_INSN, |
|
Method.VISIT_VAR_INSN, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
null, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_VAR_INSN, |
|
null, |
|
null, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_FIELD_INSN, |
|
Method.VISIT_FIELD_INSN, |
|
Method.VISIT_FIELD_INSN, |
|
Method.VISIT_FIELD_INSN, |
|
Method.VISIT_METHOD_INSN, |
|
Method.VISIT_METHOD_INSN, |
|
Method.VISIT_METHOD_INSN, |
|
Method.VISIT_METHOD_INSN, |
|
null, |
|
Method.VISIT_TYPE_INSN, |
|
Method.VISIT_INT_INSN, |
|
Method.VISIT_TYPE_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_TYPE_INSN, |
|
Method.VISIT_TYPE_INSN, |
|
Method.VISIT_INSN, |
|
Method.VISIT_INSN, |
|
null, |
|
null, |
|
Method.VISIT_JUMP_INSN, |
|
Method.VISIT_JUMP_INSN |
|
}; |
|
|
|
private static final String INVALID = "Invalid "; |
|
private static final String INVALID_DESCRIPTOR = "Invalid descriptor: "; |
|
private static final String INVALID_TYPE_REFERENCE = "Invalid type reference sort 0x"; |
|
private static final String INVALID_LOCAL_VARIABLE_INDEX = "Invalid local variable index"; |
|
private static final String MUST_NOT_BE_NULL_OR_EMPTY = " (must not be null or empty)"; |
|
private static final String START_LABEL = "start label"; |
|
private static final String END_LABEL = "end label"; |
|
|
|
|
|
public int version; |
|
|
|
|
|
private int access; |
|
|
|
|
|
|
|
|
|
*/ |
|
private int visibleAnnotableParameterCount; |
|
|
|
|
|
|
|
|
|
*/ |
|
private int invisibleAnnotableParameterCount; |
|
|
|
|
|
private boolean visitCodeCalled; |
|
|
|
|
|
private boolean visitMaxCalled; |
|
|
|
|
|
private boolean visitEndCalled; |
|
|
|
|
|
private int insnCount; |
|
|
|
|
|
private final Map<Label, Integer> labelInsnIndices; |
|
|
|
|
|
private Set<Label> referencedLabels; |
|
|
|
|
|
private int lastFrameInsnIndex = -1; |
|
|
|
|
|
private int numExpandedFrames; |
|
|
|
|
|
private int numCompressedFrames; |
|
|
|
|
|
|
|
|
|
*/ |
|
private List<Label> handlers; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public CheckMethodAdapter(final MethodVisitor methodvisitor) { |
|
this(methodvisitor, new HashMap<Label, Integer>()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public CheckMethodAdapter( |
|
final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices) { |
|
this( Opcodes.ASM8, methodVisitor, labelInsnIndices); |
|
if (getClass() != CheckMethodAdapter.class) { |
|
throw new IllegalStateException(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected CheckMethodAdapter( |
|
final int api, |
|
final MethodVisitor methodVisitor, |
|
final Map<Label, Integer> labelInsnIndices) { |
|
super(api, methodVisitor); |
|
this.labelInsnIndices = labelInsnIndices; |
|
this.referencedLabels = new HashSet<>(); |
|
this.handlers = new ArrayList<>(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public CheckMethodAdapter( |
|
final int access, |
|
final String name, |
|
final String descriptor, |
|
final MethodVisitor methodVisitor, |
|
final Map<Label, Integer> labelInsnIndices) { |
|
this( |
|
Opcodes.ASM8, access, name, descriptor, methodVisitor, labelInsnIndices); |
|
if (getClass() != CheckMethodAdapter.class) { |
|
throw new IllegalStateException(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected CheckMethodAdapter( |
|
final int api, |
|
final int access, |
|
final String name, |
|
final String descriptor, |
|
final MethodVisitor methodVisitor, |
|
final Map<Label, Integer> labelInsnIndices) { |
|
this( |
|
api, |
|
new MethodNode(api, access, name, descriptor, null, null) { |
|
@Override |
|
public void visitEnd() { |
|
Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicVerifier()); |
|
try { |
|
analyzer.analyze("dummy", this); |
|
} catch (IndexOutOfBoundsException e) { |
|
if (maxLocals == 0 && maxStack == 0) { |
|
throw new IllegalArgumentException( |
|
"Data flow checking option requires valid, non zero maxLocals and maxStack.", |
|
e); |
|
} |
|
throwError(analyzer, e); |
|
} catch (AnalyzerException e) { |
|
throwError(analyzer, e); |
|
} |
|
if (methodVisitor != null) { |
|
accept(methodVisitor); |
|
} |
|
} |
|
|
|
private void throwError(final Analyzer<BasicValue> analyzer, final Exception e) { |
|
StringWriter stringWriter = new StringWriter(); |
|
PrintWriter printWriter = new PrintWriter(stringWriter, true); |
|
CheckClassAdapter.printAnalyzerResult(this, analyzer, printWriter); |
|
printWriter.close(); |
|
throw new IllegalArgumentException(e.getMessage() + ' ' + stringWriter.toString(), e); |
|
} |
|
}, |
|
labelInsnIndices); |
|
this.access = access; |
|
} |
|
|
|
@Override |
|
public void visitParameter(final String name, final int access) { |
|
if (name != null) { |
|
checkUnqualifiedName(version, name, "name"); |
|
} |
|
CheckClassAdapter.checkAccess( |
|
access, Opcodes.ACC_FINAL + Opcodes.ACC_MANDATED + Opcodes.ACC_SYNTHETIC); |
|
super.visitParameter(name, access); |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { |
|
checkVisitEndNotCalled(); |
|
checkDescriptor(version, descriptor, false); |
|
return new CheckAnnotationAdapter(super.visitAnnotation(descriptor, visible)); |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitTypeAnnotation( |
|
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { |
|
checkVisitEndNotCalled(); |
|
int sort = new TypeReference(typeRef).getSort(); |
|
if (sort != TypeReference.METHOD_TYPE_PARAMETER |
|
&& sort != TypeReference.METHOD_TYPE_PARAMETER_BOUND |
|
&& sort != TypeReference.METHOD_RETURN |
|
&& sort != TypeReference.METHOD_RECEIVER |
|
&& sort != TypeReference.METHOD_FORMAL_PARAMETER |
|
&& sort != TypeReference.THROWS) { |
|
throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort)); |
|
} |
|
CheckClassAdapter.checkTypeRef(typeRef); |
|
CheckMethodAdapter.checkDescriptor(version, descriptor, false); |
|
return new CheckAnnotationAdapter( |
|
super.visitTypeAnnotation(typeRef, typePath, descriptor, visible)); |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitAnnotationDefault() { |
|
checkVisitEndNotCalled(); |
|
return new CheckAnnotationAdapter(super.visitAnnotationDefault(), false); |
|
} |
|
|
|
@Override |
|
public void visitAnnotableParameterCount(final int parameterCount, final boolean visible) { |
|
checkVisitEndNotCalled(); |
|
if (visible) { |
|
visibleAnnotableParameterCount = parameterCount; |
|
} else { |
|
invisibleAnnotableParameterCount = parameterCount; |
|
} |
|
super.visitAnnotableParameterCount(parameterCount, visible); |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitParameterAnnotation( |
|
final int parameter, final String descriptor, final boolean visible) { |
|
checkVisitEndNotCalled(); |
|
if ((visible |
|
&& visibleAnnotableParameterCount > 0 |
|
&& parameter >= visibleAnnotableParameterCount) |
|
|| (!visible |
|
&& invisibleAnnotableParameterCount > 0 |
|
&& parameter >= invisibleAnnotableParameterCount)) { |
|
throw new IllegalArgumentException("Invalid parameter index"); |
|
} |
|
checkDescriptor(version, descriptor, false); |
|
return new CheckAnnotationAdapter( |
|
super.visitParameterAnnotation(parameter, descriptor, visible)); |
|
} |
|
|
|
@Override |
|
public void visitAttribute(final Attribute attribute) { |
|
checkVisitEndNotCalled(); |
|
if (attribute == null) { |
|
throw new IllegalArgumentException("Invalid attribute (must not be null)"); |
|
} |
|
super.visitAttribute(attribute); |
|
} |
|
|
|
@Override |
|
public void visitCode() { |
|
if ((access & Opcodes.ACC_ABSTRACT) != 0) { |
|
throw new UnsupportedOperationException("Abstract methods cannot have code"); |
|
} |
|
visitCodeCalled = true; |
|
super.visitCode(); |
|
} |
|
|
|
@Override |
|
public void visitFrame( |
|
final int type, |
|
final int numLocal, |
|
final Object[] local, |
|
final int numStack, |
|
final Object[] stack) { |
|
if (insnCount == lastFrameInsnIndex) { |
|
throw new IllegalStateException("At most one frame can be visited at a given code location."); |
|
} |
|
lastFrameInsnIndex = insnCount; |
|
int maxNumLocal; |
|
int maxNumStack; |
|
switch (type) { |
|
case Opcodes.F_NEW: |
|
case Opcodes.F_FULL: |
|
maxNumLocal = Integer.MAX_VALUE; |
|
maxNumStack = Integer.MAX_VALUE; |
|
break; |
|
|
|
case Opcodes.F_SAME: |
|
maxNumLocal = 0; |
|
maxNumStack = 0; |
|
break; |
|
|
|
case Opcodes.F_SAME1: |
|
maxNumLocal = 0; |
|
maxNumStack = 1; |
|
break; |
|
|
|
case Opcodes.F_APPEND: |
|
case Opcodes.F_CHOP: |
|
maxNumLocal = 3; |
|
maxNumStack = 0; |
|
break; |
|
|
|
default: |
|
throw new IllegalArgumentException("Invalid frame type " + type); |
|
} |
|
|
|
if (numLocal > maxNumLocal) { |
|
throw new IllegalArgumentException( |
|
"Invalid numLocal=" + numLocal + " for frame type " + type); |
|
} |
|
if (numStack > maxNumStack) { |
|
throw new IllegalArgumentException( |
|
"Invalid numStack=" + numStack + " for frame type " + type); |
|
} |
|
|
|
if (type != Opcodes.F_CHOP) { |
|
if (numLocal > 0 && (local == null || local.length < numLocal)) { |
|
throw new IllegalArgumentException("Array local[] is shorter than numLocal"); |
|
} |
|
for (int i = 0; i < numLocal; ++i) { |
|
checkFrameValue(local[i]); |
|
} |
|
} |
|
if (numStack > 0 && (stack == null || stack.length < numStack)) { |
|
throw new IllegalArgumentException("Array stack[] is shorter than numStack"); |
|
} |
|
for (int i = 0; i < numStack; ++i) { |
|
checkFrameValue(stack[i]); |
|
} |
|
if (type == Opcodes.F_NEW) { |
|
++numExpandedFrames; |
|
} else { |
|
++numCompressedFrames; |
|
} |
|
if (numExpandedFrames > 0 && numCompressedFrames > 0) { |
|
throw new IllegalArgumentException("Expanded and compressed frames must not be mixed."); |
|
} |
|
super.visitFrame(type, numLocal, local, numStack, stack); |
|
} |
|
|
|
@Override |
|
public void visitInsn(final int opcode) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkOpcodeMethod(opcode, Method.VISIT_INSN); |
|
super.visitInsn(opcode); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public void visitIntInsn(final int opcode, final int operand) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkOpcodeMethod(opcode, Method.VISIT_INT_INSN); |
|
switch (opcode) { |
|
case Opcodes.BIPUSH: |
|
checkSignedByte(operand, "Invalid operand"); |
|
break; |
|
case Opcodes.SIPUSH: |
|
checkSignedShort(operand, "Invalid operand"); |
|
break; |
|
case Opcodes.NEWARRAY: |
|
if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) { |
|
throw new IllegalArgumentException( |
|
"Invalid operand (must be an array type code T_...): " + operand); |
|
} |
|
break; |
|
default: |
|
throw new AssertionError(); |
|
} |
|
super.visitIntInsn(opcode, operand); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public void visitVarInsn(final int opcode, final int var) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkOpcodeMethod(opcode, Method.VISIT_VAR_INSN); |
|
checkUnsignedShort(var, INVALID_LOCAL_VARIABLE_INDEX); |
|
super.visitVarInsn(opcode, var); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public void visitTypeInsn(final int opcode, final String type) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkOpcodeMethod(opcode, Method.VISIT_TYPE_INSN); |
|
checkInternalName(version, type, "type"); |
|
if (opcode == Opcodes.NEW && type.charAt(0) == '[') { |
|
throw new IllegalArgumentException("NEW cannot be used to create arrays: " + type); |
|
} |
|
super.visitTypeInsn(opcode, type); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public void visitFieldInsn( |
|
final int opcode, final String owner, final String name, final String descriptor) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkOpcodeMethod(opcode, Method.VISIT_FIELD_INSN); |
|
checkInternalName(version, owner, "owner"); |
|
checkUnqualifiedName(version, name, "name"); |
|
checkDescriptor(version, descriptor, false); |
|
super.visitFieldInsn(opcode, owner, name, descriptor); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public void visitMethodInsn( |
|
final int opcodeAndSource, |
|
final String owner, |
|
final String name, |
|
final String descriptor, |
|
final boolean isInterface) { |
|
if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) { |
|
|
|
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface); |
|
return; |
|
} |
|
int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK; |
|
|
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkOpcodeMethod(opcode, Method.VISIT_METHOD_INSN); |
|
if (opcode != Opcodes.INVOKESPECIAL || !"<init>".equals(name)) { |
|
checkMethodIdentifier(version, name, "name"); |
|
} |
|
checkInternalName(version, owner, "owner"); |
|
checkMethodDescriptor(version, descriptor); |
|
if (opcode == Opcodes.INVOKEVIRTUAL && isInterface) { |
|
throw new IllegalArgumentException("INVOKEVIRTUAL can't be used with interfaces"); |
|
} |
|
if (opcode == Opcodes.INVOKEINTERFACE && !isInterface) { |
|
throw new IllegalArgumentException("INVOKEINTERFACE can't be used with classes"); |
|
} |
|
if (opcode == Opcodes.INVOKESPECIAL && isInterface && (version & 0xFFFF) < Opcodes.V1_8) { |
|
throw new IllegalArgumentException( |
|
"INVOKESPECIAL can't be used with interfaces prior to Java 8"); |
|
} |
|
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public void visitInvokeDynamicInsn( |
|
final String name, |
|
final String descriptor, |
|
final Handle bootstrapMethodHandle, |
|
final Object... bootstrapMethodArguments) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkMethodIdentifier(version, name, "name"); |
|
checkMethodDescriptor(version, descriptor); |
|
if (bootstrapMethodHandle.getTag() != Opcodes.H_INVOKESTATIC |
|
&& bootstrapMethodHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) { |
|
throw new IllegalArgumentException("invalid handle tag " + bootstrapMethodHandle.getTag()); |
|
} |
|
for (Object bootstrapMethodArgument : bootstrapMethodArguments) { |
|
checkLdcConstant(bootstrapMethodArgument); |
|
} |
|
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public void visitJumpInsn(final int opcode, final Label label) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkOpcodeMethod(opcode, Method.VISIT_JUMP_INSN); |
|
checkLabel(label, false, "label"); |
|
super.visitJumpInsn(opcode, label); |
|
referencedLabels.add(label); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public void visitLabel(final Label label) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkLabel(label, false, "label"); |
|
if (labelInsnIndices.get(label) != null) { |
|
throw new IllegalArgumentException("Already visited label"); |
|
} |
|
labelInsnIndices.put(label, insnCount); |
|
super.visitLabel(label); |
|
} |
|
|
|
@Override |
|
public void visitLdcInsn(final Object value) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkLdcConstant(value); |
|
super.visitLdcInsn(value); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public void visitIincInsn(final int var, final int increment) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkUnsignedShort(var, INVALID_LOCAL_VARIABLE_INDEX); |
|
checkSignedShort(increment, "Invalid increment"); |
|
super.visitIincInsn(var, increment); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public void visitTableSwitchInsn( |
|
final int min, final int max, final Label dflt, final Label... labels) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
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); |
|
} |
|
super.visitTableSwitchInsn(min, max, dflt, labels); |
|
Collections.addAll(referencedLabels, labels); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { |
|
checkVisitMaxsNotCalled(); |
|
checkVisitCodeCalled(); |
|
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); |
|
} |
|
super.visitLookupSwitchInsn(dflt, keys, labels); |
|
referencedLabels.add(dflt); |
|
Collections.addAll(referencedLabels, labels); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkDescriptor(version, descriptor, false); |
|
if (descriptor.charAt(0) != '[') { |
|
throw new IllegalArgumentException( |
|
"Invalid descriptor (must be an array type descriptor): " + descriptor); |
|
} |
|
if (numDimensions < 1) { |
|
throw new IllegalArgumentException( |
|
"Invalid dimensions (must be greater than 0): " + numDimensions); |
|
} |
|
if (numDimensions > descriptor.lastIndexOf('[') + 1) { |
|
throw new IllegalArgumentException( |
|
"Invalid dimensions (must not be greater than numDimensions(descriptor)): " |
|
+ numDimensions); |
|
} |
|
super.visitMultiANewArrayInsn(descriptor, numDimensions); |
|
++insnCount; |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitInsnAnnotation( |
|
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
int sort = new TypeReference(typeRef).getSort(); |
|
if (sort != TypeReference.INSTANCEOF |
|
&& sort != TypeReference.NEW |
|
&& sort != TypeReference.CONSTRUCTOR_REFERENCE |
|
&& sort != TypeReference.METHOD_REFERENCE |
|
&& sort != TypeReference.CAST |
|
&& sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT |
|
&& sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT |
|
&& sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT |
|
&& sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) { |
|
throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort)); |
|
} |
|
CheckClassAdapter.checkTypeRef(typeRef); |
|
CheckMethodAdapter.checkDescriptor(version, descriptor, false); |
|
return new CheckAnnotationAdapter( |
|
super.visitInsnAnnotation(typeRef, typePath, descriptor, visible)); |
|
} |
|
|
|
@Override |
|
public void visitTryCatchBlock( |
|
final Label start, final Label end, final Label handler, final String type) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkLabel(start, false, START_LABEL); |
|
checkLabel(end, false, END_LABEL); |
|
checkLabel(handler, false, "handler label"); |
|
if (labelInsnIndices.get(start) != null |
|
|| labelInsnIndices.get(end) != null |
|
|| labelInsnIndices.get(handler) != null) { |
|
throw new IllegalStateException("Try catch blocks must be visited before their labels"); |
|
} |
|
if (type != null) { |
|
checkInternalName(version, type, "type"); |
|
} |
|
super.visitTryCatchBlock(start, end, handler, type); |
|
handlers.add(start); |
|
handlers.add(end); |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitTryCatchAnnotation( |
|
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
int sort = new TypeReference(typeRef).getSort(); |
|
if (sort != TypeReference.EXCEPTION_PARAMETER) { |
|
throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort)); |
|
} |
|
CheckClassAdapter.checkTypeRef(typeRef); |
|
CheckMethodAdapter.checkDescriptor(version, descriptor, false); |
|
return new CheckAnnotationAdapter( |
|
super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible)); |
|
} |
|
|
|
@Override |
|
public void visitLocalVariable( |
|
final String name, |
|
final String descriptor, |
|
final String signature, |
|
final Label start, |
|
final Label end, |
|
final int index) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkUnqualifiedName(version, name, "name"); |
|
checkDescriptor(version, descriptor, false); |
|
if (signature != null) { |
|
CheckClassAdapter.checkFieldSignature(signature); |
|
} |
|
checkLabel(start, true, START_LABEL); |
|
checkLabel(end, true, END_LABEL); |
|
checkUnsignedShort(index, INVALID_LOCAL_VARIABLE_INDEX); |
|
int startInsnIndex = labelInsnIndices.get(start).intValue(); |
|
int endInsnIndex = labelInsnIndices.get(end).intValue(); |
|
if (endInsnIndex < startInsnIndex) { |
|
throw new IllegalArgumentException( |
|
"Invalid start and end labels (end must be greater than start)"); |
|
} |
|
super.visitLocalVariable(name, descriptor, signature, start, end, index); |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitLocalVariableAnnotation( |
|
final int typeRef, |
|
final TypePath typePath, |
|
final Label[] start, |
|
final Label[] end, |
|
final int[] index, |
|
final String descriptor, |
|
final boolean visible) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
int sort = new TypeReference(typeRef).getSort(); |
|
if (sort != TypeReference.LOCAL_VARIABLE && sort != TypeReference.RESOURCE_VARIABLE) { |
|
throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort)); |
|
} |
|
CheckClassAdapter.checkTypeRef(typeRef); |
|
checkDescriptor(version, descriptor, false); |
|
if (start == null |
|
|| end == null |
|
|| index == null |
|
|| end.length != start.length |
|
|| index.length != start.length) { |
|
throw new IllegalArgumentException( |
|
"Invalid start, end and index arrays (must be non null and of identical length"); |
|
} |
|
for (int i = 0; i < start.length; ++i) { |
|
checkLabel(start[i], true, START_LABEL); |
|
checkLabel(end[i], true, END_LABEL); |
|
checkUnsignedShort(index[i], INVALID_LOCAL_VARIABLE_INDEX); |
|
int startInsnIndex = labelInsnIndices.get(start[i]).intValue(); |
|
int endInsnIndex = labelInsnIndices.get(end[i]).intValue(); |
|
if (endInsnIndex < startInsnIndex) { |
|
throw new IllegalArgumentException( |
|
"Invalid start and end labels (end must be greater than start)"); |
|
} |
|
} |
|
return super.visitLocalVariableAnnotation( |
|
typeRef, typePath, start, end, index, descriptor, visible); |
|
} |
|
|
|
@Override |
|
public void visitLineNumber(final int line, final Label start) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
checkUnsignedShort(line, "Invalid line number"); |
|
checkLabel(start, true, START_LABEL); |
|
super.visitLineNumber(line, start); |
|
} |
|
|
|
@Override |
|
public void visitMaxs(final int maxStack, final int maxLocals) { |
|
checkVisitCodeCalled(); |
|
checkVisitMaxsNotCalled(); |
|
visitMaxCalled = true; |
|
for (Label l : referencedLabels) { |
|
if (labelInsnIndices.get(l) == null) { |
|
throw new IllegalStateException("Undefined label used"); |
|
} |
|
} |
|
for (int i = 0; i < handlers.size(); i += 2) { |
|
Integer startInsnIndex = labelInsnIndices.get(handlers.get(i)); |
|
Integer endInsnIndex = labelInsnIndices.get(handlers.get(i + 1)); |
|
if (startInsnIndex == null || endInsnIndex == null) { |
|
throw new IllegalStateException("Undefined try catch block labels"); |
|
} |
|
if (endInsnIndex.intValue() <= startInsnIndex.intValue()) { |
|
throw new IllegalStateException("Emty try catch block handler range"); |
|
} |
|
} |
|
checkUnsignedShort(maxStack, "Invalid max stack"); |
|
checkUnsignedShort(maxLocals, "Invalid max locals"); |
|
super.visitMaxs(maxStack, maxLocals); |
|
} |
|
|
|
@Override |
|
public void visitEnd() { |
|
checkVisitEndNotCalled(); |
|
visitEndCalled = true; |
|
super.visitEnd(); |
|
} |
|
|
|
// ----------------------------------------------------------------------------------------------- |
|
// Utility methods |
|
// ----------------------------------------------------------------------------------------------- |
|
|
|
|
|
private void checkVisitCodeCalled() { |
|
if (!visitCodeCalled) { |
|
throw new IllegalStateException( |
|
"Cannot visit instructions before visitCode has been called."); |
|
} |
|
} |
|
|
|
|
|
private void checkVisitMaxsNotCalled() { |
|
if (visitMaxCalled) { |
|
throw new IllegalStateException("Cannot visit instructions after visitMaxs has been called."); |
|
} |
|
} |
|
|
|
|
|
private void checkVisitEndNotCalled() { |
|
if (visitEndCalled) { |
|
throw new IllegalStateException("Cannot visit elements after visitEnd has been called."); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void checkFrameValue(final Object value) { |
|
if (value == Opcodes.TOP |
|
|| value == Opcodes.INTEGER |
|
|| value == Opcodes.FLOAT |
|
|| value == Opcodes.LONG |
|
|| value == Opcodes.DOUBLE |
|
|| value == Opcodes.NULL |
|
|| value == Opcodes.UNINITIALIZED_THIS) { |
|
return; |
|
} |
|
if (value instanceof String) { |
|
checkInternalName(version, (String) value, "Invalid stack frame value"); |
|
} else if (value instanceof Label) { |
|
referencedLabels.add((Label) value); |
|
} else { |
|
throw new IllegalArgumentException("Invalid stack frame value: " + value); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static void checkOpcodeMethod(final int opcode, final Method method) { |
|
if (opcode < Opcodes.NOP || opcode > Opcodes.IFNONNULL || OPCODE_METHODS[opcode] != method) { |
|
throw new IllegalArgumentException("Invalid opcode: " + opcode); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static void checkSignedByte(final int value, final String message) { |
|
if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { |
|
throw new IllegalArgumentException(message + " (must be a signed byte): " + value); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static void checkSignedShort(final int value, final String message) { |
|
if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { |
|
throw new IllegalArgumentException(message + " (must be a signed short): " + value); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static void checkUnsignedShort(final int value, final String message) { |
|
if (value < 0 || value > 65535) { |
|
throw new IllegalArgumentException(message + " (must be an unsigned short): " + value); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static void checkConstant(final Object value) { |
|
if (!(value instanceof Integer) |
|
&& !(value instanceof Float) |
|
&& !(value instanceof Long) |
|
&& !(value instanceof Double) |
|
&& !(value instanceof String)) { |
|
throw new IllegalArgumentException("Invalid constant: " + value); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void checkLdcConstant(final Object value) { |
|
if (value instanceof Type) { |
|
int sort = ((Type) value).getSort(); |
|
if (sort != Type.OBJECT && sort != Type.ARRAY && sort != Type.METHOD) { |
|
throw new IllegalArgumentException("Illegal LDC constant value"); |
|
} |
|
if (sort != Type.METHOD && (version & 0xFFFF) < Opcodes.V1_5) { |
|
throw new IllegalArgumentException("ldc of a constant class requires at least version 1.5"); |
|
} |
|
if (sort == Type.METHOD && (version & 0xFFFF) < Opcodes.V1_7) { |
|
throw new IllegalArgumentException("ldc of a method type requires at least version 1.7"); |
|
} |
|
} else if (value instanceof Handle) { |
|
if ((version & 0xFFFF) < Opcodes.V1_7) { |
|
throw new IllegalArgumentException("ldc of a Handle requires at least version 1.7"); |
|
} |
|
Handle handle = (Handle) value; |
|
int tag = handle.getTag(); |
|
if (tag < Opcodes.H_GETFIELD || tag > Opcodes.H_INVOKEINTERFACE) { |
|
throw new IllegalArgumentException("invalid handle tag " + tag); |
|
} |
|
checkInternalName(this.version, handle.getOwner(), "handle owner"); |
|
if (tag <= Opcodes.H_PUTSTATIC) { |
|
checkDescriptor(this.version, handle.getDesc(), false); |
|
} else { |
|
checkMethodDescriptor(this.version, handle.getDesc()); |
|
} |
|
String handleName = handle.getName(); |
|
if (!("<init>".equals(handleName) && tag == Opcodes.H_NEWINVOKESPECIAL)) { |
|
checkMethodIdentifier(this.version, handleName, "handle name"); |
|
} |
|
} else if (value instanceof ConstantDynamic) { |
|
if ((version & 0xFFFF) < Opcodes.V11) { |
|
throw new IllegalArgumentException("ldc of a ConstantDynamic requires at least version 11"); |
|
} |
|
ConstantDynamic constantDynamic = (ConstantDynamic) value; |
|
checkMethodIdentifier(this.version, constantDynamic.getName(), "constant dynamic name"); |
|
checkDescriptor(this.version, constantDynamic.getDescriptor(), false); |
|
checkLdcConstant(constantDynamic.getBootstrapMethod()); |
|
int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount(); |
|
for (int i = 0; i < bootstrapMethodArgumentCount; ++i) { |
|
checkLdcConstant(constantDynamic.getBootstrapMethodArgument(i)); |
|
} |
|
} else { |
|
checkConstant(value); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static void checkUnqualifiedName(final int version, final String name, final String message) { |
|
checkIdentifier(version, name, 0, -1, message); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static void checkIdentifier( |
|
final int version, |
|
final String name, |
|
final int startPos, |
|
final int endPos, |
|
final String message) { |
|
if (name == null || (endPos == -1 ? name.length() <= startPos : endPos <= startPos)) { |
|
throw new IllegalArgumentException(INVALID + message + MUST_NOT_BE_NULL_OR_EMPTY); |
|
} |
|
int max = endPos == -1 ? name.length() : endPos; |
|
if ((version & 0xFFFF) >= Opcodes.V1_5) { |
|
for (int i = startPos; i < max; i = name.offsetByCodePoints(i, 1)) { |
|
if (".;[/".indexOf(name.codePointAt(i)) != -1) { |
|
throw new IllegalArgumentException( |
|
INVALID + message + " (must not contain . ; [ or /): " + name); |
|
} |
|
} |
|
return; |
|
} |
|
for (int i = startPos; i < max; i = name.offsetByCodePoints(i, 1)) { |
|
if (i == startPos |
|
? !Character.isJavaIdentifierStart(name.codePointAt(i)) |
|
: !Character.isJavaIdentifierPart(name.codePointAt(i))) { |
|
throw new IllegalArgumentException( |
|
INVALID + message + " (must be a valid Java identifier): " + name); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static void checkMethodIdentifier(final int version, final String name, final String message) { |
|
if (name == null || name.length() == 0) { |
|
throw new IllegalArgumentException(INVALID + message + MUST_NOT_BE_NULL_OR_EMPTY); |
|
} |
|
if ((version & 0xFFFF) >= Opcodes.V1_5) { |
|
for (int i = 0; i < name.length(); i = name.offsetByCodePoints(i, 1)) { |
|
if (".;[/<>".indexOf(name.codePointAt(i)) != -1) { |
|
throw new IllegalArgumentException( |
|
INVALID + message + " (must be a valid unqualified name): " + name); |
|
} |
|
} |
|
return; |
|
} |
|
for (int i = 0; i < name.length(); i = name.offsetByCodePoints(i, 1)) { |
|
if (i == 0 |
|
? !Character.isJavaIdentifierStart(name.codePointAt(i)) |
|
: !Character.isJavaIdentifierPart(name.codePointAt(i))) { |
|
throw new IllegalArgumentException( |
|
INVALID |
|
+ message |
|
+ " (must be a '<init>', '<clinit>' or a valid Java identifier): " |
|
+ name); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static void checkInternalName(final int version, final String name, final String message) { |
|
if (name == null || name.length() == 0) { |
|
throw new IllegalArgumentException(INVALID + message + MUST_NOT_BE_NULL_OR_EMPTY); |
|
} |
|
if (name.charAt(0) == '[') { |
|
checkDescriptor(version, name, false); |
|
} else { |
|
checkInternalClassName(version, name, message); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static void checkInternalClassName( |
|
final int version, final String name, final String message) { |
|
try { |
|
int startIndex = 0; |
|
int slashIndex; |
|
while ((slashIndex = name.indexOf('/', startIndex + 1)) != -1) { |
|
checkIdentifier(version, name, startIndex, slashIndex, null); |
|
startIndex = slashIndex + 1; |
|
} |
|
checkIdentifier(version, name, startIndex, name.length(), null); |
|
} catch (IllegalArgumentException e) { |
|
throw new IllegalArgumentException( |
|
INVALID + message + " (must be an internal class name): " + name, e); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static void checkDescriptor(final int version, final String descriptor, final boolean canBeVoid) { |
|
int endPos = checkDescriptor(version, descriptor, 0, canBeVoid); |
|
if (endPos != descriptor.length()) { |
|
throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static int checkDescriptor( |
|
final int version, final String descriptor, final int startPos, final boolean canBeVoid) { |
|
if (descriptor == null || startPos >= descriptor.length()) { |
|
throw new IllegalArgumentException("Invalid type descriptor (must not be null or empty)"); |
|
} |
|
switch (descriptor.charAt(startPos)) { |
|
case 'V': |
|
if (canBeVoid) { |
|
return startPos + 1; |
|
} else { |
|
throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); |
|
} |
|
case 'Z': |
|
case 'C': |
|
case 'B': |
|
case 'S': |
|
case 'I': |
|
case 'F': |
|
case 'J': |
|
case 'D': |
|
return startPos + 1; |
|
case '[': |
|
int pos = startPos + 1; |
|
while (pos < descriptor.length() && descriptor.charAt(pos) == '[') { |
|
++pos; |
|
} |
|
if (pos < descriptor.length()) { |
|
return checkDescriptor(version, descriptor, pos, false); |
|
} else { |
|
throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); |
|
} |
|
case 'L': |
|
int endPos = descriptor.indexOf(';', startPos); |
|
if (startPos == -1 || endPos - startPos < 2) { |
|
throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); |
|
} |
|
try { |
|
checkInternalClassName(version, descriptor.substring(startPos + 1, endPos), null); |
|
} catch (IllegalArgumentException e) { |
|
throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor, e); |
|
} |
|
return endPos + 1; |
|
default: |
|
throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static void checkMethodDescriptor(final int version, final String descriptor) { |
|
if (descriptor == null || descriptor.length() == 0) { |
|
throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)"); |
|
} |
|
if (descriptor.charAt(0) != '(' || descriptor.length() < 3) { |
|
throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); |
|
} |
|
int pos = 1; |
|
if (descriptor.charAt(pos) != ')') { |
|
do { |
|
if (descriptor.charAt(pos) == 'V') { |
|
throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); |
|
} |
|
pos = checkDescriptor(version, descriptor, pos, false); |
|
} while (pos < descriptor.length() && descriptor.charAt(pos) != ')'); |
|
} |
|
pos = checkDescriptor(version, descriptor, pos + 1, true); |
|
if (pos != descriptor.length()) { |
|
throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void checkLabel(final Label label, final boolean checkVisited, final String message) { |
|
if (label == null) { |
|
throw new IllegalArgumentException(INVALID + message + " (must not be null)"); |
|
} |
|
if (checkVisited && labelInsnIndices.get(label) == null) { |
|
throw new IllegalArgumentException(INVALID + message + " (must be visited first)"); |
|
} |
|
} |
|
} |