diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray.log b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray.log new file mode 100644 index 000000000000..a1f60380ec3e --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray.log @@ -0,0 +1,16 @@ +Cleaning output files: +out/production/ChangeAnnotationTypeMemberTypeArray/A.class +End of files +Compiling files: +src/A.java +End of files +Cleaning output files: +out/production/ChangeAnnotationTypeMemberTypeArray/C.class +out/production/ChangeAnnotationTypeMemberTypeArray/C1.class +out/production/ChangeAnnotationTypeMemberTypeArray/C2.class +End of files +Compiling files: +src/C.java +src/C1.java +src/C2.java +End of files \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/A.java.new b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/A.java.new new file mode 100644 index 000000000000..6986190312e6 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/A.java.new @@ -0,0 +1,3 @@ +public @interface A { +Class value() default String.class; +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/src/A.java b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/src/A.java new file mode 100644 index 000000000000..5c627b1ef58f --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/src/A.java @@ -0,0 +1,3 @@ +public @interface A { + Class[] value() default String.class; +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/src/C.java b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/src/C.java new file mode 100644 index 000000000000..fe4c89ec8c59 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/src/C.java @@ -0,0 +1,3 @@ +@A(Integer.class) +public class C { +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/src/C1.java b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/src/C1.java new file mode 100644 index 000000000000..13fe9bb428d6 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/src/C1.java @@ -0,0 +1,6 @@ +public class C1 { + + @A({Long.class, String.class}) + void foo() { + } +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/src/C2.java b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/src/C2.java new file mode 100644 index 000000000000..dffaa1703961 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeArray/src/C2.java @@ -0,0 +1,5 @@ +public class C2 { + + @A({Long.class, Double.class}) + int field; +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray.log b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray.log new file mode 100644 index 000000000000..e59a8676bdfc --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray.log @@ -0,0 +1,16 @@ +Cleaning output files: +out/production/ChangeAnnotationTypeMemberTypeEnumArray/A.class +End of files +Compiling files: +src/A.java +End of files +Cleaning output files: +out/production/ChangeAnnotationTypeMemberTypeEnumArray/C.class +out/production/ChangeAnnotationTypeMemberTypeEnumArray/C1.class +out/production/ChangeAnnotationTypeMemberTypeEnumArray/C2.class +End of files +Compiling files: +src/C.java +src/C1.java +src/C2.java +End of files \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/A.java.new b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/A.java.new new file mode 100644 index 000000000000..a580152a4ee3 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/A.java.new @@ -0,0 +1,3 @@ +public @interface A { +Val value() default Val.V1; +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/A.java b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/A.java new file mode 100644 index 000000000000..417dd43da273 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/A.java @@ -0,0 +1,3 @@ +public @interface A { + Val[] value() default {Val.V1, Val.V2, Val.V3}; +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/C.java b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/C.java new file mode 100644 index 000000000000..f56ccad75cbb --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/C.java @@ -0,0 +1,3 @@ +@A(Val.V2) +public class C { +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/C1.java b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/C1.java new file mode 100644 index 000000000000..a1bf9aa4cfbb --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/C1.java @@ -0,0 +1,6 @@ +public class C1 { + + @A({Val.V2, Val.V3}) + void foo() { + } +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/C2.java b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/C2.java new file mode 100644 index 000000000000..20aba131a94c --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/C2.java @@ -0,0 +1,5 @@ +public class C2 { + + @A({Val.V1, Val.V2}) + int field; +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/Val.java b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/Val.java new file mode 100644 index 000000000000..7e032975820c --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/annotations/changeAnnotationTypeMemberTypeEnumArray/src/Val.java @@ -0,0 +1,3 @@ +public enum Val { + V1, V2, V3; +} \ No newline at end of file diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java index 7f078e8da584..f324ed416012 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java @@ -18,6 +18,7 @@ package org.jetbrains.jps.builders.java.dependencyView; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; +import com.intellij.util.SmartList; import gnu.trove.THashMap; import gnu.trove.THashSet; import gnu.trove.TIntHashSet; @@ -27,9 +28,8 @@ import org.jetbrains.org.objectweb.asm.signature.SignatureReader; import org.jetbrains.org.objectweb.asm.signature.SignatureVisitor; import java.lang.annotation.RetentionPolicy; -import java.util.EnumSet; -import java.util.Map; -import java.util.Set; +import java.lang.reflect.Array; +import java.util.*; /** * @author: db @@ -38,6 +38,7 @@ import java.util.Set; class ClassfileAnalyzer { private final static Logger LOG = Logger.getInstance("#org.jetbrains.jps.builders.java.dependencyView.ClassfileAnalyzer"); public static final String LAMBDA_FACTORY_CLASS = "java/lang/invoke/LambdaMetafactory"; + private static final int ASM_API_VERSION = Opcodes.ASM5; private final DependencyContext myContext; @@ -48,7 +49,7 @@ class ClassfileAnalyzer { private class ClassCrawler extends ClassVisitor { private class AnnotationRetentionPolicyCrawler extends AnnotationVisitor { private AnnotationRetentionPolicyCrawler() { - super(Opcodes.ASM5); + super(ASM_API_VERSION); } @Override @@ -75,7 +76,7 @@ class ClassfileAnalyzer { private class AnnotationTargetCrawler extends AnnotationVisitor { private AnnotationTargetCrawler() { - super(Opcodes.ASM5); + super(ASM_API_VERSION); } @Override @@ -107,7 +108,7 @@ class ClassfileAnalyzer { private final TIntHashSet myUsedArguments = new TIntHashSet(); private AnnotationCrawler(final TypeRepr.ClassType type, final ElemType target) { - super(Opcodes.ASM5); + super(ASM_API_VERSION); this.myType = type; this.myTarget = target; final Set targets = myAnnotationTargets.get(type); @@ -120,52 +121,99 @@ class ClassfileAnalyzer { myUsages.add(UsageRepr.createClassUsage(myContext, type.className)); } - private String getMethodDescr(final Object value) { + private String getMethodDescr(final Object value, boolean isArray) { + final StringBuilder descriptor = new StringBuilder(); + descriptor.append("()"); + if (isArray) { + descriptor.append("["); + } if (value instanceof Type) { - return "()Ljava/lang/Class;"; + descriptor.append("Ljava/lang/Class;"); } - - final String name = Type.getType(value.getClass()).getInternalName(); - - if (name.equals("java/lang/Integer")) { - return "()I;"; + else { + final String name = Type.getType(value.getClass()).getInternalName(); + // only primitive, String, Class, Enum, another Annotation or array of any of these are allowed + if (name.equals("java/lang/Integer")) { + descriptor.append("I;"); + } + else if (name.equals("java/lang/Short")) { + descriptor.append("S;"); + } + else if (name.equals("java/lang/Long")) { + descriptor.append("J;"); + } + else if (name.equals("java/lang/Byte")) { + descriptor.append("B;"); + } + else if (name.equals("java/lang/Char")) { + descriptor.append("C;"); + } + else if (name.equals("java/lang/Boolean")) { + descriptor.append("Z;"); + } + else if (name.equals("java/lang/Float")) { + descriptor.append("F;"); + } + else if (name.equals("java/lang/Double")) { + descriptor.append("D;"); + } + else { + descriptor.append("L").append(name).append(";"); + } } - - if (name.equals("java/lang/Short")) { - return "()S;"; - } - - if (name.equals("java/lang/Long")) { - return "()J;"; - } - - if (name.equals("java/lang/Byte")) { - return "()B;"; - } - - if (name.equals("java/lang/Char")) { - return "()C;"; - } - - if (name.equals("java/lang/Boolean")) { - return "()Z;"; - } - - if (name.equals("java/lang/Float")) { - return "()F;"; - } - - if (name.equals("java/lang/Double")) { - return "()D;"; - } - - return "()L" + name + ";"; + return descriptor.toString(); } + @Nullable + private String myArrayName; + @Override public void visit(String name, Object value) { - final String methodDescr = getMethodDescr(value); - final int methodName = myContext.get(name); + final boolean isArray = name == null && myArrayName != null; + final String argName; + if (name != null) { + argName = name; + } + else { + argName = myArrayName; + // not interested in collecting complete array value; need to know just array type + myArrayName = null; + } + if (argName != null) { + registerUsages(argName, getMethodDescr(value, isArray), value); + } + } + + @Override + public void visitEnum(String name, String desc, String value) { + final boolean isArray = name == null && myArrayName != null; + final String argName; + if (name != null) { + argName = name; + } + else { + argName = myArrayName; + // not interested in collecting complete array value; need to know just array type + myArrayName = null; + } + if (argName != null) { + registerUsages(argName, (isArray? "()[" : "()") + desc, value); + } + } + + @Override + public AnnotationVisitor visitAnnotation(String name, String desc) { + return new AnnotationCrawler((TypeRepr.ClassType)TypeRepr.getType(myContext, desc), myTarget); + } + + @Override + public AnnotationVisitor visitArray(String name) { + myArrayName = name; + return this; + } + + private void registerUsages(String argName, String methodDescr, Object value) { + final int methodName = myContext.get(argName); if (value instanceof Type) { final String className = ((Type)value).getClassName().replace('.', '/'); @@ -178,28 +226,6 @@ class ClassfileAnalyzer { myUsedArguments.add(methodName); } - @Override - public void visitEnum(String name, String desc, String value) { - final int methodName = myContext.get(name); - final String methodDescr = "()" + desc; - - myUsages.add(UsageRepr.createMethodUsage(myContext, methodName, myType.className, methodDescr)); - myUsages.add(UsageRepr.createMetaMethodUsage(myContext, methodName, myType.className)); - - myUsedArguments.add(methodName); - } - - @Override - public AnnotationVisitor visitAnnotation(String name, String desc) { - return new AnnotationCrawler((TypeRepr.ClassType)TypeRepr.getType(myContext, desc), myTarget); - } - - @Override - public AnnotationVisitor visitArray(String name) { - myUsedArguments.add(myContext.get(name)); - return this; - } - @Override public void visitEnd() { final TIntHashSet s = myAnnotationArguments.get(myType); @@ -273,7 +299,7 @@ class ClassfileAnalyzer { private final Map> myAnnotationTargets = new THashMap>(); public ClassCrawler(final int fn) { - super(Opcodes.ASM5); + super(ASM_API_VERSION); myFileName = fn; } @@ -351,20 +377,35 @@ class ClassfileAnalyzer { } @Override - public void visitSource(String source, String debug) { } + public void visitSource(String source, String debug) { + } @Override - public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) { processSignature(signature); - if ((access & Opcodes.ACC_SYNTHETIC) == 0) { - myFields.add(new FieldRepr(myContext, access, myContext.get(name), myContext.get(desc), myContext.get(signature), value)); - } + return new FieldVisitor(ASM_API_VERSION) { + final List annotations = new SmartList(); - return new FieldVisitor(Opcodes.ASM5) { @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return new AnnotationCrawler((TypeRepr.ClassType)TypeRepr.getType(myContext, desc), ElemType.FIELD); + final TypeRepr.ClassType annotation = (TypeRepr.ClassType)TypeRepr.getType(myContext, desc); + annotations.add(annotation); + return new AnnotationCrawler(annotation, ElemType.FIELD); + } + + @Override + public void visitEnd() { + try { + super.visitEnd(); + } + finally { + if ((access & Opcodes.ACC_SYNTHETIC) == 0) { + myFields.add(new FieldRepr( + myContext, access, myContext.get(name), myContext.get(desc), myContext.get(signature), annotations, value + )); + } + } } }; } @@ -372,36 +413,79 @@ class ClassfileAnalyzer { @Override public MethodVisitor visitMethod(final int access, final String n, final String desc, final String signature, final String[] exceptions) { final Ref defaultValue = Ref.create(); - + final Collection annotations = new SmartList(); + final Collection paramAnnotations = new SmartList(); processSignature(signature); - return new MethodVisitor(Opcodes.ASM5) { + return new MethodVisitor(ASM_API_VERSION) { @Override public void visitEnd() { if ((access & Opcodes.ACC_SYNTHETIC) == 0 || (access & Opcodes.ACC_BRIDGE) > 0) { - myMethods.add(new MethodRepr(myContext, access, myContext.get(n), myContext.get(signature), desc, exceptions, defaultValue.get())); + myMethods.add(new MethodRepr( + myContext, access, myContext.get(n), myContext.get(signature), desc, annotations, paramAnnotations, exceptions, defaultValue.get() + )); } } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return new AnnotationCrawler( - (TypeRepr.ClassType)TypeRepr.getType(myContext, desc), "".equals(n) ? ElemType.CONSTRUCTOR : ElemType.METHOD - ); + final TypeRepr.ClassType annotType = (TypeRepr.ClassType)TypeRepr.getType(myContext, desc); + annotations.add(annotType); + return new AnnotationCrawler(annotType, "".equals(n) ? ElemType.CONSTRUCTOR : ElemType.METHOD); } @Override public AnnotationVisitor visitAnnotationDefault() { - return new AnnotationVisitor(Opcodes.ASM5) { + return new AnnotationVisitor(ASM_API_VERSION) { + + @Nullable + private List myAcc; + public void visit(String name, Object value) { - defaultValue.set(value); + collectValue(value); + } + + @Override + public void visitEnum(String name, String desc, String value) { + collectValue(value); + } + + @Override + public AnnotationVisitor visitArray(String name) { + myAcc = new SmartList(); + return this; + } + + @Override + public void visitEnd() { + if (myAcc != null) { + Object[] template = null; + if (!myAcc.isEmpty()) { + final Object elem = myAcc.get(0); + if (elem != null) { + template = (Object[])Array.newInstance(elem.getClass(), 0); + } + } + defaultValue.set(template != null? myAcc.toArray(template) : myAcc.toArray()); + } + } + + private void collectValue(Object value) { + if (myAcc != null) { + myAcc.add(value); + } + else { + defaultValue.set(value); + } } }; } @Override public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { - return new AnnotationCrawler((TypeRepr.ClassType)TypeRepr.getType(myContext, desc), ElemType.PARAMETER); + final TypeRepr.ClassType annotType = (TypeRepr.ClassType)TypeRepr.getType(myContext, desc); + paramAnnotations.add(new ParamAnnotation(parameter, annotType)); + return new AnnotationCrawler(annotType, ElemType.PARAMETER); } @Override @@ -618,7 +702,7 @@ class ClassfileAnalyzer { private class BaseSignatureVisitor extends SignatureVisitor { public BaseSignatureVisitor() { - super(Opcodes.ASM5); + super(ASM_API_VERSION); } @Override diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/FieldRepr.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/FieldRepr.java index 92359612f034..45b02a995306 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/FieldRepr.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/FieldRepr.java @@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.util.List; import java.util.Set; /** @@ -32,8 +33,14 @@ class FieldRepr extends ProtoMember { myType.updateClassUsages(context, owner, s); } - public FieldRepr(final DependencyContext context, final int a, final int n, final int d, final int s, final Object v) { - super(a, s, n, TypeRepr.getType(context, d), v); + public FieldRepr(final DependencyContext context, + final int access, + final int name, + final int descriptor, + final int signature, + @NotNull + final List annotations, final Object value) { + super(access, signature, name, TypeRepr.getType(context, descriptor), annotations, value); } public FieldRepr(final DependencyContext context, final DataInput in) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/MethodRepr.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/MethodRepr.java index 1338bca67e08..73a155ef2f0f 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/MethodRepr.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/MethodRepr.java @@ -15,6 +15,7 @@ */ package org.jetbrains.jps.builders.java.dependencyView; +import com.intellij.util.SmartList; import com.intellij.util.io.DataExternalizer; import com.intellij.util.io.DataInputOutputUtil; import gnu.trove.THashSet; @@ -26,20 +27,19 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.PrintStream; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.Set; +import java.util.*; /** * @author: db * Date: 01.02.11 */ class MethodRepr extends ProtoMember { + interface Predicate { boolean satisfy(MethodRepr m); } + public final Collection myParameterAnnotations; public final TypeRepr.AbstractType[] myArgumentTypes; public final Set myExceptions; @@ -128,9 +128,10 @@ class MethodRepr extends ProtoMember { final int name, final int signature, final String descriptor, - final String[] exceptions, + final Collection annotations, Collection parameterAnnotations, final String[] exceptions, final Object defaultValue) { - super(accessFlags, signature, name, TypeRepr.getType(context, Type.getReturnType(descriptor)), defaultValue); + super(accessFlags, signature, name, TypeRepr.getType(context, Type.getReturnType(descriptor)), annotations, defaultValue); + myParameterAnnotations = parameterAnnotations; Set typeCollection = exceptions != null ? new THashSet(exceptions.length) : Collections.emptySet(); myExceptions = (Set)TypeRepr.createClassType(context, exceptions, typeCollection); @@ -141,9 +142,23 @@ class MethodRepr extends ProtoMember { super(context, in); try { final DataExternalizer externalizer = TypeRepr.externalizer(context); + final int size = DataInputOutputUtil.readINT(in); myArgumentTypes = RW.read(externalizer, in, new TypeRepr.AbstractType[size]); + myExceptions = (Set)RW.read(externalizer, new THashSet(0), in); + + final DataExternalizer clsTypeExternalizer = TypeRepr.classTypeExternalizer(context); + myParameterAnnotations = RW.read(new DataExternalizer() { + @Override + public void save(@NotNull DataOutput out, ParamAnnotation value) throws IOException { + value.save(out); + } + @Override + public ParamAnnotation read(@NotNull DataInput in) throws IOException { + return new ParamAnnotation(clsTypeExternalizer, in); + } + }, new SmartList(), in); } catch (IOException e) { throw new BuildDataCorruptedException(e); @@ -155,6 +170,7 @@ class MethodRepr extends ProtoMember { super.save(out); RW.save(myArgumentTypes, out); RW.save(myExceptions, out); + RW.save(myParameterAnnotations, out); } public static DataExternalizer externalizer(final DependencyContext context) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ParamAnnotation.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ParamAnnotation.java new file mode 100644 index 000000000000..a8b2d3bb5319 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ParamAnnotation.java @@ -0,0 +1,83 @@ +/* + * 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.builders.java.dependencyView; + +import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.DataInputOutputUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.builders.storage.BuildDataCorruptedException; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +/** + * @author Eugene Zhuravlev + * Date: 20-Jul-16 + */ +public class ParamAnnotation implements RW.Savable{ + public static final ParamAnnotation[] EMPTY_ARRAY = new ParamAnnotation[0]; + + public final int myParamIndex; + @NotNull + public final TypeRepr.ClassType myAnnotationType; + + public ParamAnnotation(int paramIndex, @NotNull TypeRepr.ClassType annotationType) { + myParamIndex = paramIndex; + myAnnotationType = annotationType; + } + + public ParamAnnotation(DataExternalizer externalizer, DataInput in) { + try { + myParamIndex = DataInputOutputUtil.readINT(in); + myAnnotationType = externalizer.read(in); + } + catch (IOException e) { + throw new BuildDataCorruptedException(e); + } + } + + @Override + public void save(DataOutput out) { + try { + DataInputOutputUtil.writeINT(out, myParamIndex); + myAnnotationType.save(out); + } + catch (IOException e) { + throw new BuildDataCorruptedException(e); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ParamAnnotation that = (ParamAnnotation)o; + + if (myParamIndex != that.myParamIndex) return false; + if (!myAnnotationType.equals(that.myAnnotationType)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myParamIndex; + result = 31 * result + myAnnotationType.hashCode(); + return result; + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ProtoMember.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ProtoMember.java index afb5cc958a93..ff999f6c7e2f 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ProtoMember.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ProtoMember.java @@ -15,7 +15,10 @@ */ package org.jetbrains.jps.builders.java.dependencyView; +import com.intellij.util.SmartList; import com.intellij.util.io.DataInputOutputUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.storage.BuildDataCorruptedException; import org.jetbrains.org.objectweb.asm.Type; @@ -23,59 +26,187 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.PrintStream; +import java.lang.reflect.Array; +import java.util.Collection; /** * @author: db * Date: 07.03.11 */ abstract class ProtoMember extends Proto { - private final static byte STRING = 0; - private final static byte NONE = 1; - private final static byte INTEGER = 2; - private final static byte LONG = 3; - private final static byte FLOAT = 4; - private final static byte DOUBLE = 5; - private final static byte TYPE = 6; + @NotNull public final TypeRepr.AbstractType myType; + @NotNull + public final Collection myAnnotations; public final Object myValue; + private static abstract class DataDescriptor { + public static final DataDescriptor NONE = new DataDescriptor(0, null) { + @Override + public Object load(DataInput out) { + return null; + } + @Override + public void save(DataOutput out, Object value) throws IOException { + } + }; + public static final DataDescriptor STRING = new DataDescriptor(1, String.class) { + @Override + public String load(DataInput in) throws IOException { + return RW.readUTF(in); + } + @Override + public void save(DataOutput out, String value) throws IOException { + RW.writeUTF(out, value); + } + }; + public static final DataDescriptor INTEGER = new DataDescriptor(2, Integer.class) { + @Override + public Integer load(DataInput in) throws IOException { + return DataInputOutputUtil.readINT(in); + } + + @Override + public void save(DataOutput out, Integer value) throws IOException { + DataInputOutputUtil.writeINT(out, value.intValue()); + } + }; + public static final DataDescriptor LONG = new DataDescriptor(3, Long.class) { + @Override + public Long load(DataInput in) throws IOException { + return in.readLong(); + } + + @Override + public void save(DataOutput out, Long value) throws IOException { + out.writeLong(value.longValue()); + } + }; + public static final DataDescriptor FLOAT = new DataDescriptor(4, Float.class) { + @Override + public Float load(DataInput in) throws IOException { + return in.readFloat(); + } + + @Override + public void save(DataOutput out, Float value) throws IOException { + out.writeFloat(value.floatValue()); + } + }; + public static final DataDescriptor DOUBLE = new DataDescriptor(5, Double.class) { + @Override + public Double load(DataInput in) throws IOException { + return in.readDouble(); + } + + @Override + public void save(DataOutput out, Double value) throws IOException { + out.writeDouble(value.doubleValue()); + } + }; + public static final DataDescriptor TYPE = new DataDescriptor(6, Type.class) { + @Override + public Type load(DataInput in) throws IOException { + return Type.getType(RW.readUTF(in)); + } + + @Override + public void save(DataOutput out, Type value) throws IOException { + RW.writeUTF(out, value.getDescriptor()); + } + }; + + private final byte myId; + @Nullable + private final Class myDataType; + + private DataDescriptor(int id, Class dataType) { + myId = (byte)id; + myDataType = dataType; + } + + public byte getId() { + return myId; + } + + @Nullable + public Class getDataType() { + return myDataType; + } + + public abstract void save(DataOutput out, T value) throws IOException; + public abstract T load(DataInput in) throws IOException; + + @NotNull + public static DataDescriptor findById(byte tag) { + if (STRING.getId() == tag) { + return STRING; + } + if (INTEGER.getId() == tag) { + return INTEGER; + } + if (LONG.getId() == tag) { + return LONG; + } + if (FLOAT.getId() == tag) { + return FLOAT; + } + if (DOUBLE.getId() == tag) { + return DOUBLE; + } + if (TYPE.getId() == tag) { + return TYPE; + } + if (NONE.getId() == tag) { + return NONE; + } + assert false : "Unknown descriptor tag: " + tag; + return NONE; + } + + public static DataDescriptor findByValueType(@Nullable Class dataType) { + if (dataType != null) { + if (dataType.equals(STRING.getDataType())) { + return STRING; + } + if (dataType.equals(INTEGER.getDataType())) { + return INTEGER; + } + if (dataType.equals(LONG.getDataType())) { + return LONG; + } + if (dataType.equals(FLOAT.getDataType())) { + return FLOAT; + } + if (dataType.equals(DOUBLE.getDataType())) { + return DOUBLE; + } + //noinspection ConstantConditions + if (TYPE.getDataType().isAssignableFrom(dataType)) { + return TYPE; + } + } + return NONE; + } + } + public boolean hasValue() { return myValue != null; } - protected ProtoMember(final int access, final int signature, final int name, final TypeRepr.AbstractType t, final Object value) { + protected ProtoMember(final int access, + final int signature, + final int name, + @NotNull + final TypeRepr.AbstractType t, + @NotNull + Collection annotations, + final Object value) { super(access, signature, name); - this.myType = t; - this.myValue = value; - } - - private static Object loadTyped(final DataInput in) { - try { - switch (in.readByte()) { - case STRING: - return RW.readUTF(in); - case NONE: - return null; - case INTEGER: - return DataInputOutputUtil.readINT(in); - case LONG: - return in.readLong(); - case FLOAT: - return in.readFloat(); - case DOUBLE: - return in.readDouble(); - case TYPE : - return Type.getType(RW.readUTF(in)); - } - } - catch (IOException e) { - throw new BuildDataCorruptedException(e); - } - - assert (false); - - return null; + myType = t; + myValue = value; + myAnnotations = annotations; } protected ProtoMember(final DependencyContext context, final DataInput in) { @@ -83,6 +214,27 @@ abstract class ProtoMember extends Proto { try { myType = TypeRepr.externalizer(context).read(in); myValue = loadTyped(in); + myAnnotations = RW.read(TypeRepr.classTypeExternalizer(context), new SmartList(), in); + } + catch (IOException e) { + throw new BuildDataCorruptedException(e); + } + } + + private static Object loadTyped(final DataInput in) { + try { + final byte tag = in.readByte(); + if (tag < 0) { + // is array + final int length = DataInputOutputUtil.readINT(in); + final DataDescriptor descriptor = DataDescriptor.findById((byte)-tag); + final Object array = Array.newInstance(descriptor.getDataType(), length); + for (int idx = 0; idx < length; idx++) { + Array.set(array, idx, descriptor.load(in)); + } + return array; + } + return DataDescriptor.findById(tag).load(in); } catch (IOException e) { throw new BuildDataCorruptedException(e); @@ -94,38 +246,36 @@ abstract class ProtoMember extends Proto { myType.save(out); try { - if (myValue instanceof String) { - out.writeByte(STRING); - String value = (String)myValue; - RW.writeUTF(out, value); - } - else if (myValue instanceof Integer) { - out.writeByte(INTEGER); - DataInputOutputUtil.writeINT(out, ((Integer)myValue).intValue()); - } - else if (myValue instanceof Long) { - out.writeByte(LONG); - out.writeLong(((Long)myValue).longValue()); - } - else if (myValue instanceof Float) { - out.writeByte(FLOAT); - out.writeFloat(((Float)myValue).floatValue()); - } - else if (myValue instanceof Double) { - out.writeByte(DOUBLE); - out.writeDouble(((Double)myValue).doubleValue()); - } - else if (myValue instanceof Type) { - out.writeByte(TYPE); - RW.writeUTF(out, ((Type)myValue).getDescriptor()); + final Object val = myValue; + final Class valueType = val != null? val.getClass() : null; + if (valueType != null && valueType.isArray()) { + final int length = Array.getLength(val); + final Class dataType = length > 0? Array.get(val, 0).getClass() : valueType.getComponentType(); + final DataDescriptor descriptor = DataDescriptor.findByValueType(dataType); + out.writeByte(-descriptor.getId()); + if (descriptor != DataDescriptor.NONE) { + DataInputOutputUtil.writeINT(out, length); + for (int idx = 0; idx < length; idx++) { + final Object element = Array.get(val, idx); + //noinspection unchecked + descriptor.save(out, element); + } + } } else { - out.writeByte(NONE); + final DataDescriptor descriptor = DataDescriptor.findByValueType(valueType); + out.writeByte(descriptor.getId()); + //noinspection unchecked + descriptor.save(out, val); } } catch (IOException e) { throw new BuildDataCorruptedException(e); } + catch (Exception e) { + throw new RuntimeException(e); + } + RW.save(myAnnotations, out); } public Difference difference(final Proto past) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/TypeRepr.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/TypeRepr.java index 5c0204c2b2ed..1363fe74d10a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/TypeRepr.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/TypeRepr.java @@ -157,6 +157,7 @@ class TypeRepr { } public static class ClassType implements AbstractType { + public static final ClassType[] EMPTY_ARRAY = new ClassType[0]; public final int className; @Override @@ -265,6 +266,23 @@ class TypeRepr { return r; } + public static DataExternalizer classTypeExternalizer(final DependencyContext context) { + final DataExternalizer delegate = externalizer(context); + return new DataExternalizer() { + @Override + public void save(@NotNull DataOutput out, ClassType value) throws IOException { + delegate.save(out, value); + } + + @Override + public ClassType read(@NotNull DataInput in) throws IOException { + final AbstractType read = delegate.read(in); + assert read instanceof ClassType; + return (ClassType)read; + } + }; + } + public static DataExternalizer externalizer(final DependencyContext context) { return new DataExternalizer() { @Override diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildDataManager.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildDataManager.java index 7e7bf670fe9b..2404dc495b8f 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildDataManager.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildDataManager.java @@ -43,7 +43,7 @@ import java.util.concurrent.ConcurrentMap; * Date: 10/7/11 */ public class BuildDataManager implements StorageOwner { - private static final int VERSION = 27 + (PersistentHashMapValueStorage.COMPRESSION_ENABLED ? 1:0); + private static final int VERSION = 29 + (PersistentHashMapValueStorage.COMPRESSION_ENABLED ? 1:0); private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.storage.BuildDataManager"); private static final String SRC_TO_FORM_STORAGE = "src-form"; private static final String OUT_TARGET_STORAGE = "out-target"; diff --git a/jps/jps-builders/testSrc/org/jetbrains/ether/AnnotationTest.java b/jps/jps-builders/testSrc/org/jetbrains/ether/AnnotationTest.java index 42f2d61b1783..7343b863793b 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/ether/AnnotationTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/ether/AnnotationTest.java @@ -68,6 +68,14 @@ public class AnnotationTest extends IncrementalTestCase { doTest(); } + public void testChangeAnnotationTypeMemberTypeArray() throws Exception { + doTest(); + } + + public void testChangeAnnotationTypeMemberTypeEnumArray() throws Exception { + doTest(); + } + public void testClassAsArgument() throws Exception { doTest(); }