external build: ensure NotNull and PatterValidator instrumenters do not break the build because of failed class resolution when they actually do not modify the bytecode (IDEA-96384 Cannot compile : Class not found: org/springframework/beans/factory/InitializingBean)

This commit is contained in:
Eugene Zhuravlev
2012-12-08 15:08:19 +01:00
parent a7a97e9ed0
commit 28de0f48aa
4 changed files with 81 additions and 30 deletions
@@ -67,7 +67,7 @@ public class InstrumentationClassFinder {
protected Class findClass(String name) throws ClassNotFoundException {
final InputStream is = lookupClassBeforeClasspath(name.replace('.', '/'));
if (is == null) {
throw new ClassNotFoundException(name);
throw new ClassNotFoundException("Class not found: " + name.replace('/', '.')); // ensure presentable class name in error message
}
try {
final byte[] bytes = loadBytes(is);
@@ -104,7 +104,7 @@ public class InstrumentationClassFinder {
final InputStream is = getClassBytesAsStream(internalName);
if (is == null) {
throw new ClassNotFoundException("Class not found: " + internalName);
throw new ClassNotFoundException("Class not found: " + name.replace('/', '.')); // ensure presentable class name in error message
}
try {
@@ -39,6 +39,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
private String myClassName;
private boolean myIsModification = false;
private RuntimeException myPostponedError;
public NotNullVerifyingInstrumenter(final ClassVisitor classVisitor) {
super(Opcodes.ASM4, classVisitor);
@@ -66,12 +67,8 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
private boolean myIsNotNull = false;
private Label myStartGeneratedCodeLabel;
public AnnotationVisitor visitParameterAnnotation(
final int parameter,
final String anno,
final boolean visible) {
AnnotationVisitor av;
av = mv.visitParameterAnnotation(parameter, anno, visible);
public AnnotationVisitor visitParameterAnnotation(final int parameter, final String anno, final boolean visible) {
final AnnotationVisitor av = mv.visitParameterAnnotation(parameter, anno, visible);
if (isReferenceType(args[parameter]) && anno.equals(NOT_NULL_TYPE)) {
myNotNullParams.add(new Integer(parameter));
}
@@ -85,8 +82,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
@Override
public AnnotationVisitor visitAnnotation(String anno, boolean isRuntime) {
final AnnotationVisitor av = mv.visitAnnotation(anno, isRuntime);
if (isReferenceType(returnType) &&
anno.equals(NOT_NULL_TYPE)) {
if (isReferenceType(returnType) && anno.equals(NOT_NULL_TYPE)) {
myIsNotNull = true;
}
@@ -144,6 +140,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
mv.visitLabel(end);
myIsModification = true;
processPostponedErrors();
}
@Override
@@ -151,8 +148,8 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
try {
super.visitMaxs(maxStack, maxLocals);
}
catch (ArrayIndexOutOfBoundsException e) {
throw new ArrayIndexOutOfBoundsException("Maximums processing failed for " + myClassName + "." + name + ": " + e.getMessage());
catch (Throwable e) {
registerError(name, "visitMaxs", e);
}
}
};
@@ -161,4 +158,25 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
private static boolean isReferenceType(final Type type) {
return type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY;
}
private void registerError(String methodName, String operationName, Throwable e) {
if (myPostponedError == null) {
// throw the first error that occurred
Throwable err = e.getCause();
if (err == null) {
err = e;
}
myPostponedError = new RuntimeException("Operation '" + operationName + "' failed for " + myClassName + "." + methodName + "(): " + err.getMessage(), err);
}
if (myIsModification) {
processPostponedErrors();
}
}
private void processPostponedErrors() {
final RuntimeException error = myPostponedError;
if (error != null) {
throw error;
}
}
}
@@ -134,30 +134,35 @@ class InstrumentationAdapter extends MethodVisitor implements Opcodes {
}
public void visitMaxs(int maxStack, int maxLocals) {
if (myAssertLabel != null) {
try {
if (myAssertLabel != null) {
// next index for synthetic variable that holds return value
final int var = maxLocals + 1;
// next index for synthetic variable that holds return value
final int var = maxLocals + 1;
mv.visitLabel(myAssertLabel);
mv.visitLabel(myAssertLabel);
mv.visitVarInsn(Opcodes.ASTORE, var);
mv.visitVarInsn(Opcodes.ASTORE, var);
final Label end = new Label();
addPatternTest(myMethodPattern.patternIndex, end, var);
final Label end = new Label();
addPatternTest(myMethodPattern.patternIndex, end, var);
addPatternAssertion(MessageFormat.format("Return value of method {0}.{1} annotated as @{2} does not match pattern {3}",
myInstrumenter.myClassName, myMethodName, myMethodPattern.annotation,
myMethodPattern.pattern), true);
addPatternAssertion(MessageFormat.format("Return value of method {0}.{1} annotated as @{2} does not match pattern {3}",
myInstrumenter.myClassName, myMethodName, myMethodPattern.annotation,
myMethodPattern.pattern), true);
mv.visitLabel(end);
mv.visitLocalVariable(RETURN_VALUE_NAME, PatternInstrumenter.JAVA_LANG_STRING, null, myAssertLabel, end, var);
mv.visitLabel(end);
mv.visitLocalVariable(RETURN_VALUE_NAME, PatternInstrumenter.JAVA_LANG_STRING, null, myAssertLabel, end, var);
mv.visitVarInsn(Opcodes.ALOAD, var);
mv.visitInsn(Opcodes.ARETURN);
mv.visitVarInsn(Opcodes.ALOAD, var);
mv.visitInsn(Opcodes.ARETURN);
}
super.visitMaxs(maxStack, maxLocals);
}
catch (Throwable e) {
myInstrumenter.registerError(myMethodName, "visitMaxs", e);
}
super.visitMaxs(maxStack, maxLocals);
}
@SuppressWarnings({"HardCodedStringLiteral"})
@@ -194,7 +199,7 @@ class InstrumentationAdapter extends MethodVisitor implements Opcodes {
addThrow("java/lang/IllegalArgumentException", "(Ljava/lang/String;)V", message);
}
}
myInstrumenter.myInstrumented = true;
myInstrumenter.markInstrumented();
}
private void addThrow(@NonNls String throwableClass, @NonNls String ctorSignature, String message) {
@@ -46,7 +46,8 @@ class PatternInstrumenter extends ClassVisitor implements Opcodes {
private final Set<String> myProcessedAnnotations = new HashSet<String>(); // checked annotation classes
String myClassName;
boolean myInstrumented;
private boolean myInstrumented;
private RuntimeException myPostponedError;
boolean myIsNonStaticInnerClass;
public PatternInstrumenter(@NotNull String patternAnnotationClassName, ClassVisitor classvisitor,
@@ -66,6 +67,11 @@ class PatternInstrumenter extends ClassVisitor implements Opcodes {
return myInstrumented;
}
void markInstrumented() {
myInstrumented = true;
processPostponedErrors();
}
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
myClassName = name;
@@ -273,4 +279,26 @@ class PatternInstrumenter extends ClassVisitor implements Opcodes {
// todo
}
}
void registerError(String methodName, String operationName, Throwable e) {
if (myPostponedError == null) {
// throw the first error that occurred
Throwable err = e.getCause();
if (err == null) {
err = e;
}
myPostponedError = new RuntimeException("Operation '" + operationName + "' failed for " + myClassName + "." + methodName + "(): " + err.getMessage(), err);
}
if (myInstrumented) {
processPostponedErrors();
}
}
private void processPostponedErrors() {
final RuntimeException error = myPostponedError;
if (error != null) {
throw error;
}
}
}