Merged d9a3d685c8b9 and 5260fddf10f6
authorJan Vrany <jan.vrany@fit.cvut.cz>
Sun, 03 Aug 2014 23:43:40 +0100
changeset 3192 b6bced0551a9
parent 3188 d9a3d685c8b9 (current diff)
parent 3191 5260fddf10f6 (diff)
child 3193 7abed44f04cc
child 3195 0b6a9ff08acd
child 3199 189c572dbe71
Merged d9a3d685c8b9 and 5260fddf10f6
--- 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);
     					}
     				}
     			}