mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
generate runtime assertions for any default notnull annotation (IDEA-76782)
This commit is contained in:
+17
-11
@@ -29,7 +29,6 @@ import java.util.Map;
|
||||
*/
|
||||
public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcodes {
|
||||
private static final String NOT_NULL_CLASS_NAME = "org/jetbrains/annotations/NotNull";
|
||||
private static final String NOT_NULL_TYPE = "L"+ NOT_NULL_CLASS_NAME + ";";
|
||||
private static final String SYNTHETIC_CLASS_NAME = "java/lang/Synthetic";
|
||||
private static final String SYNTHETIC_TYPE = "L" + SYNTHETIC_CLASS_NAME + ";";
|
||||
private static final String IAE_CLASS_NAME = "java/lang/IllegalArgumentException";
|
||||
@@ -37,9 +36,9 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
|
||||
|
||||
private static final String ANNOTATION_DEFAULT_METHOD = "value";
|
||||
|
||||
private static final String NULL_ARG_MESSAGE_INDEXED = "Argument %s for @NotNull parameter of %s.%s must not be null";
|
||||
private static final String NULL_ARG_MESSAGE_NAMED = "Argument for @NotNull parameter '%s' of %s.%s must not be null";
|
||||
private static final String NULL_RESULT_MESSAGE = "@NotNull method %s.%s must not return null";
|
||||
private final String myNullArgMessageIndexed;
|
||||
private final String myNullArgMessageNamed;
|
||||
private final String myNullResultMessage;
|
||||
@SuppressWarnings("SSBasedInspection") private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
private final Map<String, Map<Integer, String>> myMethodParamNames;
|
||||
|
||||
@@ -47,15 +46,22 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
|
||||
private boolean myIsModification = false;
|
||||
private RuntimeException myPostponedError;
|
||||
private final AuxiliaryMethodGenerator myAuxGenerator;
|
||||
private final String myNotNullAnno;
|
||||
|
||||
private NotNullVerifyingInstrumenter(final ClassVisitor classVisitor, ClassReader reader) {
|
||||
private NotNullVerifyingInstrumenter(final ClassVisitor classVisitor, ClassReader reader, String notNullAnnotation) {
|
||||
super(Opcodes.API_VERSION, classVisitor);
|
||||
final String fullName = notNullAnnotation != null ? notNullAnnotation.replace('.', '/') : NOT_NULL_CLASS_NAME;
|
||||
final String shortName = fullName.substring(fullName.lastIndexOf('/') + 1);
|
||||
myNotNullAnno = "L" + fullName + ";";
|
||||
myNullArgMessageIndexed = "Argument %s for @" + shortName + " parameter of %s.%s must not be null";
|
||||
myNullArgMessageNamed = "Argument for @" + shortName + " parameter '%s' of %s.%s must not be null";
|
||||
myNullResultMessage = "@" + shortName + " method %s.%s must not return null";
|
||||
myMethodParamNames = getAllParameterNames(reader);
|
||||
myAuxGenerator = new AuxiliaryMethodGenerator(reader);
|
||||
}
|
||||
|
||||
public static boolean processClassFile(final FailSafeClassReader reader, final ClassVisitor writer) {
|
||||
final NotNullVerifyingInstrumenter instrumenter = new NotNullVerifyingInstrumenter(writer, reader);
|
||||
public static boolean processClassFile(final FailSafeClassReader reader, final ClassVisitor writer, String notNullAnnotation) {
|
||||
NotNullVerifyingInstrumenter instrumenter = new NotNullVerifyingInstrumenter(writer, reader, notNullAnnotation);
|
||||
reader.accept(instrumenter, 0);
|
||||
instrumenter.myAuxGenerator.generateReportingMethod(writer);
|
||||
return instrumenter.isModification();
|
||||
@@ -154,7 +160,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
|
||||
|
||||
public AnnotationVisitor visitParameterAnnotation(final int parameter, final String anno, final boolean visible) {
|
||||
AnnotationVisitor av = mv.visitParameterAnnotation(parameter, anno, visible);
|
||||
if (isReferenceType(args[parameter]) && anno.equals(NOT_NULL_TYPE)) {
|
||||
if (isReferenceType(args[parameter]) && anno.equals(myNotNullAnno)) {
|
||||
NotNullState state = new NotNullState(IAE_CLASS_NAME);
|
||||
myNotNullParams.put(new Integer(parameter), state);
|
||||
av = collectNotNullArgs(av, state);
|
||||
@@ -170,7 +176,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String anno, boolean isRuntime) {
|
||||
AnnotationVisitor av = mv.visitAnnotation(anno, isRuntime);
|
||||
if (isReferenceType(returnType) && anno.equals(NOT_NULL_TYPE)) {
|
||||
if (isReferenceType(returnType) && anno.equals(myNotNullAnno)) {
|
||||
myMethodNotNull = new NotNullState(ISE_CLASS_NAME);
|
||||
av = collectNotNullArgs(av, myMethodNotNull);
|
||||
}
|
||||
@@ -199,7 +205,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
|
||||
String paramName = paramNames == null ? null : paramNames.get(param);
|
||||
String descrPattern = state.message != null
|
||||
? state.message
|
||||
: paramName != null ? NULL_ARG_MESSAGE_NAMED : NULL_ARG_MESSAGE_INDEXED;
|
||||
: paramName != null ? myNullArgMessageNamed : myNullArgMessageIndexed;
|
||||
String[] args = state.message != null
|
||||
? EMPTY_STRING_ARRAY
|
||||
: new String[]{paramName != null ? paramName : String.valueOf(param - mySyntheticCount), myClassName, name};
|
||||
@@ -222,7 +228,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
|
||||
mv.visitInsn(DUP);
|
||||
final Label skipLabel = new Label();
|
||||
mv.visitJumpInsn(IFNONNULL, skipLabel);
|
||||
String descrPattern = myMethodNotNull.message != null ? myMethodNotNull.message : NULL_RESULT_MESSAGE;
|
||||
String descrPattern = myMethodNotNull.message != null ? myMethodNotNull.message : myNullResultMessage;
|
||||
String[] args = myMethodNotNull.message != null ? EMPTY_STRING_ARRAY : new String[]{myClassName, name};
|
||||
reportError(myMethodNotNull.exceptionType, skipLabel, descrPattern, args);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ public class Javac2 extends Javac {
|
||||
private ArrayList myFormFiles;
|
||||
private List myNestedFormPathList;
|
||||
private boolean instrumentNotNull = true;
|
||||
private String myNotNull;
|
||||
private List<Regexp> myClassFilterAnnotationRegexpList = new ArrayList<Regexp>(0);
|
||||
|
||||
public Javac2() {
|
||||
@@ -75,6 +76,14 @@ public class Javac2 extends Javac {
|
||||
this.instrumentNotNull = instrumentNotNull;
|
||||
}
|
||||
|
||||
public String getNotNull() {
|
||||
return myNotNull;
|
||||
}
|
||||
|
||||
public void setNotNull(String notNull) {
|
||||
myNotNull = notNull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to specify patterns of annotation class names to skip NotNull instrumentation on classes which have at least one
|
||||
* annotation matching at least one of the given patterns
|
||||
@@ -240,7 +249,6 @@ public class Javac2 extends Javac {
|
||||
final int instrumented = instrumentNotNull(getDestdir(), finder);
|
||||
log("Added @NotNull assertions to " + instrumented + " files", Project.MSG_INFO);
|
||||
}
|
||||
|
||||
}
|
||||
finally {
|
||||
finder.releaseResources();
|
||||
@@ -439,7 +447,7 @@ public class Javac2 extends Javac {
|
||||
if (version >= Opcodes.V1_5 && !shouldBeSkippedByAnnotationPattern(reader)) {
|
||||
ClassWriter writer = new InstrumenterClassWriter(reader, getAsmClassWriterFlags(version), finder);
|
||||
|
||||
if (NotNullVerifyingInstrumenter.processClassFile(reader, writer)) {
|
||||
if (NotNullVerifyingInstrumenter.processClassFile(reader, writer, myNotNull)) {
|
||||
final FileOutputStream fileOutputStream = new FileOutputStream(path);
|
||||
try {
|
||||
fileOutputStream.write(writer.toByteArray());
|
||||
|
||||
+1
-1
@@ -228,7 +228,7 @@ public class NotNullVerifyingInstrumenterTest extends UsefulTestCase {
|
||||
|
||||
FailSafeClassReader reader = new FailSafeClassReader(content, 0, content.length);
|
||||
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
|
||||
modified |= NotNullVerifyingInstrumenter.processClassFile(reader, writer);
|
||||
modified |= NotNullVerifyingInstrumenter.processClassFile(reader, writer, null);
|
||||
|
||||
byte[] instrumented = writer.toByteArray();
|
||||
final String className = FileUtil.getNameWithoutExtension(fileName);
|
||||
|
||||
+3
-1
@@ -79,7 +79,9 @@ public class NotNullInstrumentingBuilder extends BaseInstrumentingBuilder{
|
||||
ClassWriter writer,
|
||||
InstrumentationClassFinder finder) {
|
||||
try {
|
||||
if (NotNullVerifyingInstrumenter.processClassFile((FailSafeClassReader)reader, writer)) {
|
||||
final ProjectDescriptor pd = context.getProjectDescriptor();
|
||||
final String notNullAnnotation = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(pd.getProject()).getNotNullAnnotation();
|
||||
if (NotNullVerifyingInstrumenter.processClassFile((FailSafeClassReader)reader, writer, notNullAnnotation)) {
|
||||
return new BinaryContent(writer.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -31,6 +31,9 @@ public interface JpsJavaCompilerConfiguration extends JpsElement {
|
||||
boolean isAddNotNullAssertions();
|
||||
void setAddNotNullAssertions(boolean addNotNullAssertions);
|
||||
|
||||
String getNotNullAnnotation();
|
||||
void setNotNullAnnotation(String notNullAnnotation);
|
||||
|
||||
boolean isClearOutputDirectoryOnRebuild();
|
||||
void setClearOutputDirectoryOnRebuild(boolean clearOutputDirectoryOnRebuild);
|
||||
|
||||
|
||||
+11
@@ -35,6 +35,7 @@ import java.util.*;
|
||||
public class JpsJavaCompilerConfigurationImpl extends JpsCompositeElementBase<JpsJavaCompilerConfigurationImpl> implements JpsJavaCompilerConfiguration {
|
||||
public static final JpsElementChildRole<JpsJavaCompilerConfiguration> ROLE = JpsElementChildRoleBase.create("compiler configuration");
|
||||
private boolean myAddNotNullAssertions = true;
|
||||
private String myNotNullAnnotation = NotNull.class.getName();
|
||||
private boolean myClearOutputDirectoryOnRebuild = true;
|
||||
private final JpsCompilerExcludes myCompilerExcludes = new JpsCompilerExcludesImpl();
|
||||
private final List<String> myResourcePatterns = new ArrayList<String>();
|
||||
@@ -65,6 +66,11 @@ public class JpsJavaCompilerConfigurationImpl extends JpsCompositeElementBase<Jp
|
||||
return myAddNotNullAssertions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNotNullAnnotation() {
|
||||
return myNotNullAnnotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClearOutputDirectoryOnRebuild() {
|
||||
return myClearOutputDirectoryOnRebuild;
|
||||
@@ -75,6 +81,11 @@ public class JpsJavaCompilerConfigurationImpl extends JpsCompositeElementBase<Jp
|
||||
myAddNotNullAssertions = addNotNullAssertions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotNullAnnotation(String notNullAnnotation) {
|
||||
myNotNullAnnotation = notNullAnnotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClearOutputDirectoryOnRebuild(boolean clearOutputDirectoryOnRebuild) {
|
||||
myClearOutputDirectoryOnRebuild = clearOutputDirectoryOnRebuild;
|
||||
|
||||
+1
@@ -91,6 +91,7 @@ public class JpsJavaModelSerializerExtension extends JpsModelSerializerExtension
|
||||
public List<? extends JpsProjectExtensionSerializer> getProjectExtensionSerializers() {
|
||||
return Arrays.asList(new JavaProjectExtensionSerializer(),
|
||||
new JpsJavaCompilerConfigurationSerializer(),
|
||||
new JpsJavaCompilerNotNullableSerializer(),
|
||||
new JpsJavaCompilerWorkspaceConfigurationSerializer(),
|
||||
new JpsJavaCompilerOptionsSerializer("JavacSettings", "Javac"),
|
||||
new JpsEclipseCompilerOptionsSerializer("EclipseCompilerSettings", "Eclipse"),
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.jps.model.serialization.java.compiler;
|
||||
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.model.JpsProject;
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
|
||||
import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration;
|
||||
import org.jetbrains.jps.model.serialization.JpsProjectExtensionSerializer;
|
||||
|
||||
/**
|
||||
* @author vladimir.dolzhenko
|
||||
*/
|
||||
public class JpsJavaCompilerNotNullableSerializer extends JpsProjectExtensionSerializer {
|
||||
private static final String DEFAULT_VALUE = NotNull.class.getName();
|
||||
private static final String NOTNULL_ANNOTATION = "myDefaultNotNull";
|
||||
private static final String VALUE = "value";
|
||||
|
||||
public JpsJavaCompilerNotNullableSerializer() {
|
||||
super("misc.xml", "NullableNotNullManager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadExtension(@NotNull JpsProject project, @NotNull Element componentTag) {
|
||||
JpsJavaCompilerConfiguration configuration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project);
|
||||
String value = DEFAULT_VALUE;
|
||||
for (Element element : componentTag.getChildren("option")) {
|
||||
if (NOTNULL_ANNOTATION.equals(element.getAttributeValue("name"))){
|
||||
value = element.getAttributeValue(VALUE, DEFAULT_VALUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
configuration.setNotNullAnnotation(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadExtensionWithDefaultSettings(@NotNull JpsProject project) {
|
||||
JpsJavaCompilerConfiguration configuration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project);
|
||||
configuration.setNotNullAnnotation(DEFAULT_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveExtension(@NotNull JpsProject project, @NotNull Element componentTag) {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user