--- a/tools/java/src-tests/stx/libjava/tools/compiler/tests/CompilerAdapterTests.java Fri Aug 01 14:27:48 2014 +0100
+++ b/tools/java/src-tests/stx/libjava/tools/compiler/tests/CompilerAdapterTests.java Sun Aug 03 23:43:40 2014 +0100
@@ -2,6 +2,9 @@
import static org.junit.Assert.*;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.jdt.internal.compiler.ClassFile;
@@ -28,26 +31,6 @@
}
}
- @Test
- public void test_01() {
- ClassLoader l = new ClassLoader();
- CompilerAdapter c = new CompilerAdapter(l);
-
-
- c.compile("package test.pkg; public class Foo {}");
-
- assertFalse(c.getResult().hasErrors());
-
- ClassFile[] classfiles = c.getResult().getClassFiles();
-
- assertEquals(1, classfiles.length);
-
- Class<?> clazz = l.load(classfiles[0].getBytes());
-
- assertEquals("test.pkg.Foo" , clazz.getName());
- }
-
-
/**
* Tests compilation of an incomplete class
*/
@@ -262,7 +245,7 @@
}
/**
- * Regression tests - test compilation of class using ArrayList.
+ * Regression test - test compilation of class using ArrayList.
* There used to be an error in IBinaryMethod#getGenericSignature().
*/
@Test
@@ -275,6 +258,34 @@
assertFalse(c.getResult().hasErrors());
}
+ /**
+ * Regression test - test compilation of an annotation with retention
+ * RUNTIME. There used to be a bug causing the annotation to be
+ * runtime invisible.
+ */
+ @Test
+ public void test_09() {
+ ClassLoader l = new ClassLoader();
+ CompilerAdapter c = new CompilerAdapter(l);
+
+
+ c.compile("package test.pkg; " +
+ "@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)" +
+ "public @interface Foo {}");
+
+ assertFalse(c.getResult().hasErrors());
+
+ ClassFile[] classfiles = c.getResult().getClassFiles();
+
+ assertEquals(1, classfiles.length);
+
+ Class<?> clazz = l.load(classfiles[0].getBytes());
+ Retention retention = clazz.getAnnotation(Retention.class);
+
+ assertNotNull(retention);
+ assertEquals(RetentionPolicy.RUNTIME, retention.value());
+
+ }
}
--- a/tools/java/src/stx/libjava/tools/environment/ReflectiveAnnotation.java Fri Aug 01 14:27:48 2014 +0100
+++ b/tools/java/src/stx/libjava/tools/environment/ReflectiveAnnotation.java Sun Aug 03 23:43:40 2014 +0100
@@ -1,6 +1,9 @@
package stx.libjava.tools.environment;
import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
@@ -14,9 +17,8 @@
Annotation annotation;
// cached answers:
- char[] typeName;
- String typeNameString;
- ReflectiveElementValuePair[] reflElemValPairs;
+ char[] typeName;
+ ReflectiveElementValuePair[] elementValuePairs;
// for every element-value pair an Annotation.annotationType() has a method
// but there are also methods that don't represent an element-value
@@ -29,10 +31,16 @@
// annotations with supported tag bits:
// (annotations not mentioned here don't have any tag bits set and used)
- static final HashMap<String, Long> NAME_TO_TAG_BITS;
+ static final HashMap<Class<? extends Annotation>, Long> ANNOTATIONTYPE2TAGBITS_MAP;
static {
- NAME_TO_TAG_BITS = new HashMap<String, Long>();
- NAME_TO_TAG_BITS.put("Ljava/lang/Deprecated;", TagBits.AnnotationDeprecated);
+ ANNOTATIONTYPE2TAGBITS_MAP = new HashMap<Class<? extends Annotation>, Long>();
+
+ ANNOTATIONTYPE2TAGBITS_MAP.put(java.lang.Deprecated.class, TagBits.AnnotationDeprecated);
+ ANNOTATIONTYPE2TAGBITS_MAP.put(java.lang.Override.class, TagBits.AnnotationOverride);
+ ANNOTATIONTYPE2TAGBITS_MAP.put(java.lang.SuppressWarnings.class, TagBits.AnnotationSuppressWarnings);
+
+ ANNOTATIONTYPE2TAGBITS_MAP.put(java.lang.annotation.Documented.class, TagBits.AnnotationDocumented);
+ ANNOTATIONTYPE2TAGBITS_MAP.put(java.lang.annotation.Inherited.class, TagBits.AnnotationInherited);
}
public ReflectiveAnnotation(Annotation annot) {
@@ -42,8 +50,7 @@
public void flushCaches() {
typeName = null;
- typeNameString = null;
- reflElemValPairs = null;
+ elementValuePairs = null;
}
@Override
@@ -51,23 +58,22 @@
if (typeName != null)
return typeName;
- Class<? extends Annotation> c = annotation.annotationType();
- typeNameString = ReflectiveField.getFieldType(c);
- typeName = typeNameString.toCharArray();
+ Class<? extends Annotation> c = annotation.annotationType();
+ typeName = ReflectiveField.getFieldType(c).toCharArray();
return typeName;
}
@Override
public IBinaryElementValuePair[] getElementValuePairs() {
- if (reflElemValPairs != null)
- return reflElemValPairs;
+ if (elementValuePairs != null)
+ return elementValuePairs;
// for every element-value pair there is a method
Method[] annotMethods = annotation.annotationType().getMethods();
// some methods are supposed to be ignored though since they don't
// represent any annotation's element - these are in METHODS_TO_IGNORE
int pairsCnt = annotMethods.length - METHODS_TO_IGNORE.size();
- reflElemValPairs = new ReflectiveElementValuePair[pairsCnt];
+ elementValuePairs = new ReflectiveElementValuePair[pairsCnt];
int pairIndex = 0;
for (Method annotationMethod : annotMethods) {
@@ -83,23 +89,78 @@
throw new RuntimeException("Couldn't get value for " + name);
}
// the name of the method corresponds with the name of the element
- reflElemValPairs[pairIndex++] =
+ elementValuePairs[pairIndex++] =
new ReflectiveElementValuePair(name.toCharArray(), value);
}
- return reflElemValPairs;
+ return elementValuePairs;
}
- /*
+ /**
+ * Returns standard annotation tag bits for this annotation.
+ *
* Some annotations have a tag bits value assigned. This method returns
* the tag bits if this ReflectiveAnnotation type has a value assigned and
* is supported. Returns 0 (no bits set) otherwise.
+ *
+ * It seems that eclipse compiler relies on those bits, so we have to return
+ * them correctly.
*/
- public long standardAnnotationTagBits() {
- getTypeName();
- Long tagBits = NAME_TO_TAG_BITS.get(typeNameString);
- if (tagBits != null)
- return tagBits;
- return 0;
+ public long standardAnnotationTagBits() {
+ long tagBits = 0;
+ if (ANNOTATIONTYPE2TAGBITS_MAP.containsKey(annotation.annotationType())) {
+ tagBits |= ANNOTATIONTYPE2TAGBITS_MAP.get(annotation.annotationType());
+ }
+ // A special hack for java.lang.annotation.Retention annotation...
+ if (annotation.annotationType() == Retention.class) {
+ switch (((Retention)annotation).value()) {
+ case CLASS:
+ tagBits |= TagBits.AnnotationClassRetention;
+ break;
+ case RUNTIME:
+ tagBits |= TagBits.AnnotationRuntimeRetention;
+ break;
+ case SOURCE:
+ tagBits |= TagBits.AnnotationSourceRetention;
+ break;
+ default:
+ break;
+ }
+ }
+ // A special hack for java.lang.annotation.Target annotation...
+ if (annotation.annotationType() == Target.class) {
+ ElementType[] targets = ((Target)annotation).value();
+ for (ElementType target : targets) {
+ switch (target) {
+ case ANNOTATION_TYPE:
+ tagBits |= TagBits.AnnotationForAnnotationType;
+ break;
+ case CONSTRUCTOR:
+ tagBits |= TagBits.AnnotationForConstructor;
+ break;
+ case FIELD:
+ tagBits |= TagBits.AnnotationForField;
+ break;
+ case LOCAL_VARIABLE:
+ tagBits |= TagBits.AnnotationForLocalVariable;
+ break;
+ case METHOD:
+ tagBits |= TagBits.AnnotationForMethod;
+ break;
+ case PACKAGE:
+ tagBits |= TagBits.AnnotationForPackage;
+ break;
+ case PARAMETER:
+ tagBits |= TagBits.AnnotationForParameter;
+ break;
+ case TYPE:
+ tagBits |= TagBits.AnnotationForType;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return tagBits;
}
public boolean shouldBeIgnored() {
--- a/tools/java/src/stx/libjava/tools/environment/ReflectiveElementValuePair.java Fri Aug 01 14:27:48 2014 +0100
+++ b/tools/java/src/stx/libjava/tools/environment/ReflectiveElementValuePair.java Sun Aug 03 23:43:40 2014 +0100
@@ -1,15 +1,28 @@
package stx.libjava.tools.environment;
+import java.lang.annotation.Annotation;
+
+import org.eclipse.jdt.internal.compiler.env.ClassSignature;
+import org.eclipse.jdt.internal.compiler.env.EnumConstantSignature;
import org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair;
+import org.eclipse.jdt.internal.compiler.impl.BooleanConstant;
+import org.eclipse.jdt.internal.compiler.impl.ByteConstant;
+import org.eclipse.jdt.internal.compiler.impl.CharConstant;
+import org.eclipse.jdt.internal.compiler.impl.DoubleConstant;
+import org.eclipse.jdt.internal.compiler.impl.FloatConstant;
+import org.eclipse.jdt.internal.compiler.impl.IntConstant;
+import org.eclipse.jdt.internal.compiler.impl.LongConstant;
+import org.eclipse.jdt.internal.compiler.impl.ShortConstant;
+import org.eclipse.jdt.internal.compiler.impl.StringConstant;
public class ReflectiveElementValuePair implements IBinaryElementValuePair {
char[] name;
- Object value;
+ Object value;
public ReflectiveElementValuePair(char[] n, Object v) {
name = n;
- value = v;
+ value = v;
}
@Override
@@ -19,7 +32,31 @@
@Override
public Object getValue() {
- return value;
+ // * Return {@link IBinaryAnnotation} for annotation type.
+ if (value instanceof Annotation) return new ReflectiveAnnotation((Annotation) value);
+ // * Return {@link ClassSignature} for a Class {@link java.lang.Class}.
+ if (value instanceof Class) return new ClassSignature(((Class<?>)value).getName().toCharArray());
+
+ Class<?> type = value.getClass();
+
+ // * Return {@link org.eclipse.jdt.internal.compiler.impl.Constant} for compile-time constant of primitive type, as well as String literals.
+ if (type == Boolean.TYPE) return BooleanConstant.fromValue((Boolean) value);
+ if (type == Byte.TYPE) return ByteConstant.fromValue((Byte) value);
+ if (type == Short.TYPE) return ShortConstant.fromValue((Short) value);
+ if (type == Integer.TYPE) return IntConstant.fromValue((Integer) value);
+ if (type == Long.TYPE) return LongConstant.fromValue((Long) value);
+ if (type == Float.TYPE) return FloatConstant.fromValue((Float) value);
+ if (type == Double.TYPE) return DoubleConstant.fromValue((Double) value);
+ if (type == Character.TYPE) return CharConstant.fromValue((Character) value);
+ if (type == String.class) return StringConstant.fromValue((String) value);
+
+ // * Return {@link EnumConstantSignature} if value is an enum constant.
+ if (type.isEnum()) return new EnumConstantSignature(type.getName().toCharArray(), ((Enum<?>)value).name().toCharArray());
+
+ // * Return {@link Object}[] for array type.
+ if (type.isArray()) return value;
+
+ throw new RuntimeException("Unknown annotation element type / value");
}
}
--- a/tools/java/src/stx/libjava/tools/environment/ReflectiveEnvironment.java Fri Aug 01 14:27:48 2014 +0100
+++ b/tools/java/src/stx/libjava/tools/environment/ReflectiveEnvironment.java Sun Aug 03 23:43:40 2014 +0100
@@ -12,6 +12,8 @@
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
@@ -23,7 +25,8 @@
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
public class ReflectiveEnvironment implements INameEnvironment {
-
+
+ private static final Logger LOGGER = Logger.getLogger(ReflectiveEnvironment.class.getName());
private static final boolean DEBUG_isPackage = false;
private static Field FIELD_ClassLoader_Classes;
@@ -110,9 +113,6 @@
return ctype;
} catch (ClassNotFoundException cnfe) {
return null;
- } catch (RuntimeException e) {
- e.printStackTrace();
- return null;
}
}
@@ -204,7 +204,14 @@
if (isPackage(packageName, loader.getParent())) {
return true;
}
- // Check loaded classes...
+
+ /*
+ * Another performance optimization: consult the list of classes already loaded by
+ * given classloader. That list is not readable by standard ClassLoader API though this
+ * hack. On OpenJDK-based environments, there's a field {@link ClassLoader#classes}.
+ * However, we may or may not have an access to it. If not, just continue and do slow
+ * search through .jars and .class directories.
+ */
if (FIELD_ClassLoader_Classes != null) {
int packageNameLen = packageName.length();
Vector<Class<?>> classes;
@@ -219,6 +226,9 @@
(className.charAt(packageNameLen) == '.') &&
(className.startsWith(packageName))) {
return true;
+ } else if ((classNameLen == packageNameLen) &&
+ className.equals(packageName)) {
+ return false;
}
}
}
@@ -269,7 +279,7 @@
zip = new ZipFile(file);
if (DEBUG_isPackage) System.err.println("checking jar: " + file.getName());
/*
- * try to short-circuit: look for .class file assuming
+ * Try to short-circuit: look for .class file assuming
* packageName is actually class name. If found, return
* false (it is a class, not a package)
*/
@@ -277,9 +287,9 @@
return false;
} else {
zips[i] = zip;
- }
- } catch (ZipException e) {
- } catch (IOException e) {
+ }
+ } catch (IOException e) {
+ LOGGER.log(Level.INFO, "Failed to open .jar file: " + url.getPath() , e);
}
}
}